美文网首页
布局结束检测工具

布局结束检测工具

作者: 闹鬼的金矿 | 来源:发表于2018-08-06 17:11 被阅读19次

有些时候,需要知道什么时候View会布局完成。比如需要在View布局完成之后,希望页面自动跳转到某一个模块,如果不知道View什么时候布局完成,那跳转到某个位置的高度就无法计算了。

页面渲染布局必然是在准备好数据之后,所以通常一个页面先要通过网络请求将数据获取,然后再通过UI组件进行渲染。所以要对View的布局进行监听肯定是在网络请求回来之后了,对于网络什么时候会结束只可能是业务层自己才能知道,所以主要精力是分析网络请求回来之后的事情。

基本思路是通过CADdisplayLink来记录每一帧当前View作为根节点时视图层级的情况,同时和上一帧进行比较。如果在一个较短的时间内发现两者不一致,就说明布局还未结束。如果这个时间之内两者一致,则说明布局完成了。正常情况下,如果界面没有发生卡顿,一秒应该在40帧以上。如果设置的这个时间间隔是0.2s,那么在这个时间间隔中,会绘制屏幕8次,也就是说如果连续8次屏幕渲染没有发生视图层级的变化就认为是布局结束了。

具体如何比较前后两帧是否一致呢,我是通过字符串记录当前帧视图层级中每一个View的对象信息(内存地址,坐标和宽高),比较两个字符串是否相等,如果相等就说明布局没有发生变化如果不等就说明发生了变化。

要点:

1 为了更少的侵入业务层,通过category + associatedObject来实现。

2 0.2s的时间间隔需要一个定时任务,通过GCD Source来实现,不使用NSTimer因为前者要更精确,不依赖Runloop,不受其他任务的影响。

3 如何说明它的正确性:主要是通过和ViewController的声明周期方法viewDidLayoutSubViews调用时间先后的对比,如果还未布局完成那么之后系统框架还会再调用viewDidLayoutSubViews,所以只需要看回调是不是发生在viewDidLayoutSubViews之后,最后通过demo验证了这种方案的可行性。

代码:

UIView + LayoutCompleteChecker.h

typedef void(^callback)();
- (void)startCheckingWithCompletionBlock:(callback)callback;

UIView + LayoutCompleteChecker.m

- (NSString *)viewTreeString
{
    return objc_getAssociatedObject(self, @selector(viewTreeString));
}

- (void)setViewTreeString:(NSString *)treeString
{
    objc_setAssociatedObject(self, @selector(viewTreeString), treeString, OBJC_ASSOCIATION_COPY);
}

- (NSNumber *)hasLayoutCompleted
{
    NSNumber *result = objc_getAssociatedObject(self, @selector(hasLayoutCompleted));
    if (!result) {
        return @(NO);
    } else {
        return result;
    }
}

- (void)setLayoutCompleted:(NSNumber *)result
{
    objc_setAssociatedObject(self, @selector(hasLayoutCompleted), result, OBJC_ASSOCIATION_RETAIN);
}

- (CADisplayLink *)displayLink
{
    CADisplayLink *link = objc_getAssociatedObject(self, @selector(displayLink));
    if (!link) {
        link = [CADisplayLink displayLinkWithTarget:self selector:@selector(p_checkerTick)];
        objc_setAssociatedObject(self, @selector(displayLink), link, OBJC_ASSOCIATION_RETAIN);
    }
    return link;
}

- (void)p_checkerTick
{
    NSString *treeString = self.viewTreeString;
    NSString *currentTreeString = self.currentLayoutString;
    if (![treeString isEqualToString:currentTreeString]) {
        [self setViewTreeString:currentTreeString];
    } else {
        [self setLayoutCompleted:@(YES)];
    }
}

- (NSString *)currentLayoutString
{
    NSMutableString *layoutString = [NSMutableString stringWithFormat:@"%@", self];
    if (self.subviews.count) {
        for (int i = 0; i < self.subviews.count; i++) {
            UIView *subView = self.subviews[i];
            NSString *subViewString = [subView currentLayoutString];
            [layoutString appendString:subViewString];
        }
    }
    return [layoutString copy];
}

- (void)startCheckingWithCompletionBlock:(callback)callback
{
    [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, kCheckInterval * NSEC_PER_SEC, kTolerance * NSEC_PER_SEC);
    id __weak weakSelf = self;
    dispatch_source_set_event_handler(timer, ^{
        NSNumber *result = self.hasLayoutCompleted;
        if ([result boolValue]) {
            id __strong strongSelf = weakSelf;
            dispatch_source_cancel(timer);
            [[strongSelf displayLink] invalidate];
            callback();
        }
    });
    dispatch_resume(timer);
}

最后对需要检测的View调用startCheckingWithCompletionBlock方法即可:

    [self.view startCheckingWithCompletionBlock:^{
        NSLog(@"布局完了");
    }];

GitHub:https://github.com/huanshijiushiniu/layoutCompleteChecker

现在已经提交到CocoaPods,可以通过在podfile中添加引用来使用了:

pod 'layoutCompleteChecker', '1.0.0'

相关文章

  • 布局结束检测工具

    有些时候,需要知道什么时候View会布局完成。比如需要在View布局完成之后,希望页面自动跳转到某一个模块,如果不...

  • psp主板检测工具pspident

    主板检测工具 PSP主板检测工具(PSPident),是一款PSP主机适用的主板检测工具,直接安装在PSP中,运行...

  • iOS-app瘦身工具

    一、废弃图片检测工具—LSUnusedResources 1、介绍和使用方法 Xcode工程中废弃切图文件检测工具...

  • react性能优化

    性能检测工具 安装react性能检测工具: 然后在./app/index.js中加入参数: 运行程序,在操作之前在...

  • go 竞态检测

    Go 工具套件在 Go 版本 1.1 引入了一个竞态检测工具(race detector)。这个竞态检测工具是在编...

  • NBSI 安装过程中遇到的BUG

    NBSI是一款由VB语言编写的网站漏洞检测工具的名称,ASP注入漏洞检测工具,特别在SQL Server注入检测方...

  • 强制执行Lint规范代码

    Lint 开发中使用静态代码检测工具对代码进行检查,达到规范代码减少bug的目的。常用的检测工具有FindBugs...

  • 7. 逆向工具集和安装和使用

    iOS逆向工程的工具 大致可分为四类:检测工具、反编译工具、调试工具、开发工具检测工具如:Reveal、tcpdu...

  • 逆向工具列表

    1.1 检测工具 Reveal tcpdump libNotifyWatch PonyDebugger 1.2 开...

  • iOS value stored to i during its

    使用Xcode自带内存检测工具 Product--Analyze

网友评论

      本文标题:布局结束检测工具

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