前言
iOS 11beta版已经来了,正式版我想应该也快了,作为一个iOS开发者,这意味着马上就要着手来适配iOS 11了。在开始之前,我想对于iOS 11中的新特性,还是很有必要先了解一下的。总不能每次都是坐等别人的适配手册吧。
这里并没有提到新增加的ARKit
、CoreML
,我想这些新框架目前我们应该还接触不到。至于Drag & Drop
,这个还是蛮有意思的,有时间的时候可以整理下。
UIViewController
-
topLayoutGuide
,bottomLayoutGuide
这2个属性被标记为过期了,但是在Storyboard里设置约束的时候,还是会出现这2个属性 -
automaticallyAdjustsScrollViewInsets
这个属性也被标记过期了,可以使用UIScrollView
的contentInsetAdjustmentBehavior
替代
UIScrollView
- 新增contentInsetAdjustmentBehavior属性
替代之前UIViewController
的automaticallyAdjustsScrollViewInsets
,作用类似,会根据某些情况自动调整scrollview
的contentInset
(实际改变的是adjustedContentInset
属性,contentInset
属性不会变)
有4个可选值:
public enum UIScrollViewContentInsetAdjustmentBehavior : Int {
case automatic // Similar to .scrollableAxes, but will also adjust the top & bottom contentInset when the scroll view is owned by a view controller with automaticallyAdjustsScrollViewContentInset = YES inside a navigation controller, regardless of whether the scroll view is scrollable
case scrollableAxes // Edges for scrollable axes are adjusted (i.e., contentSize.width/height > frame.size.width/height or alwaysBounceHorizontal/Vertical = YES)
case never // contentInset is not adjusted
case always // contentInset is always adjusted by the scroll view's safeAreaInsets
}
- 新增
safeAreaInsets: UIEdgeInsets
属性
只读属性,为了配合contentInsetAdjustmentBehavior
使用 - 新增
adjustedContentInset: UIEdgeInsets
属性
只读属性,这个属性会根据safeAreaInsets
的变化而变化 -
UIScrollViewDelegate
新增scrollViewDidChangeAdjustedContentInset
,当adjustedContentInset
变化时会调用
UIScrollView
的这几个Inset的改变需要引起注意,他改变了原来的contentInset的逻辑(比如现在contentInset
不会受UINavigationBar
的isTranslucent
影响了),可能会对现有的项目中的页面展示有影响,在项目适配iOS11时需要留意下。
UINavigationBar
- 新增
prefersLargeTitles: Bool
属性
大标题,默认为false,当设置为true时,navigation bar会显示大标题,向上滑动页面,navigation bar 会变小直到显示成跟之前一样,同时title位置会发生变化
navigationController?.navigationBar.prefersLargeTitles = true
效果如下:
大标题滚动的过程中,通过打印navigation bar 的frame发现,navigation bar 的高度会跟着变化
如果navigation bar是透明的,scrollview的
safeAreaInsets
属性也会跟着变化大概关系是:
safeAreaInsets.top = navigationBar.frame.height+statusBar.height
UINavigationItem
- 新增
largeTitleDisplayMode
属性
这个属性配合navigation bar的大标题使用的。
当navigation bar启用prefersLargeTitles
后,这个属性才会生效,可以控制某个单独的ViewController
中的large title显示模式,有三个可选值:
public enum LargeTitleDisplayMode : Int {
/// Automatically use the large out-of-line title based on the state of the previous item in the navigation bar. An item with largeTitleDisplayMode=Automatic will show or hide the large title based on the request of the previous navigation item. If the first item pushed is set to Automatic, then it will show the large title if the navigation bar has prefersLargeTitles=YES.
case automatic
/// Always use a larger title when this item is top most.
case always
/// Never use a larger title when this item is top most.
case never
}
使用方法:
navigationItem.largeTitleDisplayMode = .never
简单来说:
-
automatic
:与上一个navigation item设置的largeTitleDisplayMode
相同 -
always
: 总是启用大标题。刚开始有个误解,always并不是说当scrollview滚动的时候,navigation bar一直是大标题模式,而是指,不管上一个viewcontroller设置的是什么,这个viewcontroller都是启用大标题 -
never
:总是显示小标题模式,就是我们正常看到的导航栏标题样式
关于如何修改largetitle
的样式,目前尚没找到正确的打开方式。以前通过navigationbar.titleTextAttributes
直接修改小标题的样式,对大标题无效(至少目前看是无效的)。
- 新增
searchController
属性
在navigation bar下面增加一个搜索框
let searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.backgroundColor = .white
navigationItem.searchController = searchController
效果如下:
searchcontroller.gif- 新增
hidesSearchBarWhenScrolling:Bool
属性
配合searchController
使用的,默认是true
。
这个属性是控制searchController
默认是否显示的。
通过上图也可以看到,searchBar默认是隐藏的,当下拉的时候才会显示出来,再上拉又会隐藏。
当设置为false时,searchBar会一直显示,当scrollview下拉时,searchBar会随着scrollview往下走,上拉时,则固定在顶部不动。
如下图:
有意思的是,当
scrollview
下拉时,navigation bar
的高度是一直增大的(通过在scrollViewDidScroll
代理里打印navigation bar
的frame
就会发现),也就是系统实际上是通过增大navigation bar
的height
,来让search bar
紧随着scrollview
的content的。查看层级关系,会发现,
searchBar
并不是navigation bar
的subview
。<img src='https://img.haomeiwen.com/i2412938/05ab84f30acc7fcb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240' width='200' />
UITableView
- 新增
separatorInsetReference
属性
分割线相关的,有2个可选值:
public enum UITableViewSeparatorInsetReference : Int {
// The value set to the separatorInset property is interpreted as an offset from the edges of the cell.
case fromCellEdges
// The value set to the separatorInset property is interpreted as an offset from the automatic separator insets.
case fromAutomaticInsets
}
举个例子,TableView的separator默认左边会留15,如果要去掉这个空隙,顶头显示
iOS 11之前的写法:
table.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
cell.layoutMargins = .zero
iOS 11之后的写法:
table.separatorInsetReference = .fromCellEdges //默认就是fromCellEdges,所以可以不写这行代码
cell.separatorInset = .zero
目前我测试的结果是,当设置separatorInsetReference
为fromCellEdges
时,separator
的Inset就相当于 cell.separatorInset
,当设置为fromAutomaticInsets
时,tableView.separatorInset
和cell.separatorInset
都无效。(可能是我的打开方式不对?)
- 新增
performBatchUpdates
函数,支持批量操作了
Swipe actions
主要是实现了TableViewCell
的左划和右划手势功能
在UITableViewDelegate
中,新增了两个delegate,如下:
// These methods supersede -editActionsForRowAtIndexPath: if implemented
// return nil to get the default swipe actions
@available(iOS 11.0, *)
optional public func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
@available(iOS 11.0, *)
optional public func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
可以看到,这2个delegate是为了取代原有的editActionsForRowAtIndexPath
的,并且细化了是左滑还是右滑,同时提供了很不错的交互体验。
下面代码是实现了一个Star功能的左滑手势
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let action = UIContextualAction(style: .normal, title: "Star") { (action, view, handler) in
self.starAction(indexPath: indexPath)
handler(true)
}
action.backgroundColor = .green
if let stared = stars[indexPath], stared {
action.title = "Unstar"
action.backgroundColor = .red
}
return UISwipeActionsConfiguration(actions: [action])
}
先看效果:
leadingswipe.gif下面看下里面涉及到的几个类
- UIContextualAction
表示的是滑开后显示的一个操作按钮,它有下面几个属性:-
style
: 有2个可选值:.normal
和.destructive
,区别是背景色不同,.normal
是灰色,.destructive
是红色。当然如果手动设置了action.backgroundColor
,则以backgroundColor
为准。
还有一个区别,下面会说。 -
title
: action显示的文字,目前没有发现api可以修改这个title的font和color -
image
: 设置action的图片,设置了image就不会显示文字了 -
handler
: 点击action后的回调,它的定义如下:
-
// call the completionHandler to reset the context to its normal state (e.g. when swiping, resets to unswiped state)
// pass YES to the completionHandler if the action was actually performed, to show a visual indication of the successful completion
public typealias UIContextualActionHandler = (UIContextualAction, UIView, (Bool) -> Swift.Void) -> Swift.Void
可以看到有3个参数:
UIContextualAction
: 就是当前所属的action啦
UIView
: 可以理解成action所呈现出来的那个视图。如果是action是文字的,view是一个叫做UIButtonLabel
的东东,如果是image的,view则是UIImageView
(Bool) -> Swift.Void
: 这个参数是一个闭包,他的作用是一个completionHandler
,在handler的定义上面,已经给出了说明,意思是在handler里你应该调用这个completionHandler
,以恢复到正常状态(可以看上面那个效果图,点击action后,cell会恢复到未左滑的状态)如果不调用,点击后就会是保持现有的左侧滑开的状态。
而且这个completionHandler
也需要一个Bool类型的参数,传true
和传false
有什么区别呢?官方的说明是pass YES to the completionHandler if the action was actually performed
其实这个就是style
中的normal
和destructive
的另一个区别。
我们知道,destructive
的意思是危险操作,一般表示的是删除。如果你调用completionHandler
传的是true
,当style=.destructive
时,系统会删掉这个cell,没错,删掉这个cell!按照官方的解释可以理解成,destructive
就是删除,你传了true
,说明action actually performed
,那系统就会删掉这个cell.
对于style=.normal
的,我试了,传true
和false
,没区别。
- UISwipeActionsConfiguration
它只有两个属性,一个是actions
数组,表明你可以添加多个action操作;还有一个叫performsFirstActionWithFullSwipe
,默认是true
,意思是当你full swipe
(完全滑动)的时候,系统会自动执行第一个action的handler,这个在上面的效果图上也能看到。
右滑跟左滑类似,就不再说了。
结束语
目前就整理了这么多,如果有描述错误的,还望不吝赐教。
希望大家都顺利完成iOS11的适配工作!😂😂
网友评论
scrollView?.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
或者:
NSLayoutConstraint(item: scrollView!, attribute: .leading, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true