这是一次公司内部技术分享会的内容,内容共分为三个部分:html
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的同行案列,结合触手TV
APP实际问题,通过汇总,如下多是须要适配的点。找到问题的根源,才能帮助咱们解决问题。
为何会出现上述问题呢?咱们看看iOS 11有些什么新增改动。
这里只列出部分以及跟今天主题相关的部分。详情能够看看官网what's new in iOS 11。安全
UIViewController
废弃LayoutGuide
。iOS7以后,为了辅助Autolayout布局系统,Apple新增UILayoutSupport
协议。就是被不少人忽略的topLayoutGuide
,bottomLayoutGuide
。@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
UIView
增长safeAreaInsets:UIEdgeInsets
安全区概念。用于替代辅助自动布局的LayoutGuide
。安全区域定义了布局View除各类Bar后剩下的可见区域。此属性是readonly
的,想要改动,须要操做UIViewController
的additionalSafeAreaInsets
属性。app
margin(若是有设置过)
后的可用布局区域。 图中淡蓝色区域即为安全区域:
ide
若是在View上的控件没有被遮挡,safeAreaInsets = {(0,0),(0,0)}
分别对应于(top,left,bottom,right)
。
假设子ViewA顶部被navigationBar
遮挡20pt,相应safeAreaInsets = {(20,0),(0,0)}
。布局
safeAreaInsets
最低支持版本为iOS 9.0。考虑到兼容性,触手TV是不能使用安全区域的。 UIViewController
废弃automaticallyAdjustsScrollViewInsets
API。这是个bool值属性,描述了是否自动适配scrollView的contentInsets。值为true
时,scrollView的内容不会被系统可见的各类bar所遮挡(statusbar
、navigationBar
,tabBar
,toolBar
),通常会引发tableView
,collectionView
,scrollView
的content下移bar的高度值个像素。若是不须要自动调整,将值设为false
。post
UIViewController
新增修改关联View
安全区域的APIadditionalSafeAreaInsets
。可对安全区域的值进行增减,知足自定义UI的需求。并提供方法viewSafeAreaInsetsDidChange
,用于在安全区域改变后,进行布局的调整。ui
相应的,UIScrollView
增长枚举属性contentInsetAdjustmentBehavior
,描述scrollView
如何调整contentInset(实际是调整adjustedContentInset
属性),配合安全区域使用。最后UIscrollView
的contentInset值为adjustedContentInset
与safeAreaInsets
之和。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
改变后通知使用者进行布局调整。
总结:
UIscrollView
或子类,若是设置了UIScrollViewContentInsetAdjustmentBehavior
不等于UIScrollViewContentInsetAdjustmentNever
,系统会自动适配UIScrollView
的内容都在安全区域内,保证内容不被各类Bar遮住。NavigationBar
多了个contentView
,这个View在开启了大标题时,上面会有个titleLable。NavigationBar
的titleView
支持自动布局。须要使用者自动撑开添加到上面的View。
在iOS11,原有的NSLocationAlwaysUsageDeion
被降级为NSLocationWhenInUseUsageDeion
。
在.plist
没文件中配置NSLocationAlwaysAndWhenInUseUsageDeion
,系统框才会弹出,使用requestAlwaysAuthorization获取权限。
navigationBar.prefersLargeTitles = true 新增大标题属性,大标题displaymode控制显示枚举:navigationItem.largeTitleDisplayMode
枚举属性:
- automatic:自动保存上一次设置的值
- always:老是显示大标题
- never:不显示复制代码
Navigation集成searchBar。navigationItem.searchController 属性赋值能够集成searchBar在navigationbar下方,navigationItem.hidesSearchBarWhenScrolling属性控制在滚动时是否自动隐藏。
确保避免 size 为 0 的自定义view,实现intrinsicContentSize 方法提供默认尺寸。
tableview开启开启高度估算(Self-Sizing),设置下面三个属性,使高度估算失效:
tableView.estimatedRowHeight = 0
tableView.estimatedSectionHeaderHeight = 0
tableView.estimatedSectionFooterHeight = 0复制代码
tableview 新增左滑右滑交互。
废弃iOS7 之后的layoutMargins,取而代之的是新增的安全区域的概念。
新增directionalLayoutMargins。对应layoutMargins。
新增systemMinimumLayoutMargins,当directionalLayout小于systemMinimumLayoutMargins,使用systemMinimumLayoutMargins。
新增 UIViewController=.viewRespectsSystemMinimumLayoutMargins,默认为FALSE。设为TRUE,可设置任意值。
明白了iOS 11大法,再来看看,这大法带来的各类问题。
通常使用NavigationBar
基本有三种手法。
NavigationBar
上添加任何的控件。NavigationBar
上有定制的控件。navigationBar
,直接用了View替代。针对上面三种状况:
使用第一种姿式的人,很幸运,你不须要进行适配。(估计不多有人不定制)
使用第二种姿式的人,可能会有返回按钮、titleView、添加的控件position
不正确的问题。
使用第三种姿式的人,很好,在非iPhone X上,也没什么大问题。
升级iOS 11后,发现有些使用UItableView
布局的页面,顶部多出来了20pt或者44pt,也或者64pt。也就是顶部可见Bar的高度的总和。包括使用MJRefresh引发的问题。也属于这类。
升级Xcode9 后,你会发现,基于iOS 11打出来的包,tableView滑动的时候,一卡一顿的。
在iOS 11有些应用在请求定位时,未能弹出系统请求权限的对话框。
iOS 11的的leftBarButtonItem 或者右边都距离边距20像素。
YYKit
的大图预览控件YYPhotoGroupView
,dismiss的时候,有些页面会抖一下。既然已经知道了iOS 11初出江湖的各类套路和带来的血雨腥风。那就春风化雨,见招拆招了。
NavigationBar
引入了AutoLayout
,以往的frame
方式可能位置有误差。之前使用CGRectMakeZero
自动撑大已经行不通。那么使用自动布局。或者实现下面View
的方法,提供默认尺寸。- (CGSize)intrinsicContentSize {
return CGSizeMake(100,100);
}复制代码
NavigationBar
View可能会出各类问题,尝试添加到contentView
上。并设置约束。frame
在赋值给navigationItem.leftBarButtonItem
。由于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个像素仍是存在的。
还有调整UIButton
的imageEdgeInsets
的,其实也会在多按钮的时候出现布局问题。
好比这篇帖子。
也有的在push
和pop
的时候进行设置的。修改约束会引发有些约束丢失。也有重写drawRect的。
其实不须要这么麻烦。新建一个类,继承自UINavigationBar
,而后重写layoutSubviews
,若是是ios11,设置contentView
的layoutMargins
为须要的值,以前的版本就执行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此篇基本上是官方视频的文字版