iOS开发笔记--什么时候调用layoutSubviews

今天在写程序时候遇见layoutSubviews触发时候引起的问题。特来总结一下什么时候会触发layoutSubviews:
layoutSubviews在以下情况下会被调用: 
1、init初始化不会触发layoutSubviews 
2、addSubview会触发layoutSubviews 
3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化 
4、滚动一个UIScrollView会触发layoutSubviews 
5、旋转Screen会触发父UIView上的layoutSubviews事件 

6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件




 

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



一、重绘机制

iOS的绘图操作是在UIView类的drawRect方法中完成的,所以如果我们要想在一个UIView中绘图,需要写一个扩展UIView 的类,并重写drawRect方法,在这里进行绘图操作,程序会自动调用此方法进行绘图。 
重绘操作仍然在drawRect方法中完成,但是苹果不建议直接调用drawRect方法,当然如果你强直直接调用此方法,当然是没有效果的。苹果要求我们调用UIView类中的setNeedsDisplay方法,则程序会自动调用drawRect方法进行重绘。(调用setNeedsDisplay会自动调用drawRect)。 
在UIView中,重写drawRect: (CGRect) aRect方法,可以自己定义想要画的图案.且此方法一般情况下只会画一次.也就是说这个drawRect方法一般情况下只会被掉用一次. 当某些情况下想要手动重画这个View,只需要掉用[self setNeedsDisplay]方法即可.

二、方法定义

  • ①、- (void)drawRect:(CGRect)rect; 
    重写此方法,执行重绘任务
  • ②、- (void)setNeedsDisplay; 
    标记为需要重绘,异步调用drawRect
  • ③、- (void)setNeedsDisplayInRect:(CGRect)rect; 
    标记为需要局部重绘

三、drawRect调用机制

drawRect调用是在Controller->loadView,,Controller->viewDidLoad 两方法之后调用的。所以不用担心在控制器中,这些View的drawRect就开始画了。这样可以在控制器中设置一些值给View(如果这些View draw的时候需要用到某些变量值).

  • 1、如果在UIView初始化时没有设置rect大小,将直接导致drawRect不被自动调用。
  • 2、该方法在调用sizeThatFits后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。
  • 3、通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
  • 4、直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect不能为0. 
    以上1,2推荐;而3,4不提倡

四、绘图

  • 1、若使用UIView绘图,只能在drawRect:方法中获取相应的contextRef并绘图。如果在其他方法中获取将获取到一个invalidate的ref并且不能用于画图。drawRect:方法不能手动显示调用,必须通过调用setNeedsDisplay 或者 setNeedsDisplayInRect ,让系统自动调该方法。
  • 2、若使用calayer绘图,只能在drawInContext: 中(类似于drawRect)绘制,或者在delegate中的相应方法绘制。同样也是调用setNeedDisplay等间接调用以上方法。
  • 3、若要实时画图,不能使用gestureRecognizer,只能使用touchbegan等方法来掉用setNeedsDisplay实时刷新屏幕

五、比较

layoutSubviews对subviews重新布局; 
layoutSubviews方法调用先于drawRect; 
setNeedsLayout在receiver标上一个需要被重新布局的标记,在系统runloop的下一个周期自动调用layoutSubviews; 
layoutIfNeeded方法如其名,UIKit会判断该receiver是否需要layout.根据Apple官方文档,layoutIfNeeded方法应该是这样的 
layoutIfNeeded遍历的不是superview链,应该是subviews链 
drawRect是对receiver的重绘,能获得context; 
setNeedDisplay在receiver标上一个需要被重新绘图的标记,在下一个draw周期自动重绘,iphone device的刷新频率是60hz,也就是1/60秒后重绘。


### ### 多次调用 `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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值