What's new in iOS 11 - 部分iOS11新特性整理

前言

iOS 11beta版已经来了,正式版我想应该也快了,作为一个iOS开发者,这意味着马上就要着手来适配iOS 11了。在开始之前,我想对于iOS 11中的新特性,还是很有必要先了解一下的。总不能每次都是坐等别人的适配手册吧。

这里并没有提到新增加的ARKitCoreML,我想这些新框架目前我们应该还接触不到。至于Drag & Drop,这个还是蛮有意思的,有时间的时候可以整理下。

UIViewController

  • topLayoutGuide, bottomLayoutGuide
    这2个属性被标记为过期了,但是在Storyboard里设置约束的时候,还是会出现这2个属性
  • automaticallyAdjustsScrollViewInsets
    这个属性也被标记过期了,可以使用UIScrollViewcontentInsetAdjustmentBehavior替代

UIScrollView

  • 新增contentInsetAdjustmentBehavior属性
    替代之前UIViewControllerautomaticallyAdjustsScrollViewInsets,作用类似,会根据某些情况自动调整scrollviewcontentInset(实际改变的是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不会受UINavigationBarisTranslucent影响了),可能会对现有的项目中的页面展示有影响,在项目适配iOS11时需要留意下。

UINavigationBar

  • 新增prefersLargeTitles: Bool属性
    大标题,默认为false,当设置为true时,navigation bar会显示大标题,向上滑动页面,navigation bar 会变小直到显示成跟之前一样,同时title位置会发生变化
navigationController?.navigationBar.prefersLargeTitles = true

效果如下:

What's new in iOS 11 - 部分iOS11新特性整理_第1张图片
大标题

滚动的过程中,通过打印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

效果如下:

What's new in iOS 11 - 部分iOS11新特性整理_第2张图片
searchcontroller.gif
  • 新增hidesSearchBarWhenScrolling:Bool属性
    配合searchController使用的,默认是true
    这个属性是控制searchController默认是否显示的。
    通过上图也可以看到,searchBar默认是隐藏的,当下拉的时候才会显示出来,再上拉又会隐藏。
    当设置为false时,searchBar会一直显示,当scrollview下拉时,searchBar会随着scrollview往下走,上拉时,则固定在顶部不动。
    如下图:

What's new in iOS 11 - 部分iOS11新特性整理_第3张图片
searchcontroller2.gif

有意思的是,当 scrollview下拉时, navigation bar的高度是一直增大的(通过在 scrollViewDidScroll代理里打印 navigation barframe就会发现),也就是系统实际上是通过增大 navigation barheight,来让 search bar紧随着 scrollview的content的。
查看层级关系,会发现, searchBar并不是 navigation barsubview

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

目前我测试的结果是,当设置separatorInsetReferencefromCellEdges时,separator的Inset就相当于 cell.separatorInset,当设置为fromAutomaticInsets时,tableView.separatorInsetcell.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])
    }

先看效果:

What's new in iOS 11 - 部分iOS11新特性整理_第4张图片
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中的normaldestructive的另一个区别。
我们知道,destructive的意思是危险操作,一般表示的是删除。如果你调用completionHandler传的是true,当style=.destructive时,系统会删掉这个cell,没错,删掉这个cell!按照官方的解释可以理解成,destructive就是删除,你传了true,说明action actually performed,那系统就会删掉这个cell.
对于style=.normal的,我试了,传truefalse,没区别。

  • UISwipeActionsConfiguration
    它只有两个属性,一个是actions数组,表明你可以添加多个action操作;还有一个叫performsFirstActionWithFullSwipe,默认是true,意思是当你full swipe(完全滑动)的时候,系统会自动执行第一个action的handler,这个在上面的效果图上也能看到。
    右滑跟左滑类似,就不再说了。

结束语

目前就整理了这么多,如果有描述错误的,还望不吝赐教。
希望大家都顺利完成iOS11的适配工作!

你可能感兴趣的:(What's new in iOS 11 - 部分iOS11新特性整理)