layoutIfNeeds、layoutSubViews、setNeedsDisplay区别
setNeedsLayout, layoutIfNeeds, layoutSubViews 用于处理对象布局,刷新子对象布局,类似 HTML 的排列和重排 setNeedsDisplay,setNeedsDisplayInRect,drawRect 用于处理对象的绘制,类似 HTML 的绘制和重绘 sizeThatFits,sizeToFit 用于改变大小
# setNeedsLayout
setNeedsLayout 在 receiver 上标记为需要重新布局,在系统 runloop 的下一个周期自动调用 layoutSubviews
# layoutIfNeeds
layoutIfNeed 遍历的是 subviews 链如果有需要刷新的标记,立即调用 layoutSubviews 进行布局(如果没有,不会调用 layoutSubviews)
若需要立即刷新 view 的 frame 的更改
- 先调用 setNeedsLayout,把标记设置为“需要重新布局”
- 然后马上调用 layoutIfNeed 实现布局 在初始化方法 init 或者 view 第一次显示之前,标记总是“需要重新布局”,可以直接调用 layoutIfNeed
# layoutSubViews
layoutSubviews,对 subviews 进行重新布局,调用先于 drawRect。当我们在某个类的内部调整子视图的位置时,需要调用。反过来的意思就是:如果你想要在外部设置 subviews 的位置,就不需要重写。默认没有做任何事情,需要子类进行重写。
# 哪些情况下被调用
- init 初始化不会触发 layoutSubviews,但是用 initWithFrame 进行初始化时,当 rect 不为 CGRectZero 时,也会触发
- addSubView 会触发 layoutSubviews
- 设置 view 的 Frame 会触发 layoutSubviews,当然前提是 frame 的值设置前后发生了变化
- 滚动一个 UIScrollView 会触发 layoutSubviews
- 旋转 Screen 会触发父 UIView 上的 layoutSubviews
- 改变一个 UIView 大小的时候也会触发父 UIView 上的 layoutSubviews
# setNeedsDisplay
在 receiver 上标记为需要重绘,在下一个 draw 周期自动重绘,iphone 设备的刷新频率为 60HZ,也就是 1/60 秒后重绘
# setNeedsDisplayInRect
标记为需要局部重绘
# drawRect
重写此方法,执行重绘任务。对 receiver 重绘,能获得 context
# 哪些情形下被调用
- 如果 UIView 初始化时没有设置 rect 大小,将直接导致 drawRect 不被自动调用。drawRect 调用是在 viewController 的 loadView 和 viewDidLoad 两个方法之后调用的,所以不用担心在控制器中这些 view 的 drawRect 就开始画了。这样可以在控制器中设置一些值给 View(如果这些 view draw 的时候需要用到某些变量值)
- 该方法在 sizeToFit 后被调用
- 通过设置 contentMode 属性为 UIViewContentModelRedraw。那么将在每次设置或者更改 frame 的时候自动调用 drawRect
- 直接调用 setNeedsDisplay 或者 setNeedsDisplayRect,触发 drawRect,但有个前提条件是 rect 不能为 0.
以上 1,2 推荐,3,4 不提倡
# 注意
- 若使用 UIView 绘图,只能在 drawRect 方法中获取相应的 contextRef 并绘图。如果在其他地方获取,将获取到一个 invalidate 的 ref,并且不能用于画图。drawRect 不能手动调用,必须通过调用 setNeedsDisplay 或者 setNeedsDisplayRect,让系统自动调用该方法。
- 若使用 CALayer 绘图,只能在 drawInContext:中(类似 drawRect)绘制,或者在 delegate 中的相应方法绘制,同样也是调用 setNeedsDisplay 等间接调用以上方法。
- 若要实时画图,不能使用 gestureRecognizer,只能使用 touchBegin 等方法来调用 setNeedsDisplay 实时刷新屏幕。
# sizeThatFits
传入的参数是 receiver 当前的 size,返回一个适合的 size
# sizeToFit
sizeToFit 会使用当前 view 对 bound 调用 sizeThatFits 方法,然后改变 bound 大小。 sizeToFit 不应该在子类中被重写,应该重写 sizeThatFits。sizeToFit 可以手动被调用,都没有递归,对 subviews 也不负责,只负责自己。