setNeedsDisplay 、layoutSubviews、setNeedsLayout、layoutIfNeeded

这几个都是UIView的方法,很容易混淆,直接面对官方文档和demo搞搞清楚

1、setNeedsDisplay
官方文档解释

You can use this method or the `setNeedsDisplayInRect:` to notify the
 system that your view’s contents need to be redrawn. 
This method makes a note of the request and returns immediately. 
The view is not actually redrawn until the next drawing cycle, 
at which point all invalidated views are updated.

Note
If your view is backed by a `CAEAGLLayer`object, this method has no effect. 
It is intended for use only with views that use native drawing technologies 
(such as UIKit and Core Graphics) to render their content.

You should use this method to request that a view be redrawn only when the 
content or appearance of the view change. If you simply change the 
geometry of the view, the view is typically not redrawn. Instead, its existing 
content is adjusted based on the value in the view’s contentMode property. 
Redisplaying the existing content improves performance by avoiding the 
need to redraw content that has not changed.

翻译下:
您可以使用此方法或setNeedsDisplayInRect:来通知系统您的视图内容需要重绘。此方法记录请求并立即
返回。在下一个绘制周期之前,视图实际上不会重绘,此时所有无效视图都会更新。

如果您的视图由`CAEAGLLayer`创建,则此方法无效。它仅适用于使用
本机绘图技术的视图(例如UIKit和Core Graphics)来呈现他们的内容。

只有在视图的内容或外观发生更改时,才应使用此方法请求重绘视图。如果只
是更改视图的几何图形,则通常不会重绘视图。而是根据视图的contentMode
属性中的值调整其现有内容。通过避免重绘未更改的内容的需要,重新显示现
有内容可以提高性能。

所以,
setNeedsDisplay就是主动触发重绘方法,等于calldrawRect:方法,drawRect:View创建的时候设置bounds的时候调用的,setNeedsDisplay要配合drawRect:方法一起使用,重写drawRect:
再者,setNeedsDisplay调用后并不会立马重绘,只是做了一个重绘标记,在下一个绘图周期(1/60秒)来之后再重绘。

  • 写个demo:
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.testView = [[OneView alloc] init];
    self.testView.color = [UIColor redColor];
    self.testView.frame = CGRectMake(100, 100, 200, 200);
    [self.view addSubview:self.testView];
    
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.frame = CGRectMake(100,300 , 100, 100);
    button.backgroundColor = [UIColor greenColor];
    [button addTarget:self action:@selector(changeColor) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}

- (void)changeColor {
    self.testView.color = [UIColor blackColor];
    [self.testView setNeedsDisplay];
}
#import "OneView.h"

@implementation OneView

- (void)drawRect:(CGRect)rect {
    UIBezierPath *path  = [UIBezierPath bezierPathWithOvalInRect:rect];
    [self.color setFill];
    [path fill];
}

@end

点击按钮,改变圆圈颜色,触发重绘。

QQ20190710-191117-HD.gif

还有一点,官方文档里提到的 只有在视图的内容或外观发生更改时,才应使用此方法请求重绘视图。如果只 是更改视图的几何图形,则通常不会重绘视图。而是根据视图的contentMode 属性中的值调整其现有内容。
如果只是修改外观,比如颜色,透明度之类的才需要调用此方法重绘,如果修改 bouds之类的,则需要看 contentMode
再看个demo:

- (void)changeColor {
    self.testView.contentMode = UIViewContentModeRedraw;
    self.testView.bounds = CGRectMake(100, 100, 100, 100);
    [self.testView setNeedsDisplay];
}

如果指定contentModeUIViewContentModeRedraw,那则可以修改Viewbounds,如果指定为其他的mode则不能触发重绘,但是我发现不指定contentMode也可以触发重绘。
关于这个方法,差不多就这些要点了。


2、layoutSubviews
官方文档解释

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.

翻译一下有以下几个要点:
1、这个方法默认是不做任何事情的。
2、当系统提供的自动布局不能满足你的布局需求时,可以重写这个方法。
3、不要直接在外部调用这个方法,如果你想强制更新布局,请调用`setNeedsLayout`;
   如果你要立即更新布局,请调用`layoutIfNeeded`。

什么时机会触发layoutSubviews 呢?
1、addSubView到另外一个视图
2、改变一个UIView里面的子视图的frame
3、滚动一个UIScrollView
4、旋转屏幕
调用时机还是蛮多的,所以不要在这个方法里做一些耗内存的事情,比如新建一个view之类的蠢事,这个方法里只能做些调整布局的小事。


3、setNeedsLayoutlayoutIfNeeded
这两个方法都可以刷新布局,唯一的区别就是刷新时机不一样,setNeedsLayout强制刷新布局,在下一个更新周期来到的时候一并更新,所以你可以一口气更新所有你想更新的布局,在调用setNeedsLayout来一并更新,苹果可能觉得这样做更加节省性能。而layoutIfNeeded则是不等下个更新周期来之前立刻马上更新布局!这就是二者的区别。

你可能感兴趣的:(setNeedsDisplay 、layoutSubviews、setNeedsLayout、layoutIfNeeded)