layoutIfNeeds、layoutSubViews、setNeedsDisplay区别

12/13/2022

setNeedsLayout, layoutIfNeeds, layoutSubViews 用于处理对象布局,刷新子对象布局,类似 HTML 的排列和重排 setNeedsDisplay,setNeedsDisplayInRect,drawRect 用于处理对象的绘制,类似 HTML 的绘制和重绘 sizeThatFits,sizeToFit 用于改变大小

# setNeedsLayout

setNeedsLayout 在 receiver 上标记为需要重新布局,在系统 runloop 的下一个周期自动调用 layoutSubviews

# layoutIfNeeds

layoutIfNeed 遍历的是 subviews 链如果有需要刷新的标记,立即调用 layoutSubviews 进行布局(如果没有,不会调用 layoutSubviews)

若需要立即刷新 view 的 frame 的更改

  1. 先调用 setNeedsLayout,把标记设置为“需要重新布局”
  2. 然后马上调用 layoutIfNeed 实现布局 在初始化方法 init 或者 view 第一次显示之前,标记总是“需要重新布局”,可以直接调用 layoutIfNeed

# layoutSubViews

layoutSubviews,对 subviews 进行重新布局,调用先于 drawRect。当我们在某个类的内部调整子视图的位置时,需要调用。反过来的意思就是:如果你想要在外部设置 subviews 的位置,就不需要重写。默认没有做任何事情,需要子类进行重写。

# 哪些情况下被调用

  1. init 初始化不会触发 layoutSubviews,但是用 initWithFrame 进行初始化时,当 rect 不为 CGRectZero 时,也会触发
  2. addSubView 会触发 layoutSubviews
  3. 设置 view 的 Frame 会触发 layoutSubviews,当然前提是 frame 的值设置前后发生了变化
  4. 滚动一个 UIScrollView 会触发 layoutSubviews
  5. 旋转 Screen 会触发父 UIView 上的 layoutSubviews
  6. 改变一个 UIView 大小的时候也会触发父 UIView 上的 layoutSubviews

# setNeedsDisplay

在 receiver 上标记为需要重绘,在下一个 draw 周期自动重绘,iphone 设备的刷新频率为 60HZ,也就是 1/60 秒后重绘

# setNeedsDisplayInRect

标记为需要局部重绘

# drawRect

重写此方法,执行重绘任务。对 receiver 重绘,能获得 context

# 哪些情形下被调用

  1. 如果 UIView 初始化时没有设置 rect 大小,将直接导致 drawRect 不被自动调用。drawRect 调用是在 viewController 的 loadView 和 viewDidLoad 两个方法之后调用的,所以不用担心在控制器中这些 view 的 drawRect 就开始画了。这样可以在控制器中设置一些值给 View(如果这些 view draw 的时候需要用到某些变量值)
  2. 该方法在 sizeToFit 后被调用
  3. 通过设置 contentMode 属性为 UIViewContentModelRedraw。那么将在每次设置或者更改 frame 的时候自动调用 drawRect
  4. 直接调用 setNeedsDisplay 或者 setNeedsDisplayRect,触发 drawRect,但有个前提条件是 rect 不能为 0.

以上 1,2 推荐,3,4 不提倡

# 注意

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

# sizeThatFits

传入的参数是 receiver 当前的 size,返回一个适合的 size

# sizeToFit

sizeToFit 会使用当前 view 对 bound 调用 sizeThatFits 方法,然后改变 bound 大小。 sizeToFit 不应该在子类中被重写,应该重写 sizeThatFits。sizeToFit 可以手动被调用,都没有递归,对 subviews 也不负责,只负责自己。

Last Updated: 10/25/2024, 6:55:06 AM