iOS UIPageViewController缺陷
为什么弃用UIPageViewController?
问题1:
设置UIPageViewController为UIPageViewControllerTransitionStyleScroll且调用setViewControllers:direction:animated:completion:传递参数animated YES时,会引发一系列症状,例如:
1. 缓存页面导航设置不正确,Page View Controller会导航到错误的页面。
2. 删除上一页view controller(已经切换完成后)失败,仍然可以scroll回到一个空白页。
这是UIPageViewController的bug,当且仅当UIPageViewController为.Scroll时,调用setViewControllers:direction:animated:completion: 且方法参数animated YES才可能出现。
错误的原因是,当使用.Scroll风格时,UIPageViewController做了一些内部的缓存排序,当调用setViewControllers:direction:animated:completion:时,它没有清空其内部缓存。它认为它已经知道前一个页面的存在,当它调用前一个页面的时候,就不会去调用dataSource方法或者调用错误。
根据这个描述,其实可以对之前的错误代码做相应的处理,通过再次调用setViewControllers方法强制重新调用dataSource方法以更新缓存。例如:
@weakify(self)
[self.pageViewController setViewControllers:@[targetViewController]
direction:direction
animated:animated
completion:^(BOOL finished)
{
@strongify(self)
if (finished) {
[self.pageViewController setViewControllers:@[self.pages[index]] direction:direction animated:NO completion:NULL];
// bug fix for uipageview controller
}
}];
很不幸这种做法引发了崩溃(iOS7/8/9):
Assertion failure in
[_UIQueuingScrollView_replaceViews:updatingContents:adjustContentInsets:animated:], /SourceCache/UIKit_Sim/UIKit-3318.16.14/_UIQueuingScrollView.m:383**
分析后发现我们忽略的一个重点,setViewControllers这个方法是更新页面的操作应该在主线程中调用。修改代码如下:
@weakify(self)
[self.pageViewController setViewControllers:@[targetViewController]
direction:direction
animated:animated
completion:^(BOOL finished)
{
@strongify(self)
if (finished) {
dispatch_async(dispatch_get_main_queue(), ^
{
if (self) {
[self.pageViewController setViewControllers:@[self.pages[index]] direction:direction animated:NO completion:NULL];
// bug fix for uipageview controller
}
});
}
}];
代码看起来虽然不怎么优雅,但好在这样可以继续使用UIPageViewController。直到我们发现了Fabric上的崩溃和上段解决方案代码引发的新问题:
Fabric崩溃:
UIPageViewController_Fabric.jpg
这个崩溃在iOS7/8/9上都有出现。显然这和Apple Developer Forums中提到的问题现象是一样的:No view controller managing visible view:
但Apple Developer Forums中的问题,是因为没有在主线程中执行第二次setViewControllers方法导致的。我们的代码里已经做了相应处理,但仍然有小规模数量的崩溃。很不幸,暂时还没找到Fabric崩溃的具体复现方式。
上图说明,上段代码并没有完全解决page view controller页面缓存不正确的bug。只是不会那么频繁地出现。
引发新问题:
此外还发现:当通过上段代码导航切换index的时候,如果第一次导航(setViewControllers)动画没有结束时,马上开始第二次导航(setViewControllers)。很容易复现问题:页面没有停在最终的那个index上。这是因为第二次主动调用的带动画的setViewControllers执行在第一次调用的回调的无动画的setViewControllers之前导致的。
问题2:
切换childViewController引起的卡顿问题很严重。在iPhone4、4s、5、5c、甚至5s上都会有不同程度的卡顿问题:
一般情况下,page view controller切换页面的资源消耗至少相当于调用一次transitionFromViewController:toViewController:duration:options:animations:completion:。在硬件配置较低的iPhone4、4s等手机上就会有明显卡顿,如果child view controller的生命周期方法中再做一些消耗资源的操作,App甚至会因为切换导致资源占用过多、内存警告,最终引起Crash。这种情况给低配手机性能优化带来了很大障碍。
其性能问题主要体现在,切换childController时候的CPU占用升高、以及切换时的内存频繁波动。
静止状态下,其CPU占用率位置在很低的水品甚至不到1%,当child congroller切换时,其CPU占用率如图所示(iPhone6 Plus/iOS9.3):
UIPageViewController快速非交互切换_CPU.png
UIPageViewController快速交互切换_CPU.png
文章开头介绍UIPageViewController时提到过,UIPageViewController的设计更多的考虑了少占用内存,从下图中内存的波动曲线也可以看到。当频繁切换child controller时,UIPageViewController尽可能快的清理内存。快速切换时,其内存波动如下图所示(iPhone6 Plus/iOS9.3):
总结:
经过多个设备、系统版本的测试和网上资料的整理。问题1的处理方案是现在最主流的一种解决方案,但这样的方案仍然引发了Fabric崩溃和新的缺陷问题,从保证App质量的角度出发,这个解决方案是不可取的。问题2的性能问题虽然在高配手机上并不明显,但考虑的所有用户的体验这个缺陷也很值得去优化。
为了彻底解决这些缺陷问题,重新开发一个Page view controller控件来彻底解决UIPageViewController带来的缺陷问题。
iOS UIPageViewController缺陷的更多相关文章
- iOS UIPageViewController
UIPageViewController是App中常用的控制器.它提供了一种分页效果来显示其childController的View.用户可以通过手势像翻书一样切换页面.切换页面时看起来是连续的,但静 ...
- iOS:UIPageViewController翻页控制器控件详细介绍
翻页控制器控件:UIPageViewController 介绍: 1.它是为我们提供了一种类似翻书效果的一种控件.我们可以通过使用UIPageViewController控件,来完成类似图书一样的翻页 ...
- Learn how to Use UIPageViewController in iOS
下面学习内容来自国外的IOS学习网站:The AppGuruz: UIPageViewController in iOS 也许需要FQ哦 认真做一遍上面入门UIPageController的教程,然 ...
- iOS 5 :一个UIPageViewController程序示例
原文:http://www.techotopia.com/index.php/An_Example_iOS_5_iPhone_UIPageViewController_Application 在Xco ...
- 获取iOS系统版本号,慎重使用[[[UIDevice currentDevice] systemVersion] floatValue]——【sdk缺陷】
iOS 最常见的获取系统版本的方法是: [[[UIDevice currentDevice] systemVersion] floatValue] 可是.这个floatValue是不靠谱的,这也算是i ...
- unity的List构造函数在IOS平台存在缺陷
当迩使用一个int[]或者string[]类似的数组时,以数组来初始化List对象,有可能在IOS平台上会出现初始化对象为空,比如 , }; List<int> listTest = ne ...
- App 开发中判断 ios 和 andriod 常用方法便于修复在两类机型样式不一样等缺陷
判断安卓, ios
- <<精通iOS开发>>第14章例子代码小缺陷的修复
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 首先推荐大家看这本书,整本书逻辑非常清晰,代码如何从无到有,到 ...
- 【腾讯Bugly干货分享】移动App入侵与逆向破解技术-iOS篇
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/577e0acc896e9ebb6865f321 如果您有耐心看完这篇文章,您将懂 ...
随机推荐
- .Net 转战 Android 4.4 日常笔记目录
.Net 转战 Android 4.4 日常笔记(1)--工具及环境搭建 .Net 转战 Android 4.4 日常笔记(2)--HelloWorld入门程序 .Net 转战 Android 4.4 ...
- HTTP的RST包与WinHttp延迟关闭TCP连接
一.RST包也常见于断开TCP连接 几个月前用wireshark抓HTTP包发现有的网络通信在结束的时候没有使用四次握手,而是直接使用RST包.如: 在TCP协议中RST表示复位,用来异常的关闭连接 ...
- Restore Volume 操作 - 每天5分钟玩转 OpenStack(60)
前面我们 backup 了 voluem,今天我们将讨论如何 restore volume. restore 的过程其实很简单,两步走: 在存储节点上创建一个空白 volume. 将 backup 的 ...
- 应用程序框架实战三十六:CRUD实战演练介绍
从本篇开始,本系列将进入实战演练阶段. 前面主要介绍了一些应用程序框架的概念和基类,本来想把所有概念介绍完,再把框架内部实现都讲完了,再进入实战,这样可以让初学者基础牢靠.不过我的精力很有限,文章进度 ...
- Rust初步(一):介绍
最近在研究Rust这个新的语言.那么Rust是什么呢? Rust是一个注重安全与速度的现代系统编程语言,通过在没有垃圾回收的情况下保证内存安全来实现它的目标,这使它成为一个在很多其它语言不适合的用例中 ...
- 1Z0-053 争议题目解析134
1Z0-053 争议题目解析134 考试科目:1Z0-053 题库版本:V13.02 题库中原题为: 134.You are managing an Oracle Database 11g datab ...
- Cesium原理篇:6 Render模块(5: VAO&RenderState&Command)
VAO VAO(Vertext Array Object),中文是顶点数组对象.之前在<Buffer>一文中,我们介绍了Cesium如何创建VBO的过程,而VAO可以简单的认为是基于VBO ...
- SFC中的故障管理
1.SFC中包的格式 网络服务包头,当Obit被设置为1时,表示一个SFC OAM消息 OAM Type:SFC OAM的类型(1.连接认证,2.持续性检验,3.路由跟踪,4.性能度量) SFC OA ...
- JavaScript代码模块化的正规方法
RequireJS-CommonJS-AMD-ES6 Import/Export详解 为什么起了一个这个抽象的名字呢,一下子提了四个名词分别是:RequireJS,CommonJS,AMD,ES6,答 ...
- OpenCV2简单的特征匹配
特征的匹配大致可以分为3个步骤: 特征的提取 计算特征向量 特征匹配 对于3个步骤,在OpenCV2中都进行了封装.所有的特征提取方法都实现FeatureDetector接口,DescriptorEx ...