iOS开发之layoutSubviews的作用和调用机制

本文详细解析了UIView中的layoutSubviews方法,包括其定义、作用及调用机制,并介绍了如何正确使用setNeedsLayout与layoutIfNeeded来更新视图布局。

一、定义

在UIView里面有一个方法layoutSubviews,这个方法定义如下
- (void)layoutSubviews; // override point. called by layoutIfNeeded automatically. As of iOS 6.0, when constraints-based layout is used the base implementation applies the constraints-based layout, otherwise it does nothing.

注:官方解释

Discussion
The default implementation of this method does nothing on iOS 5.1 and earlier. Otherwise, the default implementation uses any constraints you have set to determine the size and position of any subviews.
Subclasses can override this method as needed to perform more precise layout of their subviews. You should override this method only if the autoresizing and constraint-based behaviors of the subviews do not offer the behavior you want. You can use your implementation to set the frame rectangles of your subviews directly.
You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.

最后一段说,不要直接调用此方法。如果你想强制更新布局,你可以调用setNeedsLayout方法;如果你想立即数显你的views,你需要调用layoutIfNeed方法。

二、layoutSubviews作用

layoutSubviews是对subviews重新布局。比如,我们想更新子视图的位置的时候,可以通过调用layoutSubviews方法,即可以实现对子视图重新布局。
layoutSubviews默认是不做任何事情的,用到的时候,需要在子类进行重写。

三、layoutSubviews调用机制

  • ①、直接调用setLayoutSubviews。
  • ②、addSubview的时候触发layoutSubviews。
  • ③、当view的frame发生改变的时候触发layoutSubviews。
  • ④、第一次滑动UIScrollView的时候触发layoutSubviews。
  • ⑤、旋转Screen会触发父UIView上的layoutSubviews事件。
  • ⑥、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。

注意:
init初始化不会触发layoutSubviews,但是使用initWithFrame进行初始化时,当rect的值不为CGRectZero时,也会触发。

四、其他

  • ①、- (void)layoutSubviews;
    这个方法,默认没有做任何事情,需要子类进行重写;
  • ②、- (void)setNeedsLayout;
    标记为需要重新布局,异步调用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定会被调用;
  • ③、- (void)layoutIfNeeded;
    如果,有需要刷新的标记,立即调用layoutSubviews进行布局(如果没有标记,不会调用layoutSubviews)。

如果要立即刷新,要先调用[view setNeedsLayout],把标记设为需要布局,然后马上调用[view layoutIfNeeded],实现布局。在视图第一次显示之前,标记总是“需要刷新”的,可以直接调用[view layoutIfNeeded]

### ### 多次调用 `layoutSubviews` 的影响后果 在 iOS 开发中,`layoutSubviews` 是一个非常关键的方法,用于在视图的布局发生改变时重新计算设置子视图的位置大小。它通常在视图的生命周期中被多次调用,例如在添加子视图、修改视图的 `frame` 或者设备方向改变时。然而,频繁调用 `layoutSubviews` 可能会对性能布局逻辑产生一定的影响。 #### 1. **性能影响** `layoutSubviews` 的频繁调用可能导致布局计算的重复执行,尤其是在包含大量子视图或复杂布局逻辑的情况下。例如,在添加多个子视图时,父视图的 `layoutSubviews` 会触发一次,而不是多次,但子视图的 `layoutSubviews` 也会被调用,从而增加整体的计算负担[^1]。如果在 `layoutSubviews` 中执行复杂的布局计算或动画操作,可能会导致主线程阻塞,进而影响应用的响应速度。 #### 2. **布局逻辑的不一致性** 如果在 `layoutSubviews` 中执行了与视图状态相关的逻辑,例如根据某些条件动态调整子视图的位置或大小,那么多次调用可能导致布局状态的不一致。例如,在 `viewDidLoad` 中添加子视图并设置约束时,`layoutSubviews` 会被调用一次,此时可以安全地执行布局逻辑。但如果在其他情况下(如设备方向改变)再次调用 `layoutSubviews`,而没有正确处理状态变化,可能会导致子视图的位置或大小不符合预期[^3]。 #### 3. **设备方向改变时的额外调用** 在 iPhone 设备上,当设备方向发生改变时,`layoutSubviews` 会被调用两次,这可能是由于系统内部的布局更新机制导致的[^4]。如果在 `layoutSubviews` 中执行了某些副作用操作(如网络请求或数据更新),可能会导致不必要的资源消耗。因此,在实现 `layoutSubviews` 时,应尽量避免执行耗时操作,或者在执行前检查是否真的需要更新布局。 #### 4. **与 `setNeedsLayout` `layoutIfNeeded` 的交互** `setNeedsLayout` 会标记视图需要重新布局,而 `layoutIfNeeded` 会立即触发布局更新。如果在短时间内多次调用 `setNeedsLayout` 或 `layoutIfNeeded`,可能会导致 `layoutSubviews` 被多次调用,从而增加性能开销。为了优化性能,可以在布局逻辑中使用 `layoutIfNeeded` 来确保布局立即更新,而不是等待系统的布局周期[^2]。 #### 5. **调试测试的复杂性** 由于 `layoutSubviews` 可能在多个不同的上下文中被调用,调试测试其行为可能会变得更加复杂。例如,在调试设备方向变化时,可能会发现 `layoutSubviews` 被调用了多次,而这些调用可能来自不同的源(如父视图的 `frame` 变化或子视图的 `frame` 变化)。为了简化调试过程,可以在 `layoutSubviews` 中添加日志输出,记录调用堆栈,以便追踪调用来源。 ### ### 示例代码 以下是一个简单的示例,展示了如何在 `layoutSubviews` 中避免重复执行布局逻辑: ```objective-c - (void)layoutSubviews { [super layoutSubviews]; // 避免重复执行布局逻辑 if (CGRectEqualToRect(self.bounds, self.lastLayoutBounds)) { return; } // 执行布局逻辑 for (UIView *subview in self.subviews) { // 根据新的 bounds 调整子视图的布局 subview.frame = CGRectMake(0, 0, self.bounds.size.width / 2, self.bounds.size.height / 2); } // 记录当前的 bounds self.lastLayoutBounds = self.bounds; } ``` 在这个示例中,通过比较当前的 `bounds` 上一次布局时的 `bounds`,可以避免不必要的布局计算,从而减少性能开销。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoxiaobukuang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值