可可:框架和边界之间有什么区别?

mk1*_*k12 567 cocoa cocoa-touch uiview

UIView及其子类都具有属性framebounds.有什么不同?

she*_*hek 877

边界的的UIView的矩形,表示为位置(x,y)和尺寸(宽度,高度)相对于它自己的坐标系(0,0).

的的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);
}
Run Code Online (Sandbox Code Playgroud)

这段代码的输出是:

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
Run Code Online (Sandbox Code Playgroud)

因此,我们可以看到,在这两种情况下,无论我们是在查看边界还是框架,视图的宽度和高度都是相同的.不同的是视图的x,y定位.在边界的情况下,x和y坐标为0,0,因为这些坐标相对于视图本身.但是,帧x和y坐标是相对于父视图中视图的位置(我们之前说的是25,25).

还有一个很棒的演示文稿,涵盖了UIViews.请参阅幻灯片1-20,其中不仅解释了帧和边界之间的差异,还显示了可视化示例.

  • UIScrollView是一个示例,其中边界可以移动以显示视图中的不同内容区域. (124认同)
  • 一个小技巧 - 使用NSStringFromCGRect可以节省一些时间来记录rects. (91认同)
  • 实际上,bounds.origin可以是0,0之外的东西.使用setBoundsOrigin:移动/翻译原点.有关详细信息,请参阅"查看Cocoa编程指南"中的"查看几何". (76认同)
  • 因此,对于边界,x,y不会始终为0,0,因为它本身的对象位置.或者你能给出一个它不会的场景吗? (35认同)
  • 据我所知,边界的起源总是0,0 (5认同)
  • _视图的框架将始终完全包含其边界.大多数情况下,框架大小与边界大小匹配,但并非总是如此.例如,在视图旋转期间,框架会增长,因此它可以包含边界.当边界处于旋转角度0,90,180和270时,帧大小等于边界大小.当边界处于任何其他角度时,帧大小大于边界大小. (2认同)
  • 这个答案不应该包含旋转前和旋转后的值吗?这将以某种方式解释bounds.size如何与frame.size不同(frame.size会更大) (2认同)

Sur*_*gch 615

简答

frame =使用父视图坐标系的视图位置和大小

  • 重要的是:将视图放在父级中

bounds =使用自己的坐标系统查看位置和大小

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

详细解答

为了帮助我记住框架,我想到了墙上的相框.相框就像一个视图的边框.我可以将照片挂在墙上的任何地方.以同样的方式,我可以在父视图(也称为superview)中的任何地方放置视图.父视图就像墙.iOS中坐标系的原点是左上角.我们可以通过将视图框的xy坐标设置为(0,0)来将我们的视图放在超视图的原点上,就像将我们的图片悬挂在墙的左上角一样.要向右移动,增加x,将其向下移动增加y.

为了帮助我记住边界,我想到了一个篮球场,有时候篮球会被淘汰出局.你在整个篮球场上运球,但你并不关心球场本身的位置.它可能在健身房,高中以外,或在你家门前.没关系.你只想打篮球.同样,视图边界的坐标系统只关心视图本身.它不知道视图在父视图中的位置.边界的原点(默认为点(0,0))是视图的左上角.此视图具有的任何子视图都与此相关.就像把篮球带到球场的左前角一样.

现在,当您尝试比较帧和边界时,会出现混乱.不过,它实际上并没有最初看起来那么糟糕.让我们用一些图片来帮助我们理解.

帧与界限

在左边的第一张图片中,我们有一个位于其父视图左上角的视图.黄色矩形表示视图的框架.在右侧,我们再次看到视图,但这次没有显示父视图.那是因为边界不知道父视图.绿色矩形表示视图的边界.两个图像中的红点表示帧或边界的原点.

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

Bounds 
    origin = (0, 0)
    width = 80
    height = 130
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

因此,该图片中的框架和边界完全相同.让我们看一下它们不同的例子.

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

Bounds 
    origin = (0, 0)
    width = 80
    height = 130
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

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

到目前为止,框架和边界的宽度和高度完全相同.但这并非总是如此.看看如果我们顺时针旋转视图20度会发生什么.(使用变换完成旋转.有关详细信息,请参阅文档和这些视图图层示例.)

Frame
    origin = (20, 52)  // These are just rough estimates.
    width = 118
    height = 187

Bounds 
    origin = (0, 0)
    width = 80
    height = 130
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

您可以看到边界仍然相同.他们仍然不知道发生了什么事!但是帧值已经全部改变了.

现在可以更容易地看到框架和边界之间的区别,不是吗?这篇文章你可能不理解框架和边界定义视图帧

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

请务必注意,如果转换视图,则框架将变为未定义.实际上,我在上图中旋转的绿色边界周围绘制的黄色框架实际上并不存在.这意味着如果您旋转,缩放或进行其他转换,则不应再使用帧值.但是,您仍然可以使用边界值.Apple文档警告说:

要点:如果视图的transform属性不包含标识转换,则该视图的框架未定义,其自动调整行为的结果也是如此.

相当不幸的是自动化....但是你可以做些什么.

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
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

只有图像的左上角可以放在视图的边界内.现在看看如果我们改变边界的原点坐标会发生什么.

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

Bounds 
    origin = (280, 70)
    width = 80
    height = 130
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

框架没有在超视图中移动,但框架内的内容已更改,因为边界矩形的原点从视图的不同部分开始.这是a UIScrollView和它的子类背后的整个想法(例如,a UITableView).有关更多说明,请参阅了解UIScrollView.

何时使用框架以及何时使用边界

由于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))"

    }

}
Run Code Online (Sandbox Code Playgroud)

  • 根据此处给出的示例应用程序创建了https://github.com/maniramezan/FrameVsBounds.git存储库,以帮助更好地理解视图的“边界”和“框架”。 (4认同)

小智 43

尝试运行下面的代码

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

    NSLog(@"%@", NSStringFromCGRect(v.frame));
    NSLog(@"%@", NSStringFromCGRect(v.bounds));
}
Run Code Online (Sandbox Code Playgroud)

这段代码的输出是:

案例设备方向是纵向

{{0, 0}, {768, 1024}}
{{0, 0}, {768, 1024}}
Run Code Online (Sandbox Code Playgroud)

案例设备方向是横向

{{0, 0}, {768, 1024}}
{{0, 0}, {1024, 768}}
Run Code Online (Sandbox Code Playgroud)

显然,你可以看到框架和边界之间的区别

  • 这不再是真的(iOS 8) (2认同)

jor*_*rik 13

是定义与UIView的矩形相对于它的父.

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

即,此矩形中的任何内容都将实际显示在UIView中.


m.e*_*iry 9

frame是超视图坐标系中视图的原点(左上角)和大小,这意味着你通过改变框架原点在其超级视图中翻译视图,另一方面是边界的大小和原点自己的坐标系,所以默认情况下边界原点是(0,0).

大多数情况下帧和边界是一致的,但是如果你有一个框架((140,65),(200,250))和边界((0,0),(200,250))的视图,例如,视图是倾斜的所以它站在它的右下角,然后边界仍然是((0,0),(200,250)),但框架不是.

在此输入图像描述

框架将是封装/环绕视图的最小矩形,因此框架(如照片中)将是((140,65),(320,320)).

另一个区别是,例如,如果你有一个superView的边界是((0,0),(200,200)),这个superView有一个子视图,其框架是((20,20),(100,100)),你改变了superView边界到((20,20),(200,200)),那么subView框架仍然是((20,20),(100,100)),但是由(20,20)背景,因为它的超视图坐标系被(20, 20).

我希望这有助于某人.


yoA*_*ex5 6

Frame - x,y 取决于 parenView 并计算由 Bounds 占据的完整矩形点。x:60, y:20, width: 45, height: 45 是基于内部边界的动态。

界限- x,y 是 0,0。x:0, y:0, width: 20, height: 40 是静态的

另一幅插图显示了框架和边界之间的差异。在这个例子中:

  • View B 是一个子视图 View A
  • View B 被转移到 x:60, y: 20
  • View B 被轮换 45 degrees

在此处输入图片说明


RAG*_*ATH 5

相对于其SuperView构图,而相对于其NSView则定界。

示例:X = 40,Y = 60。还包含3个视图。此图向您展示了清晰的主意。

帧

界线


小智 5

以上所有答案都是正确的,这是我对此的看法:

为了区分框架和边界概念开发人员应该阅读:

  1. 相对于父视图(一个父视图),它包含在 = FRAME 中
  2. 相对于它自己的坐标系,确定它的子视图位置 = BOUNDS

“bounds”令人困惑,因为它给人的印象是坐标是为其设置的视图的位置。但这些是有关系的,并根据框架常数进行调整。

iPhone屏幕


Ser*_*erg 5

让我加上我的 5 美分。

视图的父视图使用框架将其放置在父视图中。

视图本身使用边界来放置它自己的内容(就像滚动视图在滚动时所做的那样)。另请参阅clipsToBounds。边界也可用于放大/缩小视图的内容。

类比:
框架~电视屏幕
边界~相机(缩放、移动、旋转)


归档时间:

查看次数:

184345 次

最近记录:

6 年,1 月 前