这几个都是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
就是主动触发重绘方法,等于call
了drawRect:
方法,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];
}
如果指定contentMode
为UIViewContentModeRedraw
,那则可以修改View
的bounds
,如果指定为其他的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、setNeedsLayout
和layoutIfNeeded
这两个方法都可以刷新布局,唯一的区别就是刷新时机不一样,setNeedsLayout
强制刷新布局,在下一个更新周期来到的时候一并更新,所以你可以一口气更新所有你想更新的布局,在调用setNeedsLayout
来一并更新,苹果可能觉得这样做更加节省性能。而layoutIfNeeded
则是不等下个更新周期来之前立刻马上更新布局!这就是二者的区别。