LayoutSubviews,layoutIfNeeded,setNeedsLayoutトリガタイミングとルール
7376 ワード
作者:伝説のサイダー銃の住所:https://www.jianshu.com/p/f17ac629dbc0著作権はすべて、転載を歓迎して、転載して出典を明記して、伝言の評論を歓迎します.
実際の開発過程や面接では、このような問題に直面することがあります.まず、関連するテストコードを使用します.
View RXLayoutViewのテスト
viewの初期化frameかどうかにかかわらずlayoutSubview はトリガーされません.他のviewに追加された場合にのみlayoutSubviewがトリガーされ、次のviewリフレッシュ時にlayoutSubview がトリガーされます.
layoutSubviews_noneZeroFrame_changeFrame
setNeedsLayoutが呼び出されると、frameが変化するかどうかにかかわらず、次のインタフェースリフレッシュサイクルでlayoutSubviews が呼び出されます. layoutIfNeededが呼び出され、frameが変化するとlayoutSubviewsが呼び出されます.そうしないとlayoutSubviews は呼び出されません. layoutSubviewsトリガのタイミング 他のビューに自分を追加する場合、サブビューを追加するときはトリガーされず、次のリフレッシュサイクルのときに が呼び出される. Frameが変化するとき、そして次のリフレッシュサイクルのときに が呼び出される setNeedsLayoutは、次のリフレッシュサイクル時に を呼び出す Frameが変化するlayoutIfNeededが呼び出されると、すぐに呼び出され、現在のサイクルで が呼び出されます.
実際の開発過程や面接では、このような問題に直面することがあります.まず、関連するテストコードを使用します.
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
");
}
@end
frameはzero - (void)_test_layoutSubviews_zeroFrame
{
RXLayoutView *view = [[RXLayoutView alloc] init];
printf("after alloc init
");
[self.view addSubview:view];
printf("after add
");
}
出力: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
");
[self.view addSubview:view];
printf("after add
");
}
出力:after alloc initWithFrame
after add
RXLayoutView layoutSubviews
結論1
上記の2つの例は、addSubview
を削除した場合、3行目の結果は出力されません.上記の2つの例から出力順序と結果に気づき,我々は
@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
");
}
@end
- (void)_test_layoutSubviews_zeroFrame
{
RXLayoutView *view = [[RXLayoutView alloc] init];
printf("after alloc init
");
[self.view addSubview:view];
printf("after add
");
}
出力:
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
");
[self.view addSubview:view];
printf("after add
");
}
出力:after alloc initWithFrame
after add
RXLayoutView layoutSubviews
結論1
上記の2つの例は、addSubview
を削除した場合、3行目の結果は出力されません.上記の2つの例から出力順序と結果に気づき,我々は
- (void)_test_layoutSubviews_noneZeroFrame
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame
");
[self.view addSubview:view];
printf("after add
");
}
after alloc initWithFrame
after add
RXLayoutView layoutSubviews
layoutSubviews_noneZeroFrame_changeFrame - (void)_test_layoutSubviews_noneZeroFrame_changeFrame
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame
");
[self.view addSubview:view];
printf("after add
");
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
");
view.frame = CGRectMake(100, 200, 200, 200);
printf("after change frame
");
});
});
}
出力:after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before change frame
after change frame
RXLayoutView layoutSubviews
注意:ここでのchangeFrameには、x,y,width,heightのいずれかの値が含まれます.
結論2:
フレームを変更するとlayoutSubviewsがトリガーされます(変更時にトリガーされるのではなく、次のリフレッシュサイクルでトリガーされます)
layoutSubviews_noneZeroFrame_layoutIfNeeded - (void)_test_layoutSubviews_noneZeroFrame_layoutIfNeeded
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame
");
[self.view addSubview:view];
printf("after add
");
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
");
[view layoutIfNeeded];
printf("after layoutIfNeeded
");
});
});
}
出力:after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before layoutIfNeeded
after layoutIfNeeded
結論3
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
");
[self.view addSubview:view];
printf("after add
");
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
");
view.frame = CGRectMake(100, 200, 200, 200);
[view layoutIfNeeded];
printf("after change frame and layoutIfNeeded
");
});
});
}
出力:after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before change frame and layoutIfNeeded
RXLayoutView layoutSubviews
after change frame and layoutIfNeeded
結論4
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
");
[self.view addSubview:view];
printf("after add
");
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
");
[view setNeedsLayout];
printf("after setNeedsLayout
");
});
});
}
出力:after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before setNeedsLayout
after setNeedsLayout
RXLayoutView layoutSubviews
結論5
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
");
[self.view addSubview:view];
printf("after add
");
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
");
view.frame = CGRectMake(100, 200, 200, 200);
printf("after change frame and before setNeedsLayout
");
[view setNeedsLayout];
printf("after change frame and setNeedsLayout
");
});
});
}
しゅつりょく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
次のテストの結果と同じです
まとめ:
- (void)_test_layoutSubviews_noneZeroFrame_changeFrame
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame
");
[self.view addSubview:view];
printf("after add
");
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
");
view.frame = CGRectMake(100, 200, 200, 200);
printf("after change frame
");
});
});
}
after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before change frame
after change frame
RXLayoutView layoutSubviews
- (void)_test_layoutSubviews_noneZeroFrame_layoutIfNeeded
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame
");
[self.view addSubview:view];
printf("after add
");
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
");
[view layoutIfNeeded];
printf("after layoutIfNeeded
");
});
});
}
出力:
after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before layoutIfNeeded
after layoutIfNeeded
結論3
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
");
[self.view addSubview:view];
printf("after add
");
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
");
view.frame = CGRectMake(100, 200, 200, 200);
[view layoutIfNeeded];
printf("after change frame and layoutIfNeeded
");
});
});
}
出力:after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before change frame and layoutIfNeeded
RXLayoutView layoutSubviews
after change frame and layoutIfNeeded
結論4
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
");
[self.view addSubview:view];
printf("after add
");
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
");
[view setNeedsLayout];
printf("after setNeedsLayout
");
});
});
}
出力:after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before setNeedsLayout
after setNeedsLayout
RXLayoutView layoutSubviews
結論5
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
");
[self.view addSubview:view];
printf("after add
");
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
");
view.frame = CGRectMake(100, 200, 200, 200);
printf("after change frame and before setNeedsLayout
");
[view setNeedsLayout];
printf("after change frame and setNeedsLayout
");
});
});
}
しゅつりょく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
次のテストの結果と同じです
まとめ:
- (void)_test_layoutSubviews_noneZeroFrame_changeFrame_layoutIfNeeded
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame
");
[self.view addSubview:view];
printf("after add
");
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
");
view.frame = CGRectMake(100, 200, 200, 200);
[view layoutIfNeeded];
printf("after change frame and layoutIfNeeded
");
});
});
}
after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before change frame and layoutIfNeeded
RXLayoutView layoutSubviews
after change frame and layoutIfNeeded
- (void)_test_layoutSubviews_noneZeroFrame_setNeedsLayout
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame
");
[self.view addSubview:view];
printf("after add
");
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
");
[view setNeedsLayout];
printf("after setNeedsLayout
");
});
});
}
出力:
after alloc initWithFrame
after add
RXLayoutView layoutSubviews
before setNeedsLayout
after setNeedsLayout
RXLayoutView layoutSubviews
結論5
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
");
[self.view addSubview:view];
printf("after add
");
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
");
view.frame = CGRectMake(100, 200, 200, 200);
printf("after change frame and before setNeedsLayout
");
[view setNeedsLayout];
printf("after change frame and setNeedsLayout
");
});
});
}
しゅつりょく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
次のテストの結果と同じです
まとめ:
- (void)_test_layoutSubviews_noneZeroFrame_changeFrame_setNeedsLayout
{
RXLayoutView *view = [[RXLayoutView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
printf("after alloc initWithFrame
");
[self.view addSubview:view];
printf("after add
");
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
");
view.frame = CGRectMake(100, 200, 200, 200);
printf("after change frame and before setNeedsLayout
");
[view setNeedsLayout];
printf("after change frame and setNeedsLayout
");
});
});
}
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