作者:传说中的汽水枪
地址:https://www.jianshu.com/p/f17ac629dbc0
版权所有,欢迎转载,转载请注明出处,欢迎留言评论。
在实际的开发过程中和面试当中都会遇到这样的问题,先用相关的测试代码。
测试View RXLayoutView
@implementation RXLayoutView
- (id)init
{
if (self = [super init]) {
self.backgroundColor = [UIColor redColor];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor redColor];
}
return self;
}
- (void)layoutSubviews
{
printf("RXLayoutView layoutSubviews\n");
}
@end
frame 为zero
- (void)_test_layoutSubviews_zeroFrame
{
RXLayoutView *view = [[RXLayoutView alloc] init];
printf("after alloc init\n");
[self.view addSubview:view];
printf("after add\n");
}
输出:
after alloc initWithFrame
after add
RXLayoutView layoutSubviews
frame为NoneZero
- (void)_test_layoutSubviews_noneZeroFrame
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame\n");
[self.view addSubview:view];
printf("after add\n");
}
输出:
after alloc initWithFrame
after add
RXLayoutView layoutSubviews
结论一
上述两个例子当把addSubview
给去掉的时候,是不会输出第三行结果的。
从上述两个例子注意到输出顺序和结果,我们可以得知
- view的初始化无论是否是frame都不会触发layoutSubviews
- 只有当添加到其他view的时候,才会触发layoutSubviews,并且是在下一个view刷新的时候触发layoutSubviews
layoutSubviews_noneZeroFrame_changeFrame
- (void)_test_layoutSubviews_noneZeroFrame_changeFrame
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame\n");
[self.view addSubview:view];
printf("after add\n");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
printf("before change frame\n");
view.frame = CGRectMake(100, 200, 200, 200);
printf("after change frame\n");
});
});
}
输出:
after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before change frame
after change frame
RXLayoutView layoutSubviews
注:这里的changeFrame包括 x,y,width,height中的任何一个值。
结论二:
变化frame的时候,触发layoutSubviews(是在下一个刷新周期触发,而不是在修改的时候触发)
layoutSubviews_noneZeroFrame_layoutIfNeeded
- (void)_test_layoutSubviews_noneZeroFrame_layoutIfNeeded
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame\n");
[self.view addSubview:view];
printf("after add\n");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
printf("before layoutIfNeeded\n");
[view layoutIfNeeded];
printf("after layoutIfNeeded\n");
});
});
}
输出:
after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before layoutIfNeeded
after layoutIfNeeded
结论三
layoutIfNeeded如果frame没有变化的时候,是没有任何效果的。
layoutSubviews_noneZeroFrame_changeFrame_layoutIfNeeded
- (void)_test_layoutSubviews_noneZeroFrame_changeFrame_layoutIfNeeded
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame\n");
[self.view addSubview:view];
printf("after add\n");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
printf("before change frame and layoutIfNeeded\n");
view.frame = CGRectMake(100, 200, 200, 200);
[view layoutIfNeeded];
printf("after change frame and layoutIfNeeded\n");
});
});
}
输出:
after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before change frame and layoutIfNeeded
RXLayoutView layoutSubviews
after change frame and layoutIfNeeded
结论四
frame变化和layoutIfNeeded会立即触发layoutSubviews
layoutSubviews_noneZeroFrame_setNeedsLayout
- (void)_test_layoutSubviews_noneZeroFrame_setNeedsLayout
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame\n");
[self.view addSubview:view];
printf("after add\n");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
printf("before setNeedsLayout\n");
[view setNeedsLayout];
printf("after setNeedsLayout\n");
});
});
}
输出:
after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before setNeedsLayout
after setNeedsLayout
RXLayoutView layoutSubviews
结论五
frame没有变化, setNeedsLayout是强制layoutSubviews,但是是在下一个页面刷新周期
layoutSubviews_noneZeroFrame_changeFrame_setNeedsLayout
- (void)_test_layoutSubviews_noneZeroFrame_changeFrame_setNeedsLayout
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame\n");
[self.view addSubview:view];
printf("after add\n");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
printf("before change frame and setNeedsLayout\n");
view.frame = CGRectMake(100, 200, 200, 200);
printf("after change frame and before setNeedsLayout\n");
[view setNeedsLayout];
printf("after change frame and setNeedsLayout\n");
});
});
}
输出
after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before change frame and setNeedsLayout
after change frame and before setNeedsLayout
after change frame and setNeedsLayout
RXLayoutView layoutSubviews
跟上一个测试结果一样
总结:
- 当调用了setNeedsLayout,无论frame有没有变化,都会在下一个界面刷新周期的时候调用layoutSubviews
- 当调用layoutIfNeeded,如果frame变化了会调用layoutSubviews,否则不会调用layoutSubviews
- layoutSubviews 触发的时机
- 把自己添加到其他View的时候,添加子view的时候并不会触发,并且是在下一个刷新周期的时候调用
- Frame变化的时候,并且是在下一个刷新周期的时候调用
- setNeedsLayout,并且是在下一个刷新周期的时候调用
- Frame变化且调用 layoutIfNeeded,会立即调用,在当前周期调用
网友评论