基于iOS 11向下适配之总结

  这篇文章总结一下项目中遇到的基于iOS 11向下适配时遇到的问题,方便以后查阅。

场景一 (20180425)

创建一个带XIB的UIViewController子类。

XIB中选中Simulated Metrics中的Top Bar为Translucent Navigation Bar,即告知XIB本页面含有导航栏,如图:

Translucent Navigation Bar

XIB中拖入一个View,设置背景色,设置约束为:宽高各60pt,水平居中,距离Safe Area Top 20pt。如下图:

布局

在 iPhone X 以及其他 iOS 11 机型上运行,显示为预期效果:

iPhone X效果

在 iOS 9/10 机型上运行显示效果“超出预期”:

超出预期

解决

给该view添加一个距离Super View Top(即视图控制器的self.view顶部)大于/等于预期间距(20pt)的约束,并设置距离Safe Area Top的约束优先级为750(低于1000)。如图:

添加约束

在 iOS 9/10 机型运行即可看到预期效果。

分析

优先满足Super View Top约束(优先级1000),其次满足Safe Area Top约束(优先级750)。

当在没有 Safe Area 的机型(iOS 9/10)上运行时,只需要满足Super View Top约束,view距离self.view的top等于84(64 + 20),view在导航栏下面20pt正常显示。

当在有 Safe Area的机型(iOS 11)上运行时,先满足 Super View Top约束,view距离self.view的top大于等于84,再满足距离Safe Area Top 20pt的约束,后者告诉前者应该“大于84”多少(有导航条时,Safe Area的Y坐标零点从导航栏下开始)。

场景二 (20180425更新)

使用XIB创建带 Translucent Navigation Bar 的 ViewController,导航栏的 BarButtonItem 要用代码手动创建。在 iOS 11上,创建一个CustomView的rightBarButtonItem可以这样写:

1
2
3
UIButton *commitButton = [[UIButton alloc] init];
[commitButton setTitle:@"完成" forState:UIControlStateNormal];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:commitButton];

在 iOS 11机型上运行,一切正常,但是在 iOS 9/10机型上运行,该BarButtonItem则不会显示。

解决

这里只需要给commitButton设置Frame即可适配 iOS 9/10/11。如下

1
2
3
UIButton *commitButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
[commitButton setTitle:@"完成" forState:UIControlStateNormal];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:commitButton];

分析

原因是 iOS 11的导航条自带AutoLayout,会把未设置Frame的commitButton自动布局。而iOS 9/10的导航条则不自带AutoLayout,Button初始化之后需要设置frame。

场景三 (20180427更新)

ViewController中有已知的三个TableView横向并排排列,并且可以滑动切换。这时,就需要一个ScrollView来做底,UI结构如下图,省略添加约束的过程。

场景三

我们知道,IB中的 ScrollView 需要设置 ContentSize 来“撑开”内容,否则 ContentSize 默认为(0, 0)。iOS 9/10 可以在 - (void)viewDidLayoutSubviews 中设置 ScrollView 的 ContentSize,本场景如下:

1
2
3
4
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.backScrollView.contentSize = CGSizeMake(self.view.frame.size.width * 3, self.view.frame.size.height);
}

但是,iOS 11 中这样设置过 ScrollView 的 ContentSize 之后,ContentSize 仍是(0, 0)。

解决

把设置 ContentSize 的代码写在 - (void)viewDidAppear:(BOOL)animated 中:

1
2
3
4
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.backScrollView.contentSize = CGSizeMake(self.backScrollView.frame.size.width * 3, self.backScrollView.frame.size.height);
}

分析

具体原理未知。

但是经测试发现,在本场景中,- (void)viewWillLayoutSubviews- (void)viewDidLayoutSubviews在 iOS 9/10 中会执行两遍,在 iOS 11 中只会执行一遍,不清楚 Apple 在这之中做了什么修改。

基于iOS 11向下适配之总结

https://oxo.red/summary_of_iOS_11_adaption/

作者

Han

发布于

2018-04-25

更新于

2021-08-20

许可协议

评论