iOS11、iPhone X、Xcode9 适配
更新iOS11后,发现有些地方需要做适配,整理后按照优先级分为以下三类:
1.单纯升级iOS11后造成的变化;
2.Xcode9 打包后造成的变化;
3.iPhoneX的适配
一、单纯升级iOS11后造成的变化
1. 升级后,发现某个拥有tableView的界面错乱,组间距和contentInset错乱,因为iOS11中 UIViewController 的 automaticallyAdjustsScrollViewInsets 属性被废弃了,因此当tableView超出安全区域时,系统自动会调整SafeAreaInsets值,进而影响adjustedContentInset值
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 有些界面以下使用代理方法来设置,发现并没有生效- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;// 这样的原理是因为之前只是实现了高度的代理方法,却没有实现View的代理方法,iOS10及以前这么写是没问题的,iOS11开启了行高估算机制引起的bug,因此有以下几种解决方法:// 解决方法一:添加实现View的代理方法,只有实现下面两个方法,方法 (CGFloat)tableView: heightForFooterInSection: 才会生效- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { return nil;}- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { return nil;}// 解决方法二:直接使用tableView属性进行设置,修复该UI错乱self.tableView.sectionHeaderHeight = 0;self.tableView.sectionFooterHeight = 5;[_optionTableView setContentInset:UIEdgeInsetsMake(-35, 0, 0, 0)];// 解决方法三:添加以下代码关闭估算行高self.tableView.estimatedRowHeight = 0;self.tableView.estimatedSectionHeaderHeight = 0;self.tableView.estimatedSectionFooterHeight = 0; |
2. 如果使用了Masonry 进行布局,就要适配safeArea
|
1
2
3
4
5
|
if ([UIDevice currentDevice].systemVersion.floatValue >= 11.0) { make.edges.equalTo(self.view.safeAreaInsets);} else { make.edges.equalTo(self.view);} |
3. 对于IM的发送原图功能,iOS11启动全新的HEIC 格式的图片,iPhone7以上设备+iOS11排出的live照片是".heic"格式图片,同一张live格式的图片,iOS10发送就没问题(转成了jpg),iOS11就不行
微信的处理方式是一比一转化成 jpg 格式
QQ和钉钉的处理方式是直接压缩,即使是原图也压缩为非原图
最终采取的是微信的方案,使用以下代码转成jpg格式
|
1
2
3
|
// 0.83能保证压缩前后图片大小是一致的// 造成不一致的原因是图片的bitmap一个是8位的,一个是16位的imageData = UIImageJPEGRepresentation([UIImage imageWithData:imageData], 0.83); |
二、使用Xcode9 编译后发现的问题
1. 发现“fastSocket”第三方报错,具体原因是缺少C99的头文件,引入“#include”即可

2. 导航栏的新特性
原生的搜索栏样式发生改变

右边为iOS11样式,搜索区域高度变大,字体变大
查看 API 后发现,iOS11后将 searchController 赋值给了 NavigationItem,通过属性 hidesSearchBarWhenScrolling 可以控制搜索栏是否在滑动的时候进行隐藏和显示
|
1
2
3
4
|
// A view controller that will be shown inside of a navigation controller can assign a UISearchController to this property to display the search controller’s search bar in its containing navigation controller’s navigation bar.@property (nonatomic, retain, nullable) UISearchController *searchController API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos);// If this property is true (the default), the searchController’s search bar will hide as the user scrolls in the top view controller’s scroll view. If false, the search bar will remain visible and pinned underneath the navigation bar.@property (nonatomic) BOOL hidesSearchBarWhenScrolling API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos); |
另外,UINavigationBar 新增属性 BOOL值 prefersLargeTitles 来实现下面的效果,并可以通过 largeTitleTextAttributes 来设置大标题的文本样式。设置大标题之后,导航栏的高度就会由之前的64pt变成 96pt,如果项目中有直接写死的高度或者隐藏导航栏之类的操作,就需要适配一下
有个界面使用到了导航栏按钮相关的frame,也发生了UI错乱,查看UI层级关系后发现,iOS11以前是直接把按钮加到了UINavigationBar上面,而iOS11则是先将按钮加到了_UITAMICAdaptorView,再加到_UIButtonBarStackView、_UINavigationBarContentView,接着才是UINavigationBar。因此如果需要获取导航栏按钮 frame 或者 superView,这里需要专门做下适配

iOS10及以下版本导航栏按钮层级关系图

iOS11导航栏按钮层级关系图
三、iPhone X的适配
下载完Xcode9之后,第一件事自然是在 iPhone X(模拟器)上过把瘾,然后编译后就发现报错了
由于iPhone X的状态栏是和其他版本手机差异比较大的,因此api 变化也比较大
先后做了以下适配
适配点一:项目中使用状态栏中图标判断当前网络的具体状态

出错代码
|
1
|
打印的 Log 报出以下错误: Trapped uncaught exception 'NSUnknownKeyException', reason: '[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key foregroundView.' |

iPhone X

其他手机
使用 runtime 打印其所有属性,发现以下差异
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 测试代码#import NSMutableString *resultStr = [NSMutableString string];//获取指定类的Ivar列表及Ivar个数unsigned int count = 0;Ivar *member = class_copyIvarList([[application valueForKeyPath:@"_statusBar"] class], &count); for(int i = 0; i < count; i++){ Ivar var = member[i]; //获取Ivar的名称 const char *memberAddress = ivar_getName(var); //获取Ivar的类型 const char *memberType = ivar_getTypeEncoding(var); NSString *str = [NSString stringWithFormat:@"key = %s type = %s \n",memberAddress,memberType]; [resultStr appendString:str];}NSLog(@"%@", resultStr); |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
// 其他版本的手机key = _inProcessProvider type = @""key = _showsForeground type = Bkey = _backgroundView type = @"UIStatusBarBackgroundView"key = _doubleHeightLabel type = @"UILabel"key = _doubleHeightLabelContainer type = @"UIView"key = _currentDoubleHeightText type = @"NSString"key = _currentRawData type = {超长。。}key = _interruptedAnimationCompositeViews type = @"NSMutableArray"key = _newStyleBackgroundView type = @"UIStatusBarBackgroundView"key = _newStyleForegroundView type = @"UIStatusBarForegroundView"key = _slidingStatusBar type = @"UIStatusBar"key = _styleAttributes type = @"UIStatusBarStyleAttributes"key = _waitingOnCallbackAfterChangingStyleOverridesLocally type = Bkey = _suppressGlow type = Bkey = _translucentBackgroundAlpha type = dkey = _showOnlyCenterItems type = Bkey = _foregroundViewShouldIgnoreStatusBarDataDuringAnimation type = Bkey = _tintColor type = @"UIColor"key = _lastUsedBackgroundColor type = @"UIColor"key = _nextTintTransition type = @"UIStatusBarStyleAnimationParameters"key = _overrideHeight type = @"NSNumber"key = _disableRasterizationReasons type = @"NSMutableSet"key = _timeHidden type = Bkey = _statusBarWindow type = @"UIStatusBarWindow"// iPhone Xkey = _statusBar ; type = @"_UIStatusBar"// 因此可见iPhone X的状态栏是多嵌套了一层,多取一次即可,最终适配代码为:NSArray *children;// 不能用 [[self deviceVersion] isEqualToString:@"iPhone X"] 来判断,因为iPhone X 的模拟器不会返回 iPhone X if ([[application valueForKeyPath:@"_statusBar"] isKindOfClass:NSClassFromString(@"UIStatusBar_Modern")]) { children = [[[[application valueForKeyPath:@"_statusBar"] valueForKeyPath:@"_statusBar"] valueForKeyPath:@"foregroundView"] subviews]; } else { children = [[[application valueForKeyPath:@"_statusBar"] valueForKeyPath:@"foregroundView"] subviews]; } |
警告以上处理,代码看起来是不报错了,然而!!具体看了下代码发现并不生效!因为从iPhone X取出来之后只有view层级的信息,因此采用以下方法确定2G/3G/4G,从API上目测是有效的
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
NSArray *typeStrings2G = @[CTRadioAccessTechnologyEdge, CTRadioAccessTechnologyGPRS, CTRadioAccessTechnologyCDMA1x]; NSArray *typeStrings3G = @[CTRadioAccessTechnologyHSDPA, CTRadioAccessTechnologyWCDMA, CTRadioAccessTechnologyHSUPA, CTRadioAccessTechnologyCDMAEVDORev0, CTRadioAccessTechnologyCDMAEVDORevA, CTRadioAccessTechnologyCDMAEVDORevB, CTRadioAccessTechnologyeHRPD]; NSArray *typeStrings4G = @[CTRadioAccessTechnologyLTE]; // 该 API 在 iOS7 以上系统才有效 if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) { CTTelephonyNetworkInfo *teleInfo= [[CTTelephonyNetworkInfo alloc] init]; NSString *accessString = teleInfo.currentRadioAccessTechnology; if ([typeStrings4G containsObject:accessString]) { NSLog(@"4G网络"); } else if ([typeStrings3G containsObject:accessString]) { NSLog(@"3G网络"); } else if ([typeStrings2G containsObject:accessString]) { NSLog(@"2G网络"); } else { NSLog(@"未知网络"); } } else { NSLog(@"未知网络"); } |
适配点二:解决这个问题后项目跑起来发现,整个app界面上下各空出大概40pt的高度

经常从 Github 上下载项目把玩的老司机们都知道,有些老项目在模拟器上跑起来之后也会只有 iPhone 4(320480)的布局空间,造成这个的原因是启动图使用 Launch Images Source 设置的时候没有勾选并设置对应的图片(11252436),解决方法如下

但是即使按照上面的操作进行之后,会发现底部 UITabBar 依旧是高出一些高度,查看层级关系后发现,同样是由于安全区的原因,UITabBar 高度由49pt变成了83pt,因此这里也要对iPhone X 及其模拟器进行适配
适配点三:iPhone X 只有 faceID,没有touchID,如果in的应用有使用到 touchID 解锁的地方,这里要根据机型进行相应的适配
适配点四:之前有偷懒的直接使用20替代状态栏高度,这些坑都要通过重新获取状态栏高度
|
1
|
CGRectGetHeight([UIApplication sharedApplication].statusBarFrame) |
适配点五:然而iPhone X更大的坑是屏幕的适配
首先看下屏幕尺寸

这张图反映出不少信息:
iPhone X的宽度虽然和7是一样的,但是高度多出145pt
使用三倍图是重点,而且一般认为肉眼所能所能识别的最高的屏幕密度是300ppi,iPhone X已达到458ppi(查证发现三星galaxy系列的屏幕密度是522ppi)
在设计方面,苹果官方文档human-interface-guidelines有明确要求,下面结合图例进行说明:

展示出来的设计布局要求填满整个屏幕

填满的同时要注意控件不要被大圆角和传感器部分所遮挡

安全区域以外的部分不允许有任何与用户交互的控件
上面这张图内含信息略多
头部导航栏不予许进行用户交互的,意味着下面这两种情况 Apple 官方是不允许的
底部虚拟区是替代了传统home键,高度为34pt,通过上滑可呼起多任务管理,考虑到手势冲突,这部分也是不允许有任何可交互的控件,但是设计的背景图要覆盖到非安全区域
状态栏在非安全区域,文档中也提到,除非可以通过隐藏状态栏给用户带来额外的价值,否则最好把状态栏还给用户
不要让 界面中的元素 干扰底部的主屏幕指示器
重复使用现有图片时,注意长宽比差异。iPhone X 与常规 iPhone 的屏幕长宽比不同,因此,全屏的 4.7 寸屏图像在 iPhone X 上会出现裁切或适配宽度显示。所以,这部分的视图需要根据设备做出适配。

注意类似占位图的适配
适配点六:横屏适配
关于 safe area,使用 safeAreaLayoutGuide 和 safeAreaInset就能解决大部分问题,但是横屏下还可能会产生一些问题,需要额外适配

产生这个原因代码是:[headerView.contentView setBackgroundColor:[UIColor headerFooterColor]],这个写法看起来没错,但是只有在 iPhone X上有问题

通过设置该属性,内容视图嵌入到了safe area中,但是contentView没有
解决方法:设置backgroundView颜色 [headerView.backgroundView setBackgroundColor:[UIColor headerFooterColor]]
适配点七:设备信息
|
1
2
3
4
5
6
|
if ([deviceString isEqualToString:@"iPhone10,1"]) return @"国行(A1863)、日行(A1906)iPhone 8";if ([deviceString isEqualToString:@"iPhone10,4"]) return @"美版(Global/A1905)iPhone 8";if ([deviceString isEqualToString:@"iPhone10,2"]) return @"国行(A1864)、日行(A1898)iPhone 8 Plus";if ([deviceString isEqualToString:@"iPhone10,5"]) return @"美版(Global/A1897)iPhone 8 Plus";if ([deviceString isEqualToString:@"iPhone10,3"]) return @"国行(A1865)、日行(A1902)iPhone X";if ([deviceString isEqualToString:@"iPhone10,6"]) return @"美版(Global/A1901)iPhone X"; |
链接:http://www.jianshu.com/p/f5ee206c7df0
iOS11、iPhone X、Xcode9 适配的更多相关文章
- iOS之iOS11、iPhone X、Xcode9 适配指南
更新iOS11后,发现有些地方需要做适配,整理后按照优先级分为以下三类: 1.单纯升级iOS11后造成的变化: 2.Xcode9 打包后造成的变化: 3.iPhoneX的适配 一.单纯升级iOS11后 ...
- iOS11、iPhone X、Xcode9 适配指南
更新iOS11后,发现有些地方需要做适配,整理后按照优先级分为以下三类: 1.单纯升级iOS11后造成的变化: 2.Xcode9 打包后造成的变化: 3.iPhoneX的适配 一.单纯升级iOS11后 ...
- iOS11 & iPhone X 适配指南
苹果WWDC开发者大会上,终于发布了大家期待已久的iOS 11,有些新特性功能确实出人意料.不过大的方面苹果貌似也就 AR 和 GM 机器学习了,9月13日凌晨1点,苹果开了新品发布会,相信大家都已经 ...
- IOS学习——iphone X的适配
说实话,对于一个刚入门iOS两个月的新手而言,在拿到这个任务的时候整个人都是懵逼的,怎么做适配?哪些地方需要适配?该怎么做?一个个问题搞得头都大了. 首先,啥都不管,先在iPhone X上运行起来看看 ...
- iOS 11适配和iPhone X的适配
这两天对自己负责的项目进行iOS 11和iPhone X的适配,网上的博客很多,也看了很多别人的记录博客,这里把自己遇到的问题记录下,当然有些不仅仅是iOS 11和iPhone X的适配,还包括自己遇 ...
- iOS:界面适配--iPhone不同机型适配 6/6plus
iOS:界面适配--iPhone不同机型适配 6/6plus 机型变化 坐标:表示屏幕物理尺寸大小,坐标变大了,表示机器屏幕尺寸变大了: 像素:表示屏幕图片的大小,跟坐标之间有个对应关系 ...
- 针对Xcode 9 + iOS11 的修改,及iPhone X的适配
1,UIScrollView的automaticallyAdjustsScrollViewInsets 失效了. automaticallyAdjustsScrollViewInsets,当设置为YE ...
- iOS11及Xcode9适配问题汇总
UIScrollView and UITableView的新特性 ScrollView 如果有一些文本位于UI滚动视图的内部,并包含在导航控制器中,现在一般navigationContollers会传 ...
- [超级懒人最简单法]iPhone 6 plus 适配切图方法分享(转载文章)
网络上已经有很多适配教程,可是看了半天总是半懂不懂..最后还是要综合多个教程再动动脑子动动手,最好有程序大哥帮你试一下(这得有多大的福气) 如果有跟我一样情况的: 1. 有人说用sketc ...
随机推荐
- MVVM 模版里的控件怎样触发命令
public class BaseWindow : Window { public BaseWindow() { InitializeStyle(); //给样式的控件加载事件 this.Loaded ...
- Java IO编程全解(一)——Java的I/O演进之路
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7419117.html JDK1.4之前的早期版本,Java对I/O的支持并不完善,开发人员在开发高性能I/O ...
- WPF DataGrid自动生成序号
需求和效果 应用WPF技术进行开发的时候,大多都会遇到给DataGrid添加序号的问题,今天分享一下查阅了很多stackoverflow的文章后,总结和改进过来的方法,先看一下效果图,文末附Demo下 ...
- CSS之 z-index 属性
层叠上下文: 三维概念,表示元素在Z轴的位置 层叠可嵌套,组合成一个分层次上下文 每个层叠上下文和兄弟元素独立,进行层叠变化或渲染时,只考虑后代元素 每个层叠上下是自成体系的 层叠顺序 1 bac ...
- win10 uwp 设置启动窗口大小 获取窗口大小
本文主要说如何设置我们窗口的启动大小,UWP启动窗口大小. 设置启动窗口 设置窗口大小 ApplicationView.PreferredLaunchViewSize = new Size(1000, ...
- 带你领会 线性代数 微积分的本质 3blue1brown 动画效果帅出天际
前段时间在 哔哩哔哩 上偶然发现了 3blue1brown 精美的动画,配上生动的讲解,非常适合帮助建立数学的形象思维 其中两大系列,非常值得反复观看: 线性代数的本质(Essence of line ...
- Table 控件各元素及属性
功能:在Web页中创建通用表格里. 属性: 1.CellPadding属性:用于设置表中单元格的边框和内容之间的距离(以像素为单位).默认为-(未设置). 2.CellSpacing属性:用于设置表中 ...
- RabbitMQ 笔记-Exchanges
Procuder Publish的Message进入了Exchange.接着通过"routing keys", RabbitMQ会找到应该把这个Message放到哪个queue里. ...
- 【Spring】构建Spring Web应用
前言 学习了Spring的注解.AOP后,接着学习Spring Web,对于Web应用开发,Spring提供了Web框架. Web应用 Spring MVC初探 MVC为(Model-View-Con ...
- ubuntu16.04, Matlab2016b caffe编译安装
在Ubuntu上编译安装caffe还是个比较蛋疼的事,有时候会莫名其妙的碰到很多库的问题,这篇文章就把我在Ubuntu上编译安装caffe的过程和遇到的问题大致记录一下. 1.安装opencv htt ...