关于C#:UIView框架,边界和中心

UIView frame, bounds and center

我想知道如何以正确的方式使用这些属性。

据我所知,frame可以从我创建的视图的容器中使用。它设置相对于容器视图的视图位置。它还设置该视图的大小。

也可以从我正在创建的视图的容器中使用center。此属性更改视图相对于其容器的位置。

最后,bounds是相对于视图本身的。它改变了视图的可绘制区域。

你能提供更多关于framebounds之间关系的信息吗?那么,clipsToBoundsmasksToBounds的属性呢?


由于我问的问题已经被多次看到,我将提供一个详细的答案。如果您想添加更多正确的内容,请随意修改。

首先回顾一下这个问题:框架、边界和中心以及它们之间的关系。

框架视图的frame(CGRect是其矩形在superview坐标系中的位置。默认情况下,它从左上角开始。

边界视图的bounds(CGRect表示其坐标系中的视图矩形。

中心a center是用superview的坐标系表示的CGPoint,它决定了视图的准确中心点的位置。

从uiview+位置上看,这些是以前属性之间的关系(由于它们是非正式的方程式,因此它们在代码中不起作用):

  • frame.origin = center - (bounds.size / 2.0)

  • center = frame.origin + (bounds.size / 2.0)

  • frame.size = bounds.size

注意:如果视图旋转,这些关系不适用。如需更多信息,我建议您查看以下图片,这些图片来自斯坦福CS193P课程的厨房抽屉。学分归@rhubarb。

Frame, bounds and center

使用frame可以在其superview中重新定位和/或调整视图的大小。通常可以从superview中使用,例如,在创建特定子视图时。例如:

1
2
3
4
5
6
// view1 will be positioned at x = 30, y = 20 starting the top left corner of [self view]
// [self view] could be the view managed by a UIViewController
UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(30.0f, 20.0f, 400.0f, 400.0f)];    
view1.backgroundColor = [UIColor redColor];

[[self view] addSubview:view1];

当你需要在一个view内绘制坐标时,你通常指的是bounds。一个典型的例子可以是在view中绘制一个子视图,作为第一个子视图的插入。绘制子视图需要了解超视图的bounds。例如:

1
2
3
4
5
6
7
UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(50.0f, 50.0f, 400.0f, 400.0f)];    
view1.backgroundColor = [UIColor redColor];

UIView* view2 = [[UIView alloc] initWithFrame:CGRectInset(view1.bounds, 20.0f, 20.0f)];    
view2.backgroundColor = [UIColor yellowColor];

[view1 addSubview:view2];

当您更改视图的bounds时,会发生不同的行为。例如,如果更改boundssize,则frame会更改(反之亦然)。这种变化发生在center的观点周围。使用下面的代码,看看会发生什么:

1
2
3
4
5
6
7
8
9
10
NSLog(@"Old Frame %@", NSStringFromCGRect(view2.frame));
NSLog(@"Old Center %@", NSStringFromCGPoint(view2.center));    

CGRect frame = view2.bounds;
frame.size.height += 20.0f;
frame.size.width += 20.0f;
view2.bounds = frame;

NSLog(@"New Frame %@", NSStringFromCGRect(view2.frame));
NSLog(@"New Center %@", NSStringFromCGPoint(view2.center));

此外,如果更改boundsorigin,则更改其内部坐标系的origin。默认情况下,origin位于(0.0, 0.0)处(左上角)。例如,如果您更改view1origin,您可以看到(如果需要,请注释前面的代码),现在view2的左上角与view1接触。动机很简单。你对view1说,它的左上角现在在(20.0, 20.0)的位置,但由于view2frameorigin是从(20.0, 20.0)开始的,它们是一致的。

1
2
3
4
CGRect frame = view1.bounds;
frame.origin.x += 20.0f;
frame.origin.y += 20.0f;
view1.bounds = frame;

origin代表view在其superview内的位置,但描述了bounds中心的位置。

最后,boundsorigin不是相关概念。两者都允许导出视图的frame(见前面的方程式)。

视图1的案例研究

下面是使用以下代码片段时发生的情况。

1
2
3
4
5
6
7
8
UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(30.0f, 20.0f, 400.0f, 400.0f)];
view1.backgroundColor = [UIColor redColor];

[[self view] addSubview:view1];

NSLog(@"view1's frame is: %@", NSStringFromCGRect([view1 frame]));
NSLog(@"view1's bounds is: %@", NSStringFromCGRect([view1 bounds]));
NSLog(@"view1's center is: %@", NSStringFromCGPoint([view1 center]));

相对图像。

enter image description here

相反,如果我像下面那样改变[self view]的边界,会发生什么。

1
2
3
4
5
// previous code here...
CGRect rect = [[self view] bounds];
rect.origin.x += 30.0f;
rect.origin.y += 20.0f;
[[self view] setBounds:rect];

相对图像。

enter image description here

这里你对[self view]说,它的左上角现在处于位置(30.0,20.0),但由于view1的帧原点从(30.0,20.0)开始,它们将重合。

附加引用(如果需要,使用其他引用更新)

  • UIVIEW几何
  • ui查看框架和边界

关于clipsToBounds(来源:Apple Doc)

Setting this value to YES causes subviews to be clipped to the bounds
of the receiver. If set to NO, subviews whose frames extend beyond the
visible bounds of the receiver are not clipped. The default value is
NO.

换句话说,如果一个视图的frame(0, 0, 100, 100),它的子视图是(90, 90, 30, 30),那么您将只看到该子视图的一部分。后者不会超出父视图的边界。

masksToBounds相当于clipsToBounds。此属性应用于CALayer,而不是UIView。在引擎盖下,clipsToBounds呼叫masksToBounds。有关进一步的参考,请看一下uiview的clipstobounds和calayer的maskstobounds之间的关系如何?.


这个问题已经有了一个很好的答案,但我想补充一些图片。我的完整答案在这里。

为了帮助我记住画框,我想到了墙上的画框。就像图片可以移动到墙上的任何地方一样,视图框架的坐标系是超级视图。(墙=超视图,框架=视图)

为了帮助我记住边界,我想到了篮球场的边界。篮球在球场内的某个地方,就像视界的坐标系在视界内一样。(court=视图,basketball/players=视图中的内容)

与框架一样,view.center也位于超级视图的坐标中。

帧与边界-示例1

黄色矩形表示视图的框架。绿色矩形表示视图的边界。两幅图像中的红点代表坐标系中帧或边界的原点。

1
2
3
4
5
6
7
8
9
Frame
    origin = (0, 0)
    width = 80
    height = 130

Bounds
    origin = (0, 0)
    width = 80
    height = 130

enter image description here

例2

1
2
3
4
5
6
7
8
9
Frame
    origin = (40, 60)  // That is, x=40 and y=60
    width = 80
    height = 130

Bounds
    origin = (0, 0)
    width = 80
    height = 130

enter image description here

例3

1
2
3
4
5
6
7
8
9
Frame
    origin = (20, 52)  // These are just rough estimates.
    width = 118
    height = 187

Bounds
    origin = (0, 0)
    width = 80
    height = 130

enter image description here

例4

这与示例2相同,只是这次显示的是视图的整个内容,如果不将其剪切到视图的边界,则显示的是它的外观。

1
2
3
4
5
6
7
8
9
Frame
    origin = (40, 60)
    width = 80
    height = 130

Bounds
    origin = (0, 0)
    width = 80
    height = 130

enter image description here

例5

1
2
3
4
5
6
7
8
9
Frame
    origin = (40, 60)
    width = 80
    height = 130

Bounds
    origin = (280, 70)
    width = 80
    height = 130

enter image description here

再次,请参阅此处了解我的答案,并提供更多详细信息。


我发现这张图片对理解框架、边界等很有帮助。

enter image description here

另外,请注意,当图像旋转时,frame.size != bounds.size


我想如果你从CALayer的角度来考虑,一切都会更清楚。

框架实际上并不是视图或层的一个独特属性,它是一个虚拟属性,从边界、位置(UIView的中心)和转换计算而来。

因此,基本上,层/视图布局是如何真正由这三个属性(和AnchorPoint)决定的,并且这三个属性中的任何一个都不会更改任何其他属性,就像更改转换不会更改边界一样。


这篇文章有很好的答案和详细的解释。我只是想说,对于WWDC 2011视频理解uikit呈现中的帧、边界、中心、转换、边界原点的含义,还有另一种解释,从@4:22到20:10。


在阅读了以上答案之后,这里添加了我的解释。

假设在网上浏览,web浏览器就是你的frame,它决定了网页的显示位置和大小。浏览器的滚动条是你的bounds.origin,它决定了网页的哪一部分将被显示。bounds.origin很难理解。最好的学习方法是创建单视图应用程序,尝试修改这些参数并查看子视图如何更改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(100.0f, 200.0f, 200.0f, 400.0f)];
[view1 setBackgroundColor:[UIColor redColor]];

UIView *view2 = [[UIView alloc] initWithFrame:CGRectInset(view1.bounds, 20.0f, 20.0f)];
[view2 setBackgroundColor:[UIColor yellowColor]];
[view1 addSubview:view2];

[[self view] addSubview:view1];

NSLog(@"Old view1 frame %@, bounds %@, center %@", NSStringFromCGRect(view1.frame), NSStringFromCGRect(view1.bounds), NSStringFromCGPoint(view1.center));
NSLog(@"Old view2 frame %@, bounds %@, center %@", NSStringFromCGRect(view2.frame), NSStringFromCGRect(view2.bounds), NSStringFromCGPoint(view2.center));

// Modify this part.
CGRect bounds = view1.bounds;
bounds.origin.x += 10.0f;
bounds.origin.y += 10.0f;

// incase you need width, height
//bounds.size.height += 20.0f;
//bounds.size.width += 20.0f;

view1.bounds = bounds;

NSLog(@"New view1 frame %@, bounds %@, center %@", NSStringFromCGRect(view1.frame), NSStringFromCGRect(view1.bounds), NSStringFromCGPoint(view1.center));
NSLog(@"New view2 frame %@, bounds %@, center %@", NSStringFromCGRect(view2.frame), NSStringFromCGRect(view2.bounds), NSStringFromCGPoint(view2.center));