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 如果您有耐心看完这篇文章,您将懂 ...
随机推荐
- javascript运动系列第三篇——曲线运动
× 目录 [1]圆周运动[2]三维圆周 [3]钟摆运动 [4]抛物线[5]流体运动 前面的话 上一篇介绍了变速运动,但只实现了直线运动.如果元素的left和top同时运动,并遵循不同的曲线公式,则会进 ...
- ASP.NET MVC之Unobtrusive Ajax(五)
前言 这一节我们来讲讲Unobtrusive中的Ajax提交,大部分情况下我们是利用JQuery来进行Ajax请求,当然利用JQuery来进行表单Ajax请求也不例外,但是相对于Unobtrusive ...
- 窥探Swift之类的继承与类的访问权限
上一篇博客<窥探Swift之别具一格的Struct和Class>的博客可谓是给Swift中的类开了个头.关于类的内容还有很多,今天就来搞一下类中的继承以及类的访问权限.说到类的继承,接触过 ...
- Objective-C中的老板是这样发通知的(Notification)
通知(Notification)简单的类比一下,公司的老总给下面的员工发通知啦,说明天公司要上市,各部门做一下准备工作.等通知发完,各部门收到后各司其职,做着自己该做的东西.假如Boss是 ...
- Linux分区:超过2TB硬盘分区
测试iscsi服务是否正常 [root@FocusBackup ~]# service iscsi restart 停止 iscsi: ...
- 把《c++ primer》读薄(3-1 标准库string类型初探)
督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正. 问题1:养成一个好习惯,在头文件中只定义确实需要的东西 using namespace std; //建议需要什么再using声 ...
- 四大组件之ContentProvider
前言 ContentProvider作为Android的四大组件之一,是属于需要掌握的基础知识,可能在我们的应用中,对于Activity和Service这两个组件用的很常见,了解的也很多,但是对Con ...
- 匿名方法与Lambda表达式
1.匿名方法 在学习委托时,我们知道委托实例至少要绑定一个方法才能使用,而调用委托实际上是调用了它所关联地方法.一般来说,需要定义一个与委托签名相符的方法,并使之与委托变量关联.如以下代码: Acti ...
- 设置WPF输入框焦点
在WPF中设置控件键盘焦点 Keyboard.Focus(/*控件名称*/);
- Oracle11g 配置 ST_GEOMETRY
安装环境:ArcGIS Desktop10.2.1 .ArcSDE10.2.134940. Oracle11.2.0.1 操作系统:Windows Server 2012R2 DataCenter 安 ...