Cocoa 可可:什么';帧和边界之间的区别是什么?

Cocoa 可可:什么';帧和边界之间的区别是什么?,cocoa,cocoa-touch,uiview,Cocoa,Cocoa Touch,Uiview,UIView及其子类都具有属性frame和bounds。区别是什么?一个对象的边界是,表示为相对于其自身坐标系(0,0)的位置(x,y)和大小(宽度,高度) 一个视图的帧是,表示为相对于它所包含的超视图的位置(x,y)和大小(宽度,高度) 因此,想象一个大小为100x100(宽x高)的视图位于其superview的25,25(x,y)处。以下代码打印出此视图的边界和框架: // This method is in the view controller of the superview - (v

UIView
及其子类都具有属性
frame
bounds
。区别是什么?

一个对象的边界是,表示为相对于其自身坐标系(0,0)的位置(x,y)和大小(宽度,高度)


一个视图的帧是,表示为相对于它所包含的超视图的位置(x,y)和大小(宽度,高度)

因此,想象一个大小为100x100(宽x高)的视图位于其superview的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)


还有一个包含UIView的。请参阅幻灯片1-20,其中不仅解释了框架和边界之间的差异,还显示了可视示例。框架是相对于其superview定义UIView的矩形

边界rect是定义NSView坐标系的值范围


i、 e.此矩形中的任何内容都将实际显示在UIView中

尝试运行下面的代码

- (void)viewDidLoad {
    [super viewDidLoad];
    UIWindow *w = [[UIApplication sharedApplication] keyWindow];
    UIView *v = [w.subviews objectAtIndex:0];

    NSLog(@"%@", NSStringFromCGRect(v.frame));
    NSLog(@"%@", NSStringFromCGRect(v.bounds));
}
此代码的输出为:

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
机箱设备方向为纵向

{{0, 0}, {768, 1024}}
{{0, 0}, {768, 1024}}
机箱设备方向为横向

{{0, 0}, {768, 1024}}
{{0, 0}, {1024, 768}}
显然,您可以看到框架和边界之间的差异

简短回答 框架=使用父视图坐标系的视图位置和大小

  • 重要提示:将视图放置在父视图中
边界=使用视图自身坐标系的视图位置和大小

  • 重要提示:将视图的内容或子视图放置在其自身中

详细答案 为了帮助我记住相框,我想到了墙上的相框。相框就像是视图的边框。我可以把这幅画挂在墙上任何我想挂的地方。同样,我可以将视图放在父视图(也称为超级视图)中我想要的任何位置。父视图类似于墙。iOS中坐标系的原点位于左上角。通过将图幅的x-y坐标设置为(0,0),我们可以将视图放置在superview的原点,这就像将图片挂在墙的左上角一样。若要向右移动,请增加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

因此,您可以看到,更改帧的x-y坐标会在父视图中移动它。但视图本身的内容看起来仍然完全相同。边界不知道有什么不同

到目前为止,框架和边界的宽度和高度完全相同。但这并不总是正确的。看看如果我们顺时针旋转视图20度会发生什么。(旋转是使用变换完成的。有关详细信息,请参见和这些以及。)

您可以看到边界仍然是相同的。他们仍然不知道发生了什么事!不过,帧值都已更改

现在更容易看出框架和边界之间的区别了,不是吗?本文将视图框架定义为

…该视图相对于其父视图的最小边界框 坐标系,包括应用于该视图的任何变换

请务必注意,如果变换视图,则框架将变得未定义。所以实际上,我在上面的图像中围绕旋转的绿色边界绘制的黄色框架实际上并不存在。这意味着如果旋转、缩放或进行其他变换,则不应再使用帧值。不过,您仍然可以使用边界值。苹果公司警告说:

重要提示:如果视图的
transform
属性不包含标识转换,则该属性的框架
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
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))"

    }

}
scrollView.contentSize = CGSize(x:320*3, y : 200)
let imageView0 = UIImageView.init(frame: CGRect(x:0, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
let imageView1 :  UIImageView.init( frame: CGRect(x:320, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
let imageView2 :  UIImageView.init(frame: CGRect(x:640, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
scrollView.addSubview(imageView0)
scrollView.addSubview(imageView0)
scrollView.addSubview(imageView0)