UIView的frame、bounds和center属性

327
我想知道如何正确地使用这些属性。
据我所知,frame 可以从我正在创建的视图的容器中使用。它将视图的位置相对于容器视图设置,并设置该视图的大小。
另外,center 也可以从我创建的视图的容器中使用。此属性更改视图相对于其容器的位置。
最后,bounds 是相对于视图本身的。它会更改视图的可绘制区域。 您能否提供有关 framebounds 之间关系的更多信息? clipsToBoundsmasksToBounds 属性呢?

1
这个讨论已经在这里解决了。请在这里查看或者在这里查看开发人员文档 http://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html https://dev59.com/g3M_5IYBdhLWcg3w1G2N http://www.slideshare.net/onoaonoa/cs193p-lecture-5-view-animation。 - Splendid
6个回答

588

因为我提出的问题已经被许多人看到,所以我将提供一个详细的答案。如果您想添加更多正确的内容,请随意修改。

首先回顾一下问题:frame、bounds和center及其关系。

Frame 一个视图的frameCGRect)是其矩形在superview坐标系中的位置。默认情况下它从左上角开始。

Bounds 视图的boundsCGRect)表示其自己坐标系统中的视图矩形。

Center 一个center是一个CGPoint,以superview的坐标系表示,并确定视图精确中心点的位置。

UIView + position中取得这些关系(它们不适用于代码,因为它们是非正式的等式):

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

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

  • frame.size = bounds.size

注意: 如果视图被旋转,这些关系不适用。有关更多信息,建议您查看以下图片,取自The Kitchen Drawer,基于斯坦福CS193p课程。鸣谢@Rhubarb

Frame, bounds and center

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

// 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。例如:

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发生。 使用下面的代码并查看发生了什么:

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),但由于view2frame origin(20.0, 20.0)开始,它们将重合。
CGRect frame = view1.bounds;
frame.origin.x += 20.0f;
frame.origin.y += 20.0f;
view1.bounds = frame; 

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

最后,boundsorigin不是相关概念。两者都可以推导出一个视图的frame(参见前面的方程式)。

View1的案例研究

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

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]的边界,会发生这种情况。

// 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)开始,它们将重合。

其他参考资料(如果需要,可以更新为其他参考资料)

关于clipsToBounds(源自苹果文档)

将此值设置为YES会导致子视图被剪切到接收器的边界。如果设置为NO,则不剪切其框架超出接收器可见边界的子视图。默认值为NO。

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

masksToBounds等同于clipsToBounds。它是应用于CALayer的属性,而不是UIView。在幕后,clipsToBounds调用masksToBounds。有关更多资料,请参见UIView的clipsToBounds和CALayer的masksToBounds之间的关系是什么?


1
@Rhubarb 你说得完全正确。我会在我的回答中指出。谢谢。 - Lorenzo B
我不知道是否会得到答案,但为什么当边界原点改变时,子视图会向负方向移动。我似乎无法理解这一点。谢谢!! - nimgrg
@nimgrg 看起来视图向负方向移动,但实际上不是这样。父视图的内部坐标已经改变了。 - Lorenzo B
@flexaddicted:你好,你添加的幻灯片上写着:“在B视图自己的坐标系中,中心点的X值是bounds.size.width/2+origin.x”-- 为什么会这样呢?既然标题说了“在它自己的坐标系中”,我认为应该是:bounds.size.width/2+bounds.origin.x,不是吗? - user2568508
@flexaddicted:嗯,现在有点困惑了,看起来幻灯片上说的是bounds.size.width/2+bounds.origin.x -- 出于某些原因,我似乎没有注意到origin.x前面的bounds,因此发表了我的评论。看起来正确的答案是:..+bounds.origin.x。 - user2568508
显示剩余3条评论

140

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

为了帮助我记住frame(框架),我想到挂在墙上的相框。就像一张图片可以移动到墙上的任何地方一样,视图的坐标系是其父视图。(墙=父视图,相框=视图)

为了帮助我记住bounds(范围),我想到篮球场的边界。篮球在球场内的某个位置,就像视图的坐标系在视图本身内部一样。(球场=视图,篮球/球员=视图内部的内容)

与frame类似,view.center也处于父视图的坐标系中。

Frame vs Bounds - 示例1

黄色矩形代表视图的frame。绿色矩形代表视图的bounds。红点在两个图像中表示frame或bounds在它们的坐标系统内的原点。

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

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

这里输入图片描述


范例二

Frame
    origin = (40, 60)  // That is, x=40 and y=60
    width = 80
    height = 130

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

在此输入图片描述


示例 3

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 相同,不同之处在于此时视图的整个内容被显示出来,就像它没有被裁剪到视图的边界一样。

Frame
    origin = (40, 60)
    width = 80
    height = 130

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

在此输入图像描述


示例5

Frame
    origin = (40, 60)
    width = 80
    height = 130

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

输入图像说明

此外,有关更详细信息,请参见这里中我提供的答案。


2
感谢Suragch。这真的是框架和边界差异非常清晰的图像。我非常感激你的努力。 - Mohd Haider
所有我想看到的东西都在一个简短而简洁的答案中。谢谢! - Matt Chuang
太棒了!例如第三个:我不明白。你只是稍微移动了框架的原点,这怎么会改变图像的旋转呢? - mfaani
@asma22,在示例3中,我只是想展示当您旋转图像时,框架会改变但边界不会。请参阅我的更完整的答案 - Suragch

89

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

enter image description here

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


16
一图胜千言。 - Narendra Kamma
4
最好的可能解释 - Ratikanta Patra
@Erben Mo:你好,你添加的那张幻灯片上写着:“在其自身坐标系中查看B的中心是:bounds.size.width/2+origin.x”。为什么会这样呢?既然标题里说了“在它自己的坐标系中”,我认为正确的方式应该是:bounds.size.width/2+bounds.origin.x。难道不是这样吗? - user2568508
1
这张图片的来源是什么?链接在哪里? - David
1
@David 这是斯坦福大学CS193p课程的内容,可以在iTunesU上找到,链接在这里:www.stanford.edu/class/cs193p/cgi-bin/drupal/downloads-2013-fall - preynolds

5

我认为如果你从CALayer的角度考虑,一切都更加清晰。

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

因此,基本上图层/视图的布局是由这三个属性(以及锚点)决定的,而这三个属性中的任何一个都不会改变其他属性,例如改变变换不会改变边界。


3

这篇文章有很好的回答,并详细解释了其中的内容。我只想提到还有另一种解释,使用可视化图形展示Frame、Bounds、Center、Transform和Bounds Origin的含义。可以在WWDC 2011视频《理解UIKit渲染》中从@4:22开始观看,直至20:10。


0
在阅读以上答案后,这里添加我的解释。
假设在网上浏览,浏览器是你的frame,它决定了网页在哪里以及以何种尺寸显示。浏览器的滚动条是你的bounds.origin,它决定了哪一部分网页将被显示。 bounds.origin很难理解。学习的最佳方法是创建Single View Application,尝试修改这些参数并观察子视图如何变化。
- (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));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接