iOS11适配详解

题外话

这是一次公司内部技术分享会的内容,内容共分为三个部分:html

  • Xcode9新特性
  • iOS 11 适配
  • iPhone X适配
    这是第二部分,若有须要请持续关注。
    第一部分Xcode9新特性
    言归正传

掀起江湖恩怨

iOS 11正式版已经来了,做为一个iOS开发者,这意味着没有适配iOS 11都晚了。好在还在Beta阶段我司技术大牛达叔第一时间体验了一把,并仔细的跑了一遍播放端APP触手TV和录制端APP触手录,除了有一个由第三方库WebViewJavascriptBridgeBase引发的严重crash,两个APP在iOS 11下基本没什么问题,发现的问题已经被达叔提早fixed了。因此组里一直没有进行适配工做,而是把精力放在了最近的大版本开发上。观察发现,直接从AppStore下载的应用,在iOS 11上跑起来是没有什么问题的,若是使用Xcode 9 Building后在运行,就或多或少的出现问题。由于Xcode 9 的Base SDKS是基于iOS 11的。因此仍是须要进行适配的。ios

风云再起

经过阅览网上适配iOS 11的同行案列,结合触手TVAPP实际问题,通过汇总,如下多是须要适配的点。找到问题的根源,才能帮助咱们解决问题。
为何会出现上述问题呢?咱们看看iOS 11有些什么新增改动。
这里只列出部分以及跟今天主题相关的部分。详情能够看看官网what's new in iOS 11安全

UIViewController 与 UIView
主要变化部分:
  • UIViewController废弃LayoutGuide。iOS7以后,为了辅助Autolayout布局系统,Apple新增UILayoutSupport协议。就是被不少人忽略的topLayoutGuidebottomLayoutGuide
@interface UIViewController (UILayoutSupport)
// These objects may be used as layout items in the NSLayoutConstraint API
@property(nonatomic,readonly,strong) id<UILayoutSupport> topLayoutGuide API_DEPRECATED_WITH_REPLACEMENT("-[UIView safeAreaLayoutGuide]", ios(7.0,11.0), tvos(7.0,11.0));
@property(nonatomic,readonly,strong) id<UILayoutSupport> bottomLayoutGuide API_DEPRECATED_WITH_REPLACEMENT("-[UIView safeAreaLayoutGuide]", ios(7.0,11.0), tvos(7.0,11.0));
@property(nonatomic) UIEdgeInsets additionalSafeAreaInsets API_AVAILABLE(ios(11.0), tvos(11.0));复制代码

这个两个属性是readonly的,通常咱们也用不到,主要做用是辅助Controller的View在布局时知道从哪里开始布局,到什么地方结束布局。在使用SB或者XIB文件布局的时候,能够进行设置,肯定开始布局和结束布局的参考点。在iOS 11 已经标记API_DEPRECATED_WITH_REPLACEMENT。取而代之的是UIView新的APIsafeAreaInsets
bash

LayoutGuide
LayoutGuide

  • UIView增长safeAreaInsets:UIEdgeInsets安全区概念。用于替代辅助自动布局的LayoutGuide。安全区域定义了布局View除各类Bar后剩下的可见区域。此属性是readonly的,想要改动,须要操做UIViewControlleradditionalSafeAreaInsets属性。app

    简单理解就是去除系统的各类Bar以及左右margin(若是有设置过) 后的可用布局区域。

    图中淡蓝色区域即为安全区域:
    ide

    safeAreaInsets
    safeAreaInsets

    若是在View上的控件没有被遮挡,safeAreaInsets = {(0,0),(0,0)}分别对应于(top,left,bottom,right)
    假设子ViewA顶部被navigationBar遮挡20pt,相应safeAreaInsets = {(20,0),(0,0)}布局

    注意:若是采用SB、XIB布局UI的,safeAreaInsets最低支持版本为iOS 9.0。考虑到兼容性,触手TV是不能使用安全区域的。
  • UIViewController废弃automaticallyAdjustsScrollViewInsetsAPI。这是个bool值属性,描述了是否自动适配scrollView的contentInsets。值为true时,scrollView的内容不会被系统可见的各类bar所遮挡(statusbarnavigationBartabBartoolBar),通常会引发tableViewcollectionViewscrollView的content下移bar的高度值个像素。若是不须要自动调整,将值设为falsepost

  • UIViewController新增修改关联View安全区域的APIadditionalSafeAreaInsets。可对安全区域的值进行增减,知足自定义UI的需求。并提供方法viewSafeAreaInsetsDidChange,用于在安全区域改变后,进行布局的调整。ui

  • 相应的,UIScrollView增长枚举属性contentInsetAdjustmentBehavior,描述scrollView如何调整contentInset(实际是调整adjustedContentInset属性),配合安全区域使用。最后UIscrollView的contentInset值为adjustedContentInsetsafeAreaInsets之和。atom

    有4个可选值:

    typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {
              UIScrollViewContentInsetAdjustmentAutomatic, //与 automaticallyAdjustsScrollViewInsets 相似
              UIScrollViewContentInsetAdjustmentScrollableAxes, // 在滚动的当前轴向上进行自动调整
              UIScrollViewContentInsetAdjustmentNever,  //不进行任何的调整
              UIScrollViewContentInsetAdjustmentAlways, // contentInset等于View的safeAreaInsets
          } API_AVAILABLE(ios(11.0),tvos(11.0));复制代码

    UIScrollViewDelegate新增scrollViewDidChangeAdjustedContentInset方法,当adjustedContentInset改变后通知使用者进行布局调整。

总结:

  • 若是使用SB、XIB布局时启用了安全区域,IB会参考安全区域进行布局。
  • 布局时没有以安全区域做为参照时,直接设置安全区域,系统并不会对布局作出自适应动做,能够经过安全区域改变回调方法进行调整。
  • 若是是UIscrollView或子类,若是设置了UIScrollViewContentInsetAdjustmentBehavior不等于UIScrollViewContentInsetAdjustmentNever,系统会自动适配UIScrollView的内容都在安全区域内,保证内容不被各类Bar遮住。
NavigationBar的改变

NavigationBar 多了个contentView,这个View在开启了大标题时,上面会有个titleLable。
NavigationBartitleView支持自动布局。须要使用者自动撑开添加到上面的View。

定位权限

在iOS11,原有的NSLocationAlwaysUsageDeion被降级为NSLocationWhenInUseUsageDeion
.plist没文件中配置NSLocationAlwaysAndWhenInUseUsageDeion,系统框才会弹出,使用requestAlwaysAuthorization获取权限。

其余变动
  1. 设置UIBarItem.landscapeImagePhone 与UIBarItem.largeContentSizeImage 来适配在竖屏和横屏下的BarItem图标和双击放大后的图标,若是使用 PDF 资源图,系统会自动从PDF资源图提取相应图标,就不用设置上述属性了。
  2. navigationBar.prefersLargeTitles = true 新增大标题属性,大标题displaymode控制显示枚举:navigationItem.largeTitleDisplayMode
    枚举属性:

    - automatic:自动保存上一次设置的值
     - always:老是显示大标题
     - never:不显示复制代码
  3. Navigation集成searchBar。navigationItem.searchController 属性赋值能够集成searchBar在navigationbar下方,navigationItem.hidesSearchBarWhenScrolling属性控制在滚动时是否自动隐藏。

  4. 确保避免 size 为 0 的自定义view,实现intrinsicContentSize 方法提供默认尺寸。

  5. tableview开启开启高度估算(Self-Sizing),设置下面三个属性,使高度估算失效:

    tableView.estimatedRowHeight = 0 
    
         tableView.estimatedSectionHeaderHeight = 0 
    
         tableView.estimatedSectionFooterHeight = 0复制代码
  6. tableview 新增左滑右滑交互。

  7. 废弃iOS7 之后的layoutMargins,取而代之的是新增的安全区域的概念。
    新增directionalLayoutMargins。对应layoutMargins。
    新增systemMinimumLayoutMargins,当directionalLayout小于systemMinimumLayoutMargins,使用systemMinimumLayoutMargins。
    新增 UIViewController=.viewRespectsSystemMinimumLayoutMargins,默认为FALSE。设为TRUE,可设置任意值。

新仇旧恨

明白了iOS 11大法,再来看看,这大法带来的各类问题。

NavigationBar问题。

通常使用NavigationBar基本有三种手法。

  1. 纯正血统,使用标注控件,不在NavigationBar上添加任何的控件。
  2. 混血,在NavigationBar上有定制的控件。
  3. 毫无血统,彻底自定义。隐藏了系统的navigationBar,直接用了View替代。

针对上面三种状况:

  • 使用第一种姿式的人,很幸运,你不须要进行适配。(估计不多有人不定制)

  • 使用第二种姿式的人,可能会有返回按钮、titleView、添加的控件position不正确的问题。

  • 使用第三种姿式的人,很好,在非iPhone X上,也没什么大问题。

UIscrollView、UItableView、UICollectionView内容下沉问题。

升级iOS 11后,发现有些使用UItableView布局的页面,顶部多出来了20pt或者44pt,也或者64pt。也就是顶部可见Bar的高度的总和。
包括使用MJRefresh引发的问题。也属于这类。

Xcode9 打出的包(iOS 11 SDK)页面卡顿问题

升级Xcode9 后,你会发现,基于iOS 11打出来的包,tableView滑动的时候,一卡一顿的。

请求定位框不弹出

在iOS 11有些应用在请求定位时,未能弹出系统请求权限的对话框。

返回按钮位置偏移问题

iOS 11的的leftBarButtonItem 或者右边都距离边距20像素。

其余问题

  1. 使用YYKit的大图预览控件YYPhotoGroupView,dismiss的时候,有些页面会抖一下。

一笑泯恩仇

既然已经知道了iOS 11初出江湖的各类套路和带来的血雨腥风。那就春风化雨,见招拆招了。

navigationBar 问题。

  • 由于NavigationBar引入了AutoLayout,以往的frame方式可能位置有误差。之前使用CGRectMakeZero自动撑大已经行不通。那么使用自动布局。或者实现下面View的方法,提供默认尺寸。
- (CGSize)intrinsicContentSize {
    return CGSizeMake(100,100);
}复制代码
  • 添加到NavigationBarView可能会出各类问题,尝试添加到contentView上。并设置约束。
  • 返回按钮问题,请设置好frame在赋值给navigationItem.leftBarButtonItem

UIscrollView、UItableView、UICollectionView内容下沉问题

由于controllerView废弃了automaticallyAdjustsScrollViewInsets,请使用UIScrollViewContentInsetAdjustmentNever来告诉系统,不要调整。或者设置additionalSafeAreaInsets来增长safeAreaInsets来抵消。

滚动卡顿问题

由于tableView默认开启Self-Sizing。设置下面三个属性,使高度估算失效:

tableView.estimatedRowHeight = 0 
    tableView.estimatedSectionHeaderHeight = 0 
    tableView.estimatedSectionFooterHeight = 0复制代码

请求定位框不弹出

在iOSi11,原有的NSLocationAlwaysUsageDeion被降级为NSLocationWhenInUseUsageDeion。所以,在原来项目中使用requestAlwaysAuthorization获取定位权限,而未在plist文件中配置NSLocationAlwaysAndWhenInUseUsageDeion,系统框不会弹出。建议新旧key值都在plist里配置。

页面跳动问题

参照内容下沉问题解决。

按钮偏移问题

iOS 11后,navigationBar新增了contentView来承载开发者添加的barButton。左右两边新增了20像素。如今很几种解决方案。若是只是想要调节返回按钮,能够直接使用系统的API:

@property(nullable,nonatomic,strong) UIImage *backIndicatorImage;
@property(nullable,nonatomic,strong) UIImage *backIndicatorTransitionMaskImage;复制代码

可是这样的处理方式,在有多个按钮的使用情景下就引发问题。20个像素仍是存在的。
还有调整UIButtonimageEdgeInsets的,其实也会在多按钮的时候出现布局问题。
好比这篇帖子
也有的在pushpop的时候进行设置的。修改约束会引发有些约束丢失。也有重写drawRect的

其实不须要这么麻烦。新建一个类,继承自UINavigationBar,而后重写layoutSubviews,若是是ios11,设置contentViewlayoutMargins为须要的值,以前的版本就执行super
核心代码以下:

@interface CustomNavigationBar:UINavigationBar
@end


const CGFloat LeftFiexSpace = 0;
const CGFloat RightFiexSpace = 8.0;

@implementation CustomNavigationBar
- (void)layoutSubviews {
    [super layoutSubviews];
    // 修正 ios 11 左右两边的边距
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"11.0")) {
        self.layoutMargins = UIEdgeInsetsZero;
        for (UIView *subview in self.subviews) {
            if ([NSStringFromClass(subview.class) containsString:@"ContentView"]) {
                subview.layoutMargins = UIEdgeInsetsMake(0, LeftFiexSpace, 0, RightFiexSpace);
                [self layoutIfNeeded];
            }
        }
    }
}
@end复制代码

在生成NavigationController的地方使用KVC将原来的NavigationBar替换成本身的.

UINavigationController *nvc = [super initWithRootViewController:rootViewController];    
    CSNavigationBar *naviBar = [[CustomNavigationBar alloc] init];
    [nvc setValue:naviBar forKey:@"navigationBar"];复制代码

最后

若是有写得不对的欢迎指正,有更高好的解决方法,也欢迎交流。

参考文章

苹果官网适配视频教程

如何设置返回按钮

你可能须要为你的APP适配iOS11此篇基本上是官方视频的文字版

相关文章
相关标签/搜索