IOS11发布以来,很多新的特性为开发工作提供了方便,小编在此给大家介绍一下IOS11的新特性以及在兼容适配等做的工作。

1. UIView变化

1.1. 更加方便的RTL边距设置

在之前的系统中我们会使用layoutMargins来获取和设置控件显示内容部分的边缘与控件边缘的距离。在iOS 11中,新增directionalLayoutMargins属性来指定边距。这两个属性的结构定义如下:

typedef struct UIEdgeInsets {
CGFloat top, left, bottom, right;
} UIEdgeInsets;
////
typedef struct NSDirectionalEdgeInsets {
CGFloat top, leading, bottom, trailing; 
} NSDirectionalEdgeInsets

从结构上看主要是将UIEdgeInsets结构的left和right调整为NSDirectionalEdgeInsets结构的leading和trailing。这一调整主要是为了Right To Left(RTL)语言下可以进行自动适配,例如:要实现文本每行尾部边距设置为30px,在以前做法则需要判断语言来区分哪些是RTL语言,然后再做设置,如:

if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.view.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft)
{
// Right to left 语言下每行尾部在左边
self.view.layoutMargins.left = 30;
}
else
{
self.view.layoutMargins.right = 30;
}

iOS 11 后则可以一步到位,如:

self.view.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(0, 0, 0, 30);

注:测试时需要添加RTL本地化语言才能看到效果

1.2. 安全区域

在iOS 11中新增了安全区域的概念,目的是告诉开发者在这个区域下绘制的内容的显示才是有效的,否则会存在被遮挡的情况(特别是iPhoneX那帅气的刘海)。在UIView中新增safeAreaLayoutGuide和safeAreaInsets来获取屏幕的安全区域(对于frame布局时是很有用的)。如图所示:

SafeArea示意图

举个例子,在一个空白的UIViewController中,分别在viewDidLoad和viewDidAppear方法中输出view.safeAreaInsets观察边距情况,代码如下:

- (void)viewDidLoad
{
[super viewDidLoad];

NSString *edgeStr = NSStringFromUIEdgeInsets(self.view.safeAreaInsets);
NSString *layoutFrmStr = NSStringFromCGRect(self.view.safeAreaLayoutGuide.layoutFrame);
NSLog(@"viewDidLoad safeAreaInsets = %@, layoutFrame = %@", edgeStr, layoutFrmStr);=
}

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];

NSString *edgeStr = NSStringFromUIEdgeInsets(self.view.safeAreaInsets);
NSString *layoutFrmStr = NSStringFromCGRect(self.view.safeAreaLayoutGuide.layoutFrame);
NSLog(@"viewDidAppear safeAreaInsets = %@, layoutFrame = %@", edgeStr, layoutFrmStr);
}

可以看到其输出为:

2017-09-19 14:45:50.246095 0800 Sample[5608:1365070] viewDidLoad safeAreaInsets = {0, 0, 0, 0}, layoutFrame = {{0, 0}, {375, 667}}
2017-09-19 14:45:50.257807 0800 Sample[5608:1365070] viewDidAppear safeAreaInsets = {20, 0, 0, 0}, layoutFrame = {{0, 20}, {375, 603}}

可见,在视图显示完成的时候View的顶部边距变为了20px,而这20px正是状态栏的高度。同样原理,如果你的是一个

UINavigationController那在显示的时候view.safeAreaInsets就会变成{64, 0, 0, 0}。注意:在该VC下所有的UIView及其子类获取到safeAreaInsets的值是相同的。

如果你想准确地知道安全区域是什么时候被改变的,可以重写UIView的safeAreaInsetsDidChange方法,在这个方法里面可以监听安全区域的边距调整的事件(如果使用的是UIViewController,其也提供相应方法来实现监听,下一章节会讲述该部分内容),代码如下:

- (void)safeAreaInsetsDidChange
{
//写入变更安全区域后的代码...
}

如果你不想让safeAreaInsets影响你的视图布局,则可以将insetsLayoutMarginsFromSafeArea设置为NO,所有的视图布局将会忽略safeAreaInsets这个属性了。要注意的是,insetsLayoutMarginsFromSafeArea仅用于AutoLayout,即使该属性为NO,视图的safeAreaInsets还是一样有值,而且安全区域变更方法safeAreaInsetsDidChange一样被调用。

2. UIViewController变化

2.1. 废除API

2.1.1. automaticallyAdjustsScrollViewInsets方法

iOS 7中使用该方法来自动调整UIScrollView的contentInset。在iOS 11之后将会使用UIScrollView的

contentInsetAdjustmentBehavior属性来代替该方法。

2.1.2. topLayoutGuide和bottomLayoutGuide属性

iOS 7中使用这两个属性来指导带有导航栏(NaviagtionBar)和页签栏(TabBar)的视图排版。其作用如下图所示

topLayoutGuide & 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和viewRespectsSystemMinimumLayoutMargins属性

该属性表示了一个系统最小的边距信息,所有的视图排版都应该遵循这个边距信息的。除非将viewRespectsSystemMinimumLayoutMargins设置为NO。

2.2.3. viewLayoutMarginsDidChange方法

根视图的边距变更时会触发该方法的回调。可以通过该方法来处理当边距改变时子视图的布局。

2.2.4. viewSafeAreaInsetsDidChange方法

当视图的安全区域发生变更时会触发该方法的回调。可以通过该方法来处理安全区域变更时的子视图布局。

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 总是启用大标题模式
UINavigationItemLargeTitleDisplayModeAlways,
/// 当前 Navigationitem 总是禁用大标题模式
UINavigationItemLargeTitleDisplayModeNever,
}

根据上面的描述,可以在VC初始化init或者awakeFromNib方法中设置显示图标模式:

self.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeAlways;

4.2 控制搜索控制器

iOS 11 中新增了两个属性searchController和hidesSearchBarWhenScrolling。这两个属性主要用于简化VC对UISearchController的集成以及视觉优化。其中searchController属性用于指定当前VC的一个搜索控制器。而hidesSearchBarWhenScrolling属性则用于控制当视图滚动时是否隐藏搜索栏的UI,当该值为YES时,搜索栏只有在内容视图(UIScrollView及其子类)顶部是才会显示,在滚动过程中会隐藏起来;当该值为NO时,则不受滚动影响一直显示在导航栏中。具体的代码实现如下:

- (void)awakeFromNib
{
[super awakeFromNib];
//设置SearchController到navigationItem
self.searchController = [[UISearchController alloc] initWithSearchResultsController:self];
self.navigationItem.searchController = self.searchController;
self.navigationItem.hidesSearchBarWhenScrolling = YES;
}

搜索栏隐藏后效果

5. UIScrollView变化

之前的系统中,如果你的滚动视图包含在一个导航控制器下,系统会自动地调整你的滚动视图的contentInset。而iOS 11新增adjustedContentInset属性取替之前contentInset的处理方式。这两者之间的关系如下图所示:

adjustedContentInset & contentInset

通过一个例子来验证这说法,代码如下:

- (void)viewDidLoad
{
[super viewDidLoad];

NSLog(@"viewDidLoad");
NSLog(@"self.tableView.contentInset = %@", NSStringFromUIEdgeInsets(self.tableView.contentInset));
NSLog(@"self.tableView.adjustedContentInset = %@", NSStringFromUIEdgeInsets(self.tableView.adjustedContentInset));
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];

NSLog(@"viewDidAppear");
NSLog(@"self.tableView.contentInset = %@", NSStringFromUIEdgeInsets(self.tableView.contentInset));
NSLog(@"self.tableView.adjustedContentInset = %@", NSStringFromUIEdgeInsets(self.tableView.adjustedContentInset));
}

执行后输出下面信息:

2017-09-20 11:54:09.361348 0800 Sample[1276:375286] viewDidLoad
2017-09-20 11:54:09.361432 0800 Sample[1276:375286] self.tableView.contentInset = {0, 0, 0, 0}
2017-09-20 11:54:09.361462 0800 Sample[1276:375286] self.tableView.adjustedContentInset = {0, 0, 0, 0}
2017-09-20 11:54:09.420000 0800 Sample[1276:375286] viewDidAppear
2017-09-20 11:54:09.420378 0800 Sample[1276:375286] self.tableView.contentInset = {0, 0, 0, 0}
2017-09-20 11:54:09.420554 0800 Sample[1276:375286] self.tableView.adjustedContentInset = {20, 0, 0, 0}

可见,tableView的adjustedContentInset自动改变了,但是contentInset的值是保持不变的。注:一定要是VC的根视图为UIScrollView或者其子类才能够得到adjustedContentInset的值,否则获取到的是空值。而且非根视图的滚动视图就会被安全区域所裁剪,看到的样式如下图所示:

样式效果对比

通过使用contentInsetAdjustmentBehavior属性可以控制 adjustedContentInset的变化。该属性为枚举类型,其定义如下:

typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {
UIScrollViewContentInsetAdjustmentAutomatic,
UIScrollViewContentInsetAdjustmentScrollableAxes, 
UIScrollViewContentInsetAdjustmentNever,
UIScrollViewContentInsetAdjustmentAlways, 
}

其中UIScrollViewContentInsetAdjustmentAutomatic与UIScrollViewContentInsetAdjustmentScrollableAxes一样,ScrollView会自动计算和适应顶部和底部的内边距并且在scrollView 不可滚动时,也会设置内边距;

UIScrollViewContentInsetAdjustmentNever表示不计算内边距;UIScrollViewContentInsetAdjustmentAlways则根据视图的安全区域来计算内边距。

如果需要感知adjustedContentInset的变化,然后根据变化进行不同操作则可以通过重写新增的adjustedContentInsetDidChange方法或者实现UIScrollViewDelegate中的scrollViewDidChangeAdjustedContentInset方法来实现。如:

//重写方法
- (void)adjustedContentInsetDidChange
{
[super adjustedContentInsetDidChange];

//执行操作...
}

//实现委托
- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView
{
//执行操作...
}

除了新增上述所说的边距相关属性外,还新增了contentLayoutGuide和frameLayoutGuide属性,用于描述内容布局和整体布局信息。

6. UI主线程操作日志提醒

之前的系统中如果你不小心将UI放入非主线程操作时,Debug日志是没有任何信息反馈的,导致有时候在排错时非常困难。在新的Xcode 9中,如果你处于调试状态,将UI放入非主线程操作,如:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.tv = [[UITableView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:self.tv];
NSLog(@"self.tv.adjustedContentInset = %@", NSStringFromUIEdgeInsets(self.tv.adjustedContentInset));
});

Log中会出现下面提示:

=================================================================
Main Thread Checker: UI API called on a background thread: -[UIView bounds]
PID: 16919, TID: 2972321, Thread name: (none), Queue name: com.apple.root.default-qos, QoS: 21
Backtrace:
4 Sample    0x00000001004885dc __29-[ViewController viewDidLoad]_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_worker_thread3   136
10 libsystem_pthread.dylib  0x000000018300b130 _pthread_wqthread   1268
11 libsystem_pthread.dylib  0x000000018300ac30 start_wqthread   4

从日志中了解到一个Main Thread Checker的东西,根据苹果官方文档来看他是作用在AppKit(OSX中)、UIKit还有一些相关API上的后台线程,主要是用来监控这些框架中的接口是否在主线程中进行调用,如果没有则发出警告日志。因此,利用这个功能可以让我们快速地定位那些地方存在问题。

以上就是本次IOS11新特性与兼容适配介绍的全部内容,如果大家还有任何问题可以在下方留言处留言讨论。

IOS11新特性与兼容适配的更多相关文章

  1. ios11 – 发货后连续培训CoreML模型

    在查看新的CoreMLAPI时,我看不到在生成.mlmodel并将其捆绑到您的应用程序后继续训练模型的任何方法.这让我觉得我无法对用户的内容或动作进行机器学习,因为模型必须事先经过完全训练.发货后有没有办法将训练数据添加到我训练过的模型中?本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  2. 本地通知无法在iOS 11中运行

    我在我的应用中使用本地通知.现在iOS11已经出现了,他们不再工作了.我没有在发布文档中看到任何可能导致此问题的内容,是否有人知道可能导致此问题的原因?我知道它已被弃用,但我认为没有理由说它为什么不应该只是工作.提前致谢解决方法似乎iOS11中存在一个错误.我可以看到我是否安排了超过64个允许的通知,它忽略了我安排的所有通知.docs状态系统保持最快的64个通知并丢弃其余的通知.在iOS11上它只是丢弃所有…

  3. 从iOS 11搜索控制器导航时,不需要的UITableView重新加载动画

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  4. iOS 11中的Apple Watch上缺少通知图标

    这在iOS10中运行良好,它在iOS11中停止工作.解决方法正如@Skyborg指出的那样,这似乎是一个watchOS错误,并在watchOS4.1中得到修复.

  5. 如何在Swift中以编程方式检测iOS11?

    我在WWDC视频中看到,有一些新的快速功能可以检测iOS11的可用性.如何使用Swift检测iOS11?

  6. swift3 – 为iOS8创建Launch Screen.xib(… iOS11,Swift 4和LaunchScreen.storyboard)

    你有两个选择:>您使用autolayout并为imageview提供全屏外观.但是,这会导致某些屏幕尺寸的图像被削减,这可能是您不想要的.所以你可能要考虑>将启动屏幕图像放入资产目录中,然后将不同的图像放入不同的大小类中.

  7. React18新增特性介绍

    react历次版本迭代主要想解决的是两类导致网页卡顿的问题,分别是cpu密集型任务和io密集型任务导致的卡顿问题,react18新增特性就是为了解决上述问题

  8. JavaScript高级程序设计(第3版)学习笔记13 ECMAScript5新特性

    通常而言,JavaScript由ECMAScript核心、BOM和DOM三部分构成,前面的文章将ECMAScript核心部分粗略的过了一篇

  9. Vant+postcss-pxtorem 实现浏览器适配功能

    这篇文章主要介绍了Vant+postcss-pxtorem 实现浏览器适配,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  10. iOS10 适配以及Xcode8配置总结

    这篇文章主要介绍了iOS10 适配以及Xcode8配置总结的相关资料,本文通过图文并茂的形式给大家介绍,非常不错具有参考借鉴价值,需要的朋友可以参考下

随机推荐

  1. iOS实现拖拽View跟随手指浮动效果

    这篇文章主要为大家详细介绍了iOS实现拖拽View跟随手指浮动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. iOS – genstrings:无法连接到输出目录en.lproj

    使用我桌面上的项目文件夹,我启动终端输入:cd然后将我的项目文件夹拖到终端,它给了我路径.然后我将这行代码粘贴到终端中找.-name*.m|xargsgenstrings-oen.lproj我在终端中收到此错误消息:genstrings:无法连接到输出目录en.lproj它多次打印这行,然后说我的项目是一个目录的路径?没有.strings文件.对我做错了什么的想法?

  3. iOS 7 UIButtonBarItem图像没有色调

    如何确保按钮图标采用全局色调?解决方法只是想将其转换为根注释,以便为“回答”复选标记提供更好的上下文,并提供更好的格式.我能想出这个!

  4. ios – 在自定义相机层的AVFoundation中自动对焦和自动曝光

    为AVFoundation定制图层相机创建精确的自动对焦和曝光的最佳方法是什么?

  5. ios – Xcode找不到Alamofire,错误:没有这样的模块’Alamofire’

    我正在尝试按照github(https://github.com/Alamofire/Alamofire#cocoapods)指令将Alamofire包含在我的Swift项目中.我创建了一个新项目,导航到项目目录并运行此命令sudogeminstallcocoapods.然后我面临以下错误:搜索后我设法通过运行此命令安装cocoapodssudogeminstall-n/usr/local/bin

  6. ios – 在没有iPhone6s或更新的情况下测试ARKit

    我在决定下载Xcode9之前.我想玩新的框架–ARKit.我知道要用ARKit运行app我需要一个带有A9芯片或更新版本的设备.不幸的是我有一个较旧的.我的问题是已经下载了新Xcode的人.在我的情况下有可能运行ARKit应用程序吗?那个或其他任何模拟器?任何想法或我将不得不购买新设备?解决方法任何iOS11设备都可以使用ARKit,但是具有高质量AR体验的全球跟踪功能需要使用A9或更高版本处理器的设备.使用iOS11测试版更新您的设备是必要的.

  7. 将iOS应用移植到Android

    我们制作了一个具有2000个目标c类的退出大型iOS应用程序.我想知道有一个最佳实践指南将其移植到Android?此外,由于我们的应用程序大量使用UINavigation和UIView控制器,我想知道在Android上有类似的模型和实现.谢谢到目前为止,guenter解决方法老实说,我认为你正在计划的只是制作难以维护的糟糕代码.我意识到这听起来像很多工作,但从长远来看它会更容易,我只是将应用程序的概念“移植”到android并从头开始编写.

  8. ios – 在Swift中覆盖Objective C类方法

    我是Swift的初学者,我正在尝试在Swift项目中使用JSONModel.我想从JSONModel覆盖方法keyMapper,但我没有找到如何覆盖模型类中的Objective-C类方法.该方法的签名是:我怎样才能做到这一点?解决方法您可以像覆盖实例方法一样执行此操作,但使用class关键字除外:

  9. ios – 在WKWebView中获取链接URL

    我想在WKWebView中获取tapped链接的url.链接采用自定义格式,可触发应用中的某些操作.例如HTTP://我的网站/帮助#深层链接对讲.我这样使用KVO:这在第一次点击链接时效果很好.但是,如果我连续两次点击相同的链接,它将不报告链接点击.是否有解决方法来解决这个问题,以便我可以检测每个点击并获取链接?任何关于这个的指针都会很棒!解决方法像这样更改addobserver在observeValue函数中,您可以获得两个值

  10. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

返回
顶部