UIView和它的子类都有frame和bounds属性。有什么不同?
当前回答
这里有一个很棒的视频(来自Sean Allen):
Swift - Bounds vs. Frame - iOS面试问题
框架是相对于父视图坐标的位置:
边界是相对于自身坐标系的位置。
和…如果视图是旋转的,它会变得更加棘手。(看视频!)
其他回答
这里有一个很棒的视频(来自Sean Allen):
Swift - Bounds vs. Frame - iOS面试问题
框架是相对于父视图坐标的位置:
边界是相对于自身坐标系的位置。
和…如果视图是旋转的,它会变得更加棘手。(看视频!)
以上所有答案都是正确的,以下是我的看法:
为了区分框架和边界概念,开发者应该阅读:
相对于父视图(父视图),它包含在= FRAME中 相对于它自己的坐标系,决定了它的子视图location = BOUNDS
"bounds"是令人困惑的,因为它给人的印象是坐标是它所设置的视图的位置。但这些是有关系的,可以根据坐标系常数进行调整。
UIView的边界是一个矩形,表示为相对于它自己的坐标系(0,0)的位置(x,y)和大小(宽度,高度)。
UIView的框架是一个矩形,表示为相对于它所包含的父视图的位置(x,y)和大小(宽度,高度)。
因此,想象一个视图的大小为100x100(宽x高),位于其父视图的25,25 (x,y)处。下面的代码打印出这个视图的边界和框架:
// This method is in the view controller of the superview
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"bounds.origin.x: %f", label.bounds.origin.x);
NSLog(@"bounds.origin.y: %f", label.bounds.origin.y);
NSLog(@"bounds.size.width: %f", label.bounds.size.width);
NSLog(@"bounds.size.height: %f", label.bounds.size.height);
NSLog(@"frame.origin.x: %f", label.frame.origin.x);
NSLog(@"frame.origin.y: %f", label.frame.origin.y);
NSLog(@"frame.size.width: %f", label.frame.size.width);
NSLog(@"frame.size.height: %f", label.frame.size.height);
}
这段代码的输出是:
bounds.origin.x: 0
bounds.origin.y: 0
bounds.size.width: 100
bounds.size.height: 100
frame.origin.x: 25
frame.origin.y: 25
frame.size.width: 100
frame.size.height: 100
我们可以看到,在这两种情况下,视图的宽度和高度是相同的不管我们是在看边界还是在看框架。不同之处在于视图的x,y定位。在边界的情况下,x和y坐标是0,0,因为这些坐标是相对于视图本身的。然而,坐标系x和y坐标是相对于视图在父视图中的位置(前面我们说过是在25,25)。
还有一个关于UIViews的很棒的演示。请看幻灯片1-20,它不仅解释了帧和边界之间的区别,而且还展示了可视化的例子。
简短的回答
Frame =使用父视图坐标系统的视图的位置和大小
重要的是:将视图放置在父视图中
边界=视图使用自己的坐标系统的位置和大小
重要的是:将视图的内容或子视图置于视图自身中
详细的回答
To help me remember frame, I think of a picture frame on a wall. The picture frame is like the border of a view. I can hang the picture anywhere I want on the wall. In the same way, I can put a view anywhere I want inside a parent view (also called a superview). The parent view is like the wall. The origin of the coordinate system in iOS is the top left. We can put our view at the origin of the superview by setting the view frame's x-y coordinates to (0, 0), which is like hanging our picture in the very top left corner of the wall. To move it right, increase x, to move it down increase y.
To help me remember bounds, I think of a basketball court where sometimes the basketball gets knocked out of bounds. You are dribbling the ball all over the basketball court, but you don't really care where the court itself is. It could be in a gym, or outside at a high school, or in front of your house. It doesn't matter. You just want to play basketball. In the same way, the coordinate system for a view's bounds only cares about the view itself. It doesn't know anything about where the view is located in the parent view. The bounds' origin (point (0, 0) by default) is the top left corner of the view. Any subviews that this view has are laid out in relation to this point. It is like taking the basketball to the front left corner of the court.
当你试着比较框架和边界时,困惑就来了。事实上,它并没有一开始看起来那么糟糕。让我们用一些图片来帮助我们理解。
框架与边界
在左边的第一张图片中,我们有一个位于父视图左上角的视图。黄色矩形表示视图的框架。在右边,我们再次看到了视图,但这次没有显示父视图。这是因为边界不知道父视图。绿色矩形表示视图的边界。两个图像中的红点表示帧或边界的原点。
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
你可以看到改变坐标系的x-y坐标会把它移到父视图中。但是视图本身的内容看起来仍然完全相同。边界不知道有什么不同。
到目前为止,框架和边界的宽度和高度都是完全相同的。但这并不总是正确的。看看如果我们顺时针旋转视图20度会发生什么。(旋转是使用变换完成的。更多信息请参见文档以及这些视图和层示例。)
Frame
origin = (20, 52) // These are just rough estimates.
width = 118
height = 187
Bounds
origin = (0, 0)
width = 80
height = 130
你可以看到积分限还是一样的。他们还不知道发生了什么!但是帧值都改变了。
现在更容易看出框架和边界的区别了,不是吗?你可能不了解框架和边界这篇文章将视图框架定义为
...视图相对于父视图的最小边界框 坐标系统,包括应用于该视图的任何转换。
重要的是要注意,如果转换视图,那么框架将变成未定义的。所以实际上,我在上图中围绕旋转的绿色边界所画的黄色框架实际上并不存在。这意味着如果你旋转,缩放或做一些其他变换,那么你不应该再使用帧值。不过,你仍然可以使用边界值。苹果的文件警告说:
重要提示:如果视图的transform属性不包含恒等变换,则该视图的框架是未定义的 其自动调整大小行为的结果。
相当不幸的自动调整大小....不过还是有办法的。
苹果文件中写道:
当修改视图的transform属性时,所有 对象的中心点相对地执行转换 视图。
如果你需要在转换完成后在父视图中移动视图,你可以通过改变视图来实现。中心坐标。像frame一样,center使用父视图的坐标系统。
好,我们先不做旋转,把注意力集中在积分限上。到目前为止,边界原点一直保持在(0,0),但也不是必须的。如果我们的视图有一个很大的子视图,太大而不能一次显示呢?我们会让它成为一个UIImageView带有一个大图。这是我们上面的第二张图片,但是这一次我们可以看到我们视图的子视图的整个内容是什么样子的。
Frame
origin = (40, 60)
width = 80
height = 130
Bounds
origin = (0, 0)
width = 80
height = 130
只有图像的左上角可以放入视图的边界内。现在看看如果我们改变边界的原点坐标会发生什么。
Frame
origin = (40, 60)
width = 80
height = 130
Bounds
origin = (280, 70)
width = 80
height = 130
框架在父视图中没有移动,但是框架内的内容发生了变化,因为边界矩形的原点开始于视图的不同部分。这是UIScrollView及其子类(例如,UITableView)背后的整个思想。更多解释请参见理解UIScrollView。
什么时候使用框架,什么时候使用边界
由于frame与视图在父视图中的位置相关,所以当你进行向外更改时,比如更改其宽度或查找视图与父视图顶部之间的距离时,可以使用它。
当你在进行内部更改时,比如在视图中绘制东西或排列子视图时,请使用边界。如果您对视图进行了一些转换,也可以使用边界来获取视图的大小。
进一步研究的文章:
苹果公司的文档
视图几何 的观点 视图和窗口体系结构
StackOverflow相关问题
uiviewframe, bounds和center UIView的框架,边界,中心,原点,什么时候用什么? 在iPhone中重新定位后,边框/窗口大小“不正确”
其他资源
你可能不了解框架和边界 iOS基础:框架,边界和CGGeometry CS193p第5讲-视图,绘图,动画
实践自己
除了阅读上面的文章,它对我制作测试应用程序也有很大帮助。你可能想尝试做一些类似的事情。(我是从这个视频课程中得到的灵感,但不幸的是它不是免费的。)
以下是代码供您参考:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var myView: UIView!
// Labels
@IBOutlet weak var frameX: UILabel!
@IBOutlet weak var frameY: UILabel!
@IBOutlet weak var frameWidth: UILabel!
@IBOutlet weak var frameHeight: UILabel!
@IBOutlet weak var boundsX: UILabel!
@IBOutlet weak var boundsY: UILabel!
@IBOutlet weak var boundsWidth: UILabel!
@IBOutlet weak var boundsHeight: UILabel!
@IBOutlet weak var centerX: UILabel!
@IBOutlet weak var centerY: UILabel!
@IBOutlet weak var rotation: UILabel!
// Sliders
@IBOutlet weak var frameXSlider: UISlider!
@IBOutlet weak var frameYSlider: UISlider!
@IBOutlet weak var frameWidthSlider: UISlider!
@IBOutlet weak var frameHeightSlider: UISlider!
@IBOutlet weak var boundsXSlider: UISlider!
@IBOutlet weak var boundsYSlider: UISlider!
@IBOutlet weak var boundsWidthSlider: UISlider!
@IBOutlet weak var boundsHeightSlider: UISlider!
@IBOutlet weak var centerXSlider: UISlider!
@IBOutlet weak var centerYSlider: UISlider!
@IBOutlet weak var rotationSlider: UISlider!
// Slider actions
@IBAction func frameXSliderChanged(sender: AnyObject) {
myView.frame.origin.x = CGFloat(frameXSlider.value)
updateLabels()
}
@IBAction func frameYSliderChanged(sender: AnyObject) {
myView.frame.origin.y = CGFloat(frameYSlider.value)
updateLabels()
}
@IBAction func frameWidthSliderChanged(sender: AnyObject) {
myView.frame.size.width = CGFloat(frameWidthSlider.value)
updateLabels()
}
@IBAction func frameHeightSliderChanged(sender: AnyObject) {
myView.frame.size.height = CGFloat(frameHeightSlider.value)
updateLabels()
}
@IBAction func boundsXSliderChanged(sender: AnyObject) {
myView.bounds.origin.x = CGFloat(boundsXSlider.value)
updateLabels()
}
@IBAction func boundsYSliderChanged(sender: AnyObject) {
myView.bounds.origin.y = CGFloat(boundsYSlider.value)
updateLabels()
}
@IBAction func boundsWidthSliderChanged(sender: AnyObject) {
myView.bounds.size.width = CGFloat(boundsWidthSlider.value)
updateLabels()
}
@IBAction func boundsHeightSliderChanged(sender: AnyObject) {
myView.bounds.size.height = CGFloat(boundsHeightSlider.value)
updateLabels()
}
@IBAction func centerXSliderChanged(sender: AnyObject) {
myView.center.x = CGFloat(centerXSlider.value)
updateLabels()
}
@IBAction func centerYSliderChanged(sender: AnyObject) {
myView.center.y = CGFloat(centerYSlider.value)
updateLabels()
}
@IBAction func rotationSliderChanged(sender: AnyObject) {
let rotation = CGAffineTransform(rotationAngle: CGFloat(rotationSlider.value))
myView.transform = rotation
updateLabels()
}
private func updateLabels() {
frameX.text = "frame x = \(Int(myView.frame.origin.x))"
frameY.text = "frame y = \(Int(myView.frame.origin.y))"
frameWidth.text = "frame width = \(Int(myView.frame.width))"
frameHeight.text = "frame height = \(Int(myView.frame.height))"
boundsX.text = "bounds x = \(Int(myView.bounds.origin.x))"
boundsY.text = "bounds y = \(Int(myView.bounds.origin.y))"
boundsWidth.text = "bounds width = \(Int(myView.bounds.width))"
boundsHeight.text = "bounds height = \(Int(myView.bounds.height))"
centerX.text = "center x = \(Int(myView.center.x))"
centerY.text = "center y = \(Int(myView.center.y))"
rotation.text = "rotation = \((rotationSlider.value))"
}
}
让我加上我的5美分。
视图的父视图使用Frame将其放置在父视图中。
视图本身使用Bounds来放置它自己的内容(就像滚动视图在滚动时所做的那样)。请参见clipsToBounds。边界也可以用来放大/缩小视图的内容。
类比: 框架~电视屏幕 边界~相机(缩放,移动,旋转)
推荐文章
- 如何使用接口生成器创建的nib文件加载UIView
- 模拟器慢动作动画现在打开了吗?
- 滑动删除和“更多”按钮(就像iOS 7的邮件应用程序)
- 如何比较两个nsdate:哪个是最近的?
- 删除/重置核心数据中的所有条目?
- 不区分大小写的比较
- 我如何模仿地图应用程序的底部表格?
- 编译警告:对于架构i386,没有处理文件的规则
- 如何复制文本到剪贴板/剪贴板与Swift
- 在prepareForSegue方法中阻止segue ?
- 从路径字符串中提取文件名
- CSS打印:避免在页面之间减半div ?
- 如何在不接触其他依赖项的情况下更新单个pod
- 运行时出现“未知类<MyClass> in Interface Builder file”错误
- UIView无限360度旋转动画?