可可:框架和界限有什么区别?
UIView
及其子类都具有属性frame
和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 =使用父视图坐标系的视图的位置和大小
边界 =使用自己的坐标系统的视图的位置和大小
详细的答案
为了帮助我记住框架 ,我想到了墙上的一个相框 。 相框就像一个视图的边框。 我可以把照片挂在墙上的任何地方。 以同样的方式,我可以在父视图(也称为超级视图)内的任何位置放置视图。 父视图就像墙。 iOS中坐标系的原点是左上角。 通过将视图框架的xy坐标设置为(0,0),我们可以将视图放在超视图的起源处,这就像将我们的图片悬挂在墙的左上角一样。 要正确移动它,请增加x,将其向下移动y。
为了帮助我记住界限 ,我想到了一个篮球场 ,有时篮球会被打破界限 。 你在篮球场上运球,但你并不在乎球场的本身。 它可能在健身房,或在高中以外,或在你家门前。 没关系。 你只是想打篮球。 同样,视图边界的坐标系统只关心视图本身。 它不知道视图在父视图中的位置。 边界的原点(默认点(0,0))是视图的左上角。 这个观点所具有的任何子视图都是与这一点相关的。 这就像把篮球带到球场的左前角。
现在,当您尝试比较帧和边界时会出现混淆。 尽管如此,它实际上并没有起初那么糟糕。 让我们用一些图片来帮助我们理解。
框架与边界
在左边的第一张图片中,我们有一个位于其父视图左上方的视图。 黄色矩形表示视图的框架。 在右侧,我们再次看到视图,但是这次不显示父视图。 这是因为边界不知道父视图。 绿色矩形表示视图的边界。 两幅图像中的红点代表帧或边界的起源 。
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
所以你可以看到改变框架的xy坐标在父视图中移动它。 但视图本身的内容仍然看起来完全一样。 界限不知道有什么不同。
到目前为止,框架和边界的宽度和高度都完全相同。 但是,这并非总是如此。 看看我们将视图顺时针旋转20度会发生什么。 (使用转换完成旋转。请参阅文档以及这些视图和图层示例以获取更多信息。)
Frame
origin = (20, 52) // These are just rough estimates.
width = 118
height = 187
Bounds
origin = (0, 0)
width = 80
height = 130
你可以看到边界仍然是一样的。 他们仍然不知道发生了什么! 虽然帧值已经全部改变了。
现在看到框架和边界之间的区别会更容易一些,不是吗? 您可能不理解框架和边界的文章将视图框架定义为
...该视图相对于其父系坐标系的最小边界框,包括应用于该视图的任何转换。
需要注意的是,如果您转换视图,那么该框架变得未定义。 所以实际上,我在上面的图像中围绕旋转的绿色边界绘制的黄色框架从未实际存在。 这意味着如果您旋转,缩放或做其他转换,则不应再使用帧值。 不过,您仍然可以使用边界值。 苹果文档警告:
重要提示:如果视图的transform
属性不包含标识转换,则该视图的框架未定义,其自动调整行为的结果也是如此。
相当不幸的autoresizing ....然而,你可以做的事情。
Apple文档声明:
修改视图的transform
属性时,所有变换均相对于视图的中心点执行。
因此,如果您在转换完成后需要在父项中移动视图,则可以通过更改view.center
坐标来完成。 像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
将视图的位置与其父视图相关联,所以当您进行外部更改时 (如更改其宽度或查找视图与其父视图顶部之间的距离),可以使用frame
。
在进行内部更改时使用bounds
,例如在视图内绘制东西或安排子视图。 如果你已经做了一些转换,也可以使用边界来获得视图的大小。
进一步研究的文章:
Apple文档
相关的StackOverflow问题
其他资源
练习自己
除了阅读上述文章外,它还可以帮助我制作测试应用程序。 你可能想尝试做类似的事情。 (我从这个视频课程中获得了这个想法,但不幸的是它不是免费的。)
以下是供您参考的代码:
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))"
}
}
尝试运行下面的代码
- (void)viewDidLoad {
[super viewDidLoad];
UIWindow *w = [[UIApplication sharedApplication] keyWindow];
UIView *v = [w.subviews objectAtIndex:0];
NSLog(@"%@", NSStringFromCGRect(v.frame));
NSLog(@"%@", NSStringFromCGRect(v.bounds));
}
这段代码的输出是:
机箱设备方向是纵向
{{0, 0}, {768, 1024}}
{{0, 0}, {768, 1024}}
案例设备方向是风景
{{0, 0}, {768, 1024}}
{{0, 0}, {1024, 768}}
显然,你可以看到框架和边界之间的区别
链接地址: http://www.djcxy.com/p/85163.html上一篇: Cocoa: What's the difference between the frame and the bounds?