UITableView
看看我们的复杂状态量有哪些
- init 方法
- updateConstrains 方法
- drawRect 方法
- setNeedsDisplay
breakpoint1 - (instancetype)init {};
breakpoint2 - (void)setNeedsDisplay {};
breakpoint3 - (void)updateConstraints {};
breakpoint4 - (void)drawRect:(CGRect)rect {};
根据我的测试 调用栈是这样的
cell1 init updateConstrains setNeedsDisplay drawRect
2017-01-18 22:54:52.221624 RecordLife[6006:1468686] setNeedsDisplay
2017-01-18 22:54:52.221753 RecordLife[6006:1468686] setNeedsDisplay
2017-01-18 22:54:52.221805 RecordLife[6006:1468686] setNeedsDisplay
2017-01-18 22:54:52.225492 RecordLife[6006:1468686] updateConstraints
2017-01-18 22:54:52.229674 RecordLife[6006:1468686] updateConstraints
2017-01-18 22:54:52.237403 RecordLife[6006:1468686] layoutSubviews
2017-01-18 22:54:52.259127 RecordLife[6006:1468686] drawRect
一些布局相关的方法
//layoutIfNeeded 强制提前在 drawRect 前布局
// Allows you to perform layout before the drawing cycle happens. -layoutIfNeeded forces layout early
- (void)setNeedsLayout;
- (void)layoutIfNeeded;
// iOS 6.0 layoutIfNeeded 会自动调用 layoutSubviews
- (void)layoutSubviews; // override point. called by layoutIfNeeded automatically. As of iOS 6.0, when constraints-based layout is used the base implementation applies the constraints-based layout, otherwise it does nothing.
//在传入的矩形中绘制接收器的图像。
//此方法的默认实现不执行任何操作。使用Core Graphics和UIKit等技术绘制视图内容的子类应该重写这个方法并在那里实现它们的绘图代码。如果您的视图以其他方式设置其内容,则不需要覆盖此方法。例如,如果视图仅显示背景颜色,或者您的视图使用底层对象直接设置其内容,则不需要重写此方法。
//在调用此方法时,UIKit已为您的视图正确配置了绘图环境,您可以简单地调用任何绘制方法和函数来渲染内容。具体来说,UIKit创建和配置图形上下文以绘制和调整该上下文的变换,使其原点与视图边界矩形的原点相匹配。您可以使用UIGraphicsGetCurrentContext函数获取对图形上下文的引用,但不建立对图形上下文的强引用,因为它可以在调用drawRect:方法之间更改。
//类似地,如果使用OpenGL ES和GLKView类来绘制,GLKit在调用此方法(或GLKView委托的glkView:drawInRect:方法)之前,为您的视图适当地配置底层OpenGL ES上下文,因此您可以简单地发出任何OpenGL ES命令您需要呈现您的内容。有关如何使用OpenGL ES绘制的详细信息,请参阅适用于iOS的OpenGL ES编程指南。
//您应该将任何绘图限制为rect参数中指定的矩形。此外,如果视图的opaque属性设置为YES,您的drawRect:方法必须使用不透明内容完全填充指定的矩形。
//如果你直接子类化UIView,你的这个方法的实现不需要调用super。然而,如果你是一个不同的视图类的子类,你应该在实现的某个点调用super。
//当首次显示视图时或当发生使视图的可见部分无效的事件时,将调用此方法。你不应该直接自己调用这个方法。要使视图的一部分无效,从而使该部分重绘,请调用setNeedsDisplay或setNeedsDisplayInRect:方法。
- (void)drawRect:(CGRect)rect;
// 通知系统,您的视图内容需要重绘,此方法记下请求并立即返回。 在下一个绘制周期之前,实际上不会重绘视图,此时所有无效视图都会更新。
- (void)setNeedsDisplay;
// 特定的矩形范围内重绘
- (void)setNeedsDisplayInRect:(CGRect)rect;
父子视图调用
需要注意的是 updateConstrains 是从 子视图 开始 父视图 结束
view
subview
观察 View frame
There are usually notifications or other observable events where KVO isn’t supported. Even though the docs says ‘no’, it is ostensibly safe to observe the CALayer backing the UIView. Observing the CALayer works in practice because of its extensive use of KVO and proper accessors (instead of ivar manipulation). It’s not guaranteed to work going forward.
Anyway, the view’s frame is just the product of other properties. Therefore we need to observe those:
[self.view addObserver:self forKeyPath:@"frame" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"bounds" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"transform" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"position" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"zPosition" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"anchorPoint" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"anchorPointZ" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"frame" options:0 context:NULL];
See full example here https://gist.github.com/hfossli/7234623
NOTE: This is not said to be supported in the docs, but it works as of today with all iOS versions this far (currently iOS 2 -> iOS 10)
With ReactiveCocoa you can do
RACSignal *signal = [RACSignal merge:@[
RACObserve(view, frame),
RACObserve(view, layer.bounds),
RACObserve(view, layer.transform),
RACObserve(view, layer.position),
RACObserve(view, layer.zPosition),
RACObserve(view, layer.anchorPoint),
RACObserve(view, layer.anchorPointZ),
RACObserve(view, layer.frame),
]];
[signal subscribeNext:^(id x) {
NSLog(@"View probably changed its geometry");
}];
And if you only want to know when bounds changes you can do
@weakify(view);
RACSignal *boundsChanged = [[signal map:^id(id value) {
@strongify(view);
return [NSValue valueWithCGRect:view.bounds];
}] distinctUntilChanged];
[boundsChanged subscribeNext:^(id ignore) {
NSLog(@"View bounds changed its geometry");
}];
And if you only want to know when frame changes you can do
@weakify(view);
RACSignal *frameChanged = [[signal map:^id(id value) {
@strongify(view);
return [NSValue valueWithCGRect:view.frame];
}] distinctUntilChanged];
[frameChanged subscribeNext:^(id ignore) {
NSLog(@"View frame changed its geometry");
}];