IOS11新特性与兼容适配
IOS11发布以来,很多新的特性为开发工作提供了方便,我在此给大家介绍一下IOS11的新特性以及在兼容适配等做的工作。
1. UIVie变化
1.1. 更加方便的RTL边距设置
在之前的系统中我们会使用layoutMargins来获取和设置控件显示内容部分的边缘与控件边缘的距离。在iOS 11中,新增directionalLayoutMargins属性来指定边距。这两个属性的结构定义如下
typedef struct UIEdgeInsets {
CGFloat , left, bottom, right;
} UIEdgeInsets;
////
typedef struct NSDirectionalEdgeInsets {
CGFloat , leading, bottom, trailing;
} NSDirectionalEdgeInsets
从结构上看主要是将UIEdgeInsets结构的left和right调整为NSDirectionalEdgeInsets结构的leading和trailing。这一调整主要是为了Right To Left(RTL)语言下可以进行自动适配,例如要实现文本每行尾部边距设置为30px,在以前做法则需要判断语言来区分哪些是RTL语言,然后再做设置,如
if ([UIVie userInterfaceLayoutDirectionForSemanticContentAttribute:self.vie.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft)
{
// Right to left 语言下每行尾部在左边
self.vie.layoutMargins.left = 30;
}
else
{
self.vie.layoutMargins.right = 30;
}
iOS 11 后则可以一步到位,如
self.vie.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(0, 0, 0, 30);
注测试时需要添加RTL本地化语言才能看到效果
1.2. 安全区域
在iOS 11中新增了安全区域的概念,目的是告诉开发者在这个区域下绘制的内容的显示才是有效的,否则会存在被遮挡的情况(特别是iPhoneX那帅气的刘海)。在UIVie中新增safeAreaLayoutGuide和safeAreaInsets来获取屏幕的安全区域(对于frame布局时是很有用的)。如图所示
SafeArea示意图
举个例子,在一个空白的UIVieController中,分别在vieDidLoad和vieDidAppear方法中输出vie.safeAreaInsets观察边距情况,代码如下
- (void)vieDidLoad
{
[super vieDidLoad];
NSString edgeStr = NSStringFromUIEdgeInsets(self.vie.safeAreaInsets);
NSString layoutFrmStr = NSStringFromCGRect(self.vie.safeAreaLayoutGuide.layoutframe);
NSLog(@"vieDidLoad safeAreaInsets = %@, layoutframe = %@", edgeStr, layoutFrmStr);=
}
- (void)vieDidAppear:(BOOL)animated
{
[super vieDidAppear:animated];
NSString edgeStr = NSStringFromUIEdgeInsets(self.vie.safeAreaInsets);
NSString layoutFrmStr = NSStringFromCGRect(self.vie.safeAreaLayoutGuide.layoutframe);
NSLog(@"vieDidAppear safeAreaInsets = %@, layoutframe = %@", edgeStr, layoutFrmStr);
}
可以看到其输出为
2017-09-19 14:45:50.246095+0800 Sample[5608:1365070] vieDidLoad safeAreaInsets = {0, 0, 0, 0}, layoutframe = {{0, 0}, {375, 667}}
2017-09-19 14:45:50.257807+0800 Sample[5608:1365070] vieDidAppear safeAreaInsets = {20, 0, 0, 0}, layoutframe = {{0, 20}, {375, 603}}
可见,在视图显示完成的时候Vie的顶部边距变为了20px,而这20px正是状态栏的高度。同样原理,如果你的是一个
UINavigationController那在显示的时候vie.safeAreaInsets就会变成{64, 0, 0, 0}。注意在该VC下所有的UIVie及其子类获取到safeAreaInsets的值是相同的。
如果你想准确地知道安全区域是什么时候被改变的,可以重写UIVie的safeAreaInsetsDidChange方法,在这个方法里面可以监听安全区域的边距调整的事件(如果使用的是UIVieController,其也提供相应方法来实现监听,下一章节会讲述该部分内容),代码如下
- (void)safeAreaInsetsDidChange
{
//写入变更安全区域后的代码...
}
如果你不想让safeAreaInsets影响你的视图布局,则可以将insetsLayoutMarginsFromSafeArea设置为NO,所有的视图布局将会忽略safeAreaInsets这个属性了。要注意的是,insetsLayoutMarginsFromSafeArea仅用于AutoLayout,即使该属性为NO,视图的safeAreaInsets还是一样有值,而且安全区域变更方法safeAreaInsetsDidChange一样被调用。
2. UIVieController变化
2.1. 废除API
2.1.1. automaticallyAdjustsScrollVieInsets方法
iOS 7中使用该方法来自动调整UIScrollVie的contentInset。在iOS 11之后将会使用UIScrollVie的
contentInsetAdjustmentBehavior属性来代替该方法。
2.1.2. LayoutGuide和bottomLayoutGuide属性
iOS 7中使用这两个属性来指导带有导航栏(NaviagtionBar)和页签栏(TabBar)的视图排版。其作用如下图所示
LayoutGuide & bottomLayoutGuide
在iOS 11之后将使用安全区域(Safe Area)来代替该部分功能的实现。
2.2. 排版
2.2.1. additionalSafeAreaInsets属性
iOS 11加入安全区域后,对于VC则可以通过该属性来对该区域附加一个边距信息。如
self.additionalSafeAreaInsets = UIEdgeInsetsMake(30, 0, 0, 30);
注意这里是附加边距,意思就是在原有的safeAreaInsets值中增加对应的边距值。如果原来的是{10, 0, 0, 10}, 则得出的边距是{40, 0, 0, 40}。
2.2.2. systemMinimumLayoutMargins和vieRespectsSystemMinimumLayoutMargins属性
该属性表示了一个系统最小的边距信息,所有的视图排版都应该遵循这个边距信息的。除非将vieRespectsSystemMinimumLayoutMargins设置为NO。
2.2.3. vieLayoutMarginsDidChange方法
根视图的边距变更时会触发该方法的回调。可以通过该方法来处理当边距改变时子视图的布局。
2.2.4. vieSafeAreaInsetsDidChange方法
当视图的安全区域发生变更时会触发该方法的回调。可以通过该方法来处理安全区域变更时的子视图布局。
3. UINavigationBar变化
iOS 11中加入了大标题模式,其显示效果如下所示
大标题效果图
实现该效果需要将导航栏的prefersLargeTitles设置为YES,如
self.navigationController.navigationBar.prefersLargeTitles = YES;
4. UINavigationItem变化
4.1 控制大标题的显示
如果你想控制每个视图的大标题是否显示,这需要使用UINavigationItem的largeTitleDisplayMode属性来控制大标题的显示。该属性为枚举类型,定义如下
typedef NS_ENUM(NSInteger, UINavigationItemLargeTitleDisplayMode)
{
/// 自动模式,会继承前一个NavigationItem所设置的模式
UINavigationItemLargeTitleDisplayModeAutomatic,
/// 当前 Navigationitem 总是启用大标题模式
UINavigationItemLargeTitleDisplayModeAlays,
/// 当前 Navigationitem 总是禁用大标题模式
UINavigationItemLargeTitleDisplayModeNever,
}
根据上面的描述,可以在VC初始化init或者aakeFromNib方法中设置显示图标模式
self.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeAlays;
4.2 控制搜索控制器
iOS 11 中新增了两个属性searchController和hidesSearchBarWhenScrolling。这两个属性主要用于简化VC对UISearchController的集成以及视觉优化。其中searchController属性用于指定当前VC的一个搜索控制器。而hidesSearchBarWhenScrolling属性则用于控制当视图滚动时是否隐藏搜索栏的UI,当该值为YES时,搜索栏只有在内容视图(UIScrollVie及其子类)顶部是才会显示,在滚动过程中会隐藏起来;当该值为NO时,则不受滚动影响一直显示在导航栏中。具体的代码实现如下
- (void)aakeFromNib
{
[super aakeFromNib];
//设置SearchController到navigationItem
self.searchController = [[UISearchController alloc] initWithSearchResultsController:self];
self.navigationItem.searchController = self.searchController;
self.navigationItem.hidesSearchBarWhenScrolling = YES;
}
搜索栏隐藏后效果
5. UIScrollVie变化
之前的系统中,如果你的滚动视图包含在一个导航控制器下,系统会自动地调整你的滚动视图的contentInset。而iOS 11新增adjustedContentInset属性取替之前contentInset的处理方式。这两者之间的关系如下图所示
adjustedContentInset & contentInset
通过一个例子来验证这说法,代码如下
- (void)vieDidLoad
{
[super vieDidLoad];
NSLog(@"vieDidLoad");
NSLog(@"self.tableVie.contentInset = %@", NSStringFromUIEdgeInsets(self.tableVie.contentInset));
NSLog(@"self.tableVie.adjustedContentInset = %@", NSStringFromUIEdgeInsets(self.tableVie.adjustedContentInset));
}
- (void)vieDidAppear:(BOOL)animated
{
[super vieDidAppear:animated];
NSLog(@"vieDidAppear");
NSLog(@"self.tableVie.contentInset = %@", NSStringFromUIEdgeInsets(self.tableVie.contentInset));
NSLog(@"self.tableVie.adjustedContentInset = %@", NSStringFromUIEdgeInsets(self.tableVie.adjustedContentInset));
}
执行后输出下面信息
2017-09-20 11:54:09.361348+0800 Sample[1276:375286] vieDidLoad
2017-09-20 11:54:09.361432+0800 Sample[1276:375286] self.tableVie.contentInset = {0, 0, 0, 0}
2017-09-20 11:54:09.361462+0800 Sample[1276:375286] self.tableVie.adjustedContentInset = {0, 0, 0, 0}
2017-09-20 11:54:09.420000+0800 Sample[1276:375286] vieDidAppear
2017-09-20 11:54:09.420378+0800 Sample[1276:375286] self.tableVie.contentInset = {0, 0, 0, 0}
2017-09-20 11:54:09.420554+0800 Sample[1276:375286] self.tableVie.adjustedContentInset = {20, 0, 0, 0}
可见,tableVie的adjustedContentInset自动改变了,contentInset的值是保持不变的。注一定要是VC的根视图为UIScrollVie或者其子类才能够得到adjustedContentInset的值,否则获取到的是空值。而且非根视图的滚动视图就会被安全区域所裁剪,看到的样式如下图所示
样式效果对比
通过使用contentInsetAdjustmentBehavior属性可以控制 adjustedContentInset的变化。该属性为枚举类型,其定义如下
typedef NS_ENUM(NSInteger, UIScrollVieContentInsetAdjustmentBehavior) {
UIScrollVieContentInsetAdjustmentAutomatic,
UIScrollVieContentInsetAdjustmentScrollableAxes,
UIScrollVieContentInsetAdjustmentNever,
UIScrollVieContentInsetAdjustmentAlays,
}
其中UIScrollVieContentInsetAdjustmentAutomatic与UIScrollVieContentInsetAdjustmentScrollableAxes一样,ScrollVie会自动计算和适应顶部和底部的内边距并且在scrollVie 不可滚动时,也会设置内边距;
UIScrollVieContentInsetAdjustmentNever表示不计算内边距;UIScrollVieContentInsetAdjustmentAlays则根据视图的安全区域来计算内边距。
如果需要感知adjustedContentInset的变化,然后根据变化进行不同操作则可以通过重写新增的adjustedContentInsetDidChange方法或者实现UIScrollVieDelegate中的scrollVieDidChangeAdjustedContentInset方法来实现。如
//重写方法
- (void)adjustedContentInsetDidChange
{
[super adjustedContentInsetDidChange];
//执行操作...
}
//实现委托
- (void)scrollVieDidChangeAdjustedContentInset:(UIScrollVie )scrollVie
{
//执行操作...
}
除了新增上述所说的边距相关属性外,还新增了contentLayoutGuide和frameLayoutGuide属性,用于描述内容布局和整体布局信息。
6. UI主线程操作日志提醒
之前的系统中如果你不小心将UI放入非主线程操作时,Debug日志是没有任何信息反馈的,导致有时候在排错时非常困难。在新的Xcode 9中,如果你处于调试状态,将UI放入非主线程操作,如
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.tv = [[UITableVie alloc] initWithframe:self.vie.bounds];
[self.vie addSubvie:self.tv];
NSLog(@"self.tv.adjustedContentInset = %@", NSStringFromUIEdgeInsets(self.tv.adjustedContentInset));
});
Log中会出现下面提示
================================================================= Main Thread Checker: UI API called on a background thread: -[UIVie bounds] PID: 16919, TID: 2972321, Thread name: (none), Queue name: .apple.root.default-qos, QoS: 21 Backtrace: 4 Sample 0x00000001004885dc __29-[VieController vieDidLoad]_block_invoke + 112 5 libdispatch.dylib 0x000000010077149c _dispatch_call_block_and_release + 24 6 libdispatch.dylib 0x000000010077145c _dispatch_client_callout + 16 7 libdispatch.dylib 0x000000010077d56c _dispatch_queue_override_invoke + 980 8 libdispatch.dylib 0x0000000100782b54 _dispatch_root_queue_drain + 616 9 libdispatch.dylib 0x0000000100782880 _dispatch_orker_thread3 + 136 10 libsystem_pthread.dylib 0x000000018300b130 _pthread_qthread + 1268 11 libsystem_pthread.dylib 0x000000018300ac30 start_qthread + 4
从日志中了解到一个Main Thread Checker的东西,根据苹果官方文档来看他是作用在AppKit(OSX中)、UIKit还有一些相关API上的后台线程,主要是用来监控这些框架中的接口是否在主线程中进行调用,如果没有则发出警告日志。,利用这个功能可以让我们快速地定位那些地方存在问题。
空调维修
- 我的世界电脑版运行身份怎么弄出来(我的世界
- 空调抽湿是什么意思,设置抽湿的温度有什么意
- 方太燃气灶有一个打不着火 怎么修复与排查方法
- 夏季免费清洗汽车空调的宣传口号
- 清洗完空调后出现漏水现象
- iphone6能玩什么游戏(iphone6游戏)
- 如何设置电脑密码锁屏(如何设置电脑密码锁屏
- win10删除开机密码提示不符合密码策略要求
- 电脑w7显示不是正版(w7不是正版怎么解决)
- 万家乐z8热水器显示e7解决 怎么修复与排查方法
- 1匹空调多少瓦数(1匹空调多少瓦)
- 安卓手机连接电脑用什么软件好(关于安卓手机
- 电脑网页看视频卡是什么原因(爱拍看视频卡)
- 华帝燃气灶点火器一直响然后熄火怎么办:问题
- 电脑壁纸怎么换(关于电脑壁纸怎么换的介绍)
- 冬天空调的出风口应该朝什么方向(冬天空调风