博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
布局结束检测工具
阅读量:7038 次
发布时间:2019-06-28

本文共 3658 字,大约阅读时间需要 12 分钟。

有些时候,需要知道什么时候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'复制代码

转载地址:http://xgyal.baihongyu.com/

你可能感兴趣的文章
scrum之旅读书笔记
查看>>
PIX8.0的LAN-to-LAN IPSEC***反向路由注入测试
查看>>
实现F5后端服务器访问
查看>>
ThinkPHP源码阅读2-----C函数配置文件详解
查看>>
6421B Lab6 NPS角色服务的安装、配置和故障排除
查看>>
快速提高博客访问量的22条军规
查看>>
***S 2012 内置字段 -- 介绍
查看>>
MongoDB自然排序
查看>>
AutoCAD快捷键大全
查看>>
vCloud Automation Center (vCAC) 6.0 (六)
查看>>
微软推出一个非常有趣的网站—— How-old.net 看照片猜年龄!
查看>>
puppet之master/agent模型详解
查看>>
Docker分离部署MySQL、Nginx+Tomcat复制共享
查看>>
hashtable和dictionary
查看>>
05-Windows Server 2012 新特性 ---- 集成的ISCSI网络存储
查看>>
Percona XtraDB Cluster Installation Guide
查看>>
.net中的mapinfo开发:图层读写(二)
查看>>
Percona Cluster集群讲解
查看>>
让RHEL5.8支持ext4文件系统
查看>>
IPHONE5为什么爽约?
查看>>