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 如果您有耐心看完这篇文章,您将懂 ...
随机推荐
- 通过setTimeout来取消因大量计算造成的网页卡顿
js是单线程的,所以有些大量计算的操作会占用线程资源,导致页面卡住. 今天遇到这样一个场景,选择一个下拉框之后,对数据进行筛选,这个过程中有大量计算,点了selecte的option之后,option ...
- 生成Kindle可读的mobi和PDF电子书
购买kindle之后,自然欣喜万分,不来自于工具本身,而来自于发现自己能够静下心来阅读长篇和复杂的文字了,可喜可贺.更重要的是,kindle减轻了我眼睛的莫大的压力.但马上就出现几个问题: 不是所有的 ...
- PHP精选数组函数
编程怎么能少的了数组呢,以下是学习PHP时常用的数组处理函数.在编程中要遵循一个原则就是DRY(Don`t Repeat Yourself)原则,PHP中有大量的函数,都记住这些函数不太现实,但常用的 ...
- 深入理解 OWIN 中的 Host 和 Server
The Open Web Interface for .NET (OWIN),注意单词为大写,之前好像都写成了 Owin,但用于项目的时候,可以写成:Microsoft.Owin.*. OWIN 体系 ...
- Android中Fragment+ViewPager的配合使用
官方推荐 ViewPager与Fragment一起使用,可以更加方便的管理每个Page的生命周期,这里有标准的适配器实现用于ViewPager和Fragment,涵盖最常见的用例.FragmentPa ...
- CSS垂直居中和水平居中
前言 CSS居中一直是一个比较敏感的话题,为了以后开发的方便,楼主觉得确实需要总结一下了,总的来说,居中问题分为垂直居中和水平居中,实际上水平居中是很简单的,但垂直居中的方式和方法就千奇百怪了. 内联 ...
- QTableWidget行选中/删除/添加行
1 均分各列 tableWidget->horizontalHeader()->setStretchLastSection(true); //就是这个地方 tableWidget-> ...
- 关系数据库SQL之可编程性存储过程
前言 前面关系数据库SQL之可编程性函数(用户自定义函数)一文提到关系型数据库提供了可编程性的函数.存储过程.事务.触发器及游标,前文已介绍了函数,本文来介绍一下存储过程的创建.执行.删除.(还是以前 ...
- 自己实现简单的AOP(四)自动初始化代理对象
前面三篇随笔,已经完成了AOP的核心功能,但 代理对象的初始化还是有些麻烦,本文将解决该问题. Demo 片段如下: public class HomeController : Controller ...
- ISS部署网站--HTTP 错误 404.17 - Not Found 请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理。
1.控制面板>程序和功能>打开或关闭Windows功能 > Internet信息服务 > 万维网服务 > 应用程序开发功能 > ASP.NET(看这个是否选上): ...