layoutSubviews, layoutIfNeeded, setNeedsLayout 트리거 시기와 규칙

7258 단어
저자: 전설의 사이다 총 주소: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
"); } @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
상술한 두 가지 예는 addSubview를 없앨 때 세 번째 줄의 결과를 출력하지 않는다.상술한 두 가지 예에서 출력 순서와 결과를 알아차리면 우리는 알 수 있다
  • view의 초기화는 프레임이든 아니든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
    "); [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 프레임이 변하지 않았을 때 아무런 효과가 없습니다.
    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
    

    결론
    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
    

    저번 테스트 결과랑 똑같아요.
    요약:
  • setNeedsLayout을 호출하면 프레임에 변화가 있든 없든 다음 인터페이스 리셋 주기에layoutSubviews
  • 를 호출합니다
  • layoutIfNeeded를 호출할 때 프레임이 바뀌면layoutSubviews를 호출하고 그렇지 않으면layoutSubviews를 호출하지 않습니다
  • layoutSubviews가 트리거하는 시기
  • 자신을 다른 뷰에 추가할 때 서브뷰를 추가할 때 터치되지 않으며 다음 리셋 주기에 호출됩니다
  • 프레임이 변할 때, 그리고 다음 리셋 주기에 호출
  • setNeedsLayout, 그리고 다음 리셋 주기에 호출
  • Frame 변경 및 layoutIfNeeded 호출, 즉시 호출, 현재 주기 호출
  • 좋은 웹페이지 즐겨찾기