适配 iPhone X 的相关内容详见我的另一篇文章关于 SafeArea 和 适配 iPhoneX
前言
又是一波震撼人心的苹果秋季新产品发布会,同时也放出了 OS 的 GM 版,相信很多果粉已经按捺不住内心已经开始了升级之旅。当然,从 iOS 11 beta1 就已经尝鲜的朋友也不在少数。所以,同时也意味着 iOS 开发者的 iOS11 的适配工作已经刻不容缓了。
1、适配导航栏(UINavigationBar)
导航栏遇到问题
别家 App 早就已经适配了 iOS11,可是原谅我有其他的事情,而且大致在 iOS11 上运行了一下 App,没有发现什么问题,所以适配工作一直在耽搁,但是近些天更新了 Xcode9 在新的编译器上编译运行了一下 App,WTF,一眼就看到了下面的丑爆的导航栏,什么鬼?然后在iOS11以下
的设备上运行,完全正常。
我们的 App 的导航栏不是用的UINavigationController
的navigationBar
,而是每个VC
都有一个自己的navigationBar
,这样的话,导航栏的自由度高,而且个人感觉系统原生的导航栏的切换效果不好(当然,iOS11 导航栏的大标题切换特效还是蛮可以的,可以参考音乐、备忘录等系统应用查看效果,也可以自己添加设置UINavigationBar
的属性setPrefersLargeTitles
来实现大标题)。
iOS11 的导航栏:
之前正常的导航栏见下面的 Nav_Normal 截图
分析问题
好的,那就让我们一探究竟,到底是怎么回事。
经过 DebugView,在下图看出了端倪。
原来 iOS11 的
UINavigationBar
有两个子视图,分别是UIBarBackground
和UINavigationBarContentView
,我们重点放在后者身上,因为BarButtonItem
和TitleLabel
都被添加到他上面,可以仔细看一下上面这张截图,可以很清楚看出各自之间的关系。
知道了图层之间的关系,那为什么我们的导航栏会在 iOS11 上产生出如上的元素错乱的问题呢?仔细观察会发现,貌似是往上平移了将近 20 px,我们接着看一下布局,见下图
果然
UINavigationBarContentView
的midY坐标是22,而这个 contentView 的高度是 44px,所以到这里已经找到了原因,原来是直接忽略状态栏的 20px 直接贴在了屏幕顶部,所以布局不错乱才怪。
解决方案
找到了原因,我们距离成功就不远了,创建一个UINavigationBar
的子类,重写layoutSubviews
,重新布局导航栏的子视图,如下
- (void)layoutSubviews {
[super layoutSubviews];
self.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), 64);
for (UIView *view in self.subviews) {
if([NSStringFromClass([view class]) containsString:@"Background"]) {
view.frame = self.bounds;
}
else if ([NSStringFromClass([view class]) containsString:@"ContentView"]) {
CGRect frame = view.frame;
frame.origin.y = 20;
frame.size.height = self.bounds.size.height - frame.origin.y;
view.frame = frame;
}
}
}
编译运行一下,完美解决。
Debug一下,布局已经正确了
导航栏适配总结
也有一些童鞋也到了titleView
布局错乱问题,iOS10 及以下自定义titleView
会添加在navigationBar
上,而 iOS11 添加在UINavigationBarContentView
上,之前的UINavigationItemView
在 iOS11 替换成了UINavigationBarContentView
。使用上面的解决思路应该很快就能解决。
为了实现 iOS11 新系统的大标题新UI效果,苹果部分重构了UINavigationBar
的代码及元素布局逻辑,从 iOS7 到 iOS10 阶段内导航栏都没有大的变动,而这个
iOS11 带来的变动或多或少会影响到我们的 App 导航栏的布局效果。而且在[Apple Developer Forums]也有相关的讨论,像sizeThatFits not working,像There has a problem with UINavigationbar(maybe is bug),可能在这部分代码苹果还会相继修改完善。但不管怎么说,现阶段还是应该找一套可行的适配方案来应对导航栏变化带来的影响。
2、适配 UITableViewController(UIScrollView)
ScrollView 自动适配 Insets 的方式变化automaticallyAdjustsScrollViewInsets
与contentInsetAdjustmentBehavior
-
automaticallyAdjustsScrollViewInsets
是控制器ViewController
的属性,默认是true
,一般来说如果ScrollView
直接添加在控制器视图上时,会自动设置ScrollView
的Inset
属性来空出status bar, search bar, navigation bar, toolbar, or tab bar
的位置来。在日常开发中为了程序的稳定性与自由度一般我们是将这个属性设置为false
。但是,这个属性在iOS11
失效了,所以导致了我们 App 的下面的问题。
问题很好解决,在文档中标注很明白
@property(nonatomic,assign) BOOL automaticallyAdjustsScrollViewInsets API_DEPRECATED_WITH_REPLACEMENT("Use UIScrollView's contentInsetAdjustmentBehavior instead", ios(7.0,11.0),tvos(7.0,11.0)); // Defaults to YES
-
contentInsetAdjustmentBehavior
对于我们现在来说是个陌生面孔。这是在 iOS11 为ScrollView
新定义的一个枚举属性。注意,上面谈到的automaticallyAdjustsScrollViewInsets
是控制器的属性。
typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {
UIScrollViewContentInsetAdjustmentAutomatic, // Similar to .scrollableAxes, but for backward compatibility will also adjust the top & bottom contentInset when the scroll view is owned by a view controller with automaticallyAdjustsScrollViewInsets = YES inside a navigation controller, regardless of whether the scroll view is scrollable
UIScrollViewContentInsetAdjustmentScrollableAxes, // Edges for scrollable axes are adjusted (i.e., contentSize.width/height > frame.size.width/height or alwaysBounceHorizontal/Vertical = YES)
UIScrollViewContentInsetAdjustmentNever, // contentInset is not adjusted
UIScrollViewContentInsetAdjustmentAlways, // contentInset is always adjusted by the scroll view's safeAreaInsets
} API_AVAILABLE(ios(11.0),tvos(11.0));
所以我们将这个属性设置为.AdjustmentNever
即可解决Insets
异常问题。
if (@available(iOS 11.0, *)) {
aboutMeTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
如下图
- 每个需要修改的地方都写令人讨厌的
ifelse
代码有些不爽,为了方面,我写了一个宏定义,如下,设置起来就简单过了
MCDisbaleAutoAdjustScrollViewInsets(_tableView, self)
/**
取消自动适配 ScrollView 的 Insets 行为
@param scrollView 滑动视图
@param vc 所在控制器
*/
#define MCDisbaleAutoAdjustScrollViewInsets(scrollView, vc)\
do { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \
if (@available(iOS 11.0,*)) {\
scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;\
} else {\
vc.automaticallyAdjustsScrollViewInsets = NO;\
}\
_Pragma("clang diagnostic pop")\
} while (0);
预估高度estimatedXXHeight
iOS11 中的estimatedXXHeight
由默认的 0 变成了现在的默认.AutomaticDimension
,导致高度计算出错,最后导致的现象就是上拉加载更多的时候 UI 错乱、TableView
视图的高度异常等一系列问题。重新置 0 即可。
_tableView.estimatedRowHeight = 0;
_tableView.estimatedSectionFooterHeight = 0;
_tableView.estimatedSectionHeaderHeight = 0;
3、其他
-
info.plist
新添Privacy - Photo Library Additions Usage Description
键,跟Privacy - Photo Library Usage Description
的不同之处在于,前者允许你只写入图库,不需要读取。这样使得隐私权限更加细化。对这个键官方描述如下:Although this keys governs read and write access to the user’s photo library, it’s best to use NSPhotoLibraryAddUsageDescription if your app needs only to add assets to the library and does not need to read any assets.