UIView的bounds到底是干嘛的

本文深入探讨了UIKit中UIView的frame、bounds与center属性的作用及相互关系,并通过实例演示了如何利用这些属性进行视图定位与旋转操作。

view的三个相关属性

UIView 的 frame 属性使用的很频繁,但是 bounds 这个属性却一直用的不多。最近的工作内容涉及 bounds 比较多,抽空研究了一下。先说结论,

  • frame : 当前 view 在其 superView 中的位置及大小
  • bounds : 是 view 自身的坐标系(为其 subViews 提供的坐标系)
  • center : 该view的中心点在父view坐标系统中的位置

1
2
3
4
5
6
7
8
@interface UIView(UIViewGeometry)

// animatable. do not use frame if view is transformed since it will not correctly reflect the actual location of the view. use bounds + center instead.
@property(nonatomic) CGRect            frame;

// use bounds/center and not frame if non-identity transform. if bounds dimension is odd, center may be have fractional part
@property(nonatomic) CGRect            bounds;      // default bounds is zero origin, frame size. animatable
@property(nonatomic) CGPoint           center;      // center is center of frame. animatable

bounds与坐标系

bounds 的 origin 默认为 (0, 0) 点,除非你更改了它。

可以设想,在保持一个 view 的 subViews 的 frame 不变的情况下,改变 view.bounds 将改变 subViews的位置。如果要同时移动所有 subViews 的话,改 bounds 的 origin 是一个很简单的办法。示例,

执行以下代码,

1
2
3
4
5
6
7
8
9
UIView *viewA = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];
viewA.backgroundColor = [UIColor blackColor];
[self.view addSubview:viewA];

viewA.bounds = CGRectMake(-100, -100, 200, 200);

UIView *viewB = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
viewB.backgroundColor = [UIColor grayColor];
[viewA addSubview:viewB];

结果可以看到,由于 viewA.bounds.origin 为 (-100, -100),所以 viewA 坐标系的 (0, 0) 被挪到了它的中心上去,于是 viewB 的位置也跟着挪到了这里。 

巧用center属性移动view

我们知道 center 是当前 view 的 中心点在其 superView 坐标系中的位置。我们可以推测,如果将上例中 viewB 的 center 改为 (0, 0) 点的话,superView 将正好与 viewA 居中。

1
2
3
4
viewB.center = CGPointMake(0, 0);
NSLog(@"%@", NSStringFromCGRect(viewB.frame));

//test[9849:2271050] {{-25, -25}, {50, 50}}

增加代码,运行,结果和预想的一致。而且 viewB.frame 由于受到 center 改变的影响,也发生了变化。

如果我们旋转 viewB 会怎样呢?

添加代码,执行,打印 frame 和 bounds。

1
2
3
4
5
CGAffineTransform t = viewB.transform;
viewB.transform = CGAffineTransformRotate(t, M_PI_4);

//test[12009:2783329] frame  -> {{-35.355339059327378, -35.355339059327378}, {70.710678118654755, 70.710678118654755}}
//test[12009:2783329] bounds -> {{0, 0}, {50, 50}}

旋转之后 viewB.frame变大,为其形状的外接的最小的矩形(与坐标系方向相同的矩形)。而 viewB.bounds 没有变化。

在 iOS 开发中,NSISEngine 是 Auto Layout 布局系统的核心组件之一,负责解析和执行 NSLayoutConstraint 所定义的布局约束条件。该引擎基于线性约束求解算法,将视图之间的约束关系转换为数学方程组,并通过求解这些方程来确定视图在屏幕上的最终位置和尺寸[^1]。 NSISEngine 的主要作用包括: ### 约束解析与布局计算 NSISEngine 接收开发者通过 NSLayoutConstraint 或 Interface Builder 定义的约束条件,并将这些约束转换为内部可处理的表达式。它会根据这些约束构建一个线性方程系统,并通过求解器计算出每个视图的 frame 值。这一过程确保了视图能够按照预期在不同设备和屏幕尺寸上正确显示。 ### 布局更新与冲突检测 当约束条件发生改变时,例如通过动画修改约束常量或激活/停用某些约束,NSISEngine 会重新评估布局并更新视图的几何属性。同时,它还会检测是否存在约束冲突(如两个约束对同一属性定义了不同值)或约束不足(导致布局无法确定),并在运行时抛出警告或异常以帮助开发者调试。 ### 多线程支持与线程安全 尽管 Auto Layout 的大部分操作通常在主线程上执行,但 iOS 17 中对 NSISEngine 的行为进行了调整,使其在某些异步渲染场景(如 SwiftUI 的 AsyncRenderer 线程)中也参与布局计算。这种变化可能导致在非主线程修改约束时引发线程安全问题,从而导致崩溃。因此,在 iOS 开发中应避免在后台线程直接修改约束,以防止 NSISEngine 出现状态不一致或布局引擎损坏的情况[^3]。 ### 与 SwiftUI 的交互 在 SwiftUI 框架中,NSISEngine 可能被用于某些视图的异步布局渲染过程中。由于 iOS 17 引入了对 Auto Layout 引擎线程行为的变更,某些原本在 iOS 16 及更早版本中正常运行的异步布局操作可能在 iOS 17 中触发异常。例如,在 AsyncRenderer 线程中使用 NSISEngine 更新布局可能导致线程冲突,进而引发崩溃。为避免此类问题,开发者应确保所有对约束的修改都在主线程上执行。 ### 示例:在 UIKit 中使用 NSISEngine 的典型约束定义 ```swift let view1 = UIView() let view2 = UIView() view1.translatesAutoresizingMaskIntoConstraints = false view2.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ view1.leadingAnchor.constraint(equalTo: view2.leadingAnchor), view1.topAnchor.constraint(equalTo: view2.topAnchor), view1.widthAnchor.constraint(equalToConstant: 100), view1.heightAnchor.constraint(equalToConstant: 100) ]) ``` 在上述代码中,NSISEngine 会解析并执行这些 NSLayoutConstraint,并在布局更新时计算出 view1 的正确 frame 值。 ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值