美文网首页iOS面试总结
layoutSubviews,layoutIfNeeded,se

layoutSubviews,layoutIfNeeded,se

作者: 传说中的汽水枪 | 来源:发表于2019-02-17 21:14 被阅读8次

    作者:传说中的汽水枪
    地址: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
    

    跟上一个测试结果一样

    总结:

    1. 当调用了setNeedsLayout,无论frame有没有变化,都会在下一个界面刷新周期的时候调用layoutSubviews
    2. 当调用layoutIfNeeded,如果frame变化了会调用layoutSubviews,否则不会调用layoutSubviews
    3. layoutSubviews 触发的时机
    • 把自己添加到其他View的时候,添加子view的时候并不会触发,并且是在下一个刷新周期的时候调用
    • Frame变化的时候,并且是在下一个刷新周期的时候调用
    • setNeedsLayout,并且是在下一个刷新周期的时候调用
    • Frame变化且调用 layoutIfNeeded,会立即调用,在当前周期调用

    相关文章

      网友评论

        本文标题:layoutSubviews,layoutIfNeeded,se

        本文链接:https://www.haomeiwen.com/subject/reyneqtx.html