zoukankan      html  css  js  c++  java
  • iOS -- iOS11新特性,如何适配iOS11

    前言

    这几天抽空把WWDC的Session看了一些,总结了一些iOS11新的特性,可能对我们的App有影响,需要我们进行适配。本文作为一个总结。

    本文内容包括:集成了搜索的大标题栏、横向选项卡栏、Margins 和 Insets以及 UIScrollViewUITableView 的更新和功能更强大的滑动操作。

    一. 在UIKit’s Bars中加入的新功能

    WWDC通过iOS新增的文件管理App:Files开始介绍,在Files这个APP中能够看到iOS11中UIKit’s Bars的一些新特性:在浏览功能上的大标题视图(向上滑动后标题会回到原来的UI效果)、横屏状态下tab上的文字和icon会变为左右排列。我用iOS11的模拟器体验了一下Files这个APP,如下图所示:

     
     

     

    (command+向左的箭头让模拟器横屏)
    在iPhone上,tab上的图标较小,tab bar较小,这样垂直空间可多放置内容。如果有人看不清楚tab bar上的图标或文字,可以通过长按tab bar上的任意item,会将该item显示在HUD上,这样可以清楚的看清icon和text。对tool bar 和 navigation bar同理,长按item也会放大显示。如下图显示:

     

    UIBarItem

    UIBarItem是UI tab bar item和UI bar button item的父类,要想实现上面介绍的效果,只需要为UIBarItem 设置landscapeImagePhone属性,在storyboard中也支持这个设置,对于HUD的image需要设置另一个iOS11新增的属性:largeContentSizeImage,关于这部分更详细的讨论,可以参考 WWDC2017 Session 215:What's New in Accessibility

    控制大标题的显示

    在UI navigation bar中新增了一个BOOL属性prefersLargeTitles,将该属性设置为ture,navigation bar就会在整个APP中显示大标题,如果想要在控制不同页面大标题的显示,可以通过设置当前页面的navigationItemlargeTitleDisplayMode属性;

    navigationItem.largeTitleDisplayMode 
    
    typedef NS_ENUM(NSInteger, UINavigationItemLargeTitleDisplayMode) {  
    /// 自动模式依赖上一个 item 的特性
    UINavigationItemLargeTitleDisplayModeAutomatic,
    /// 针对当前 item 总是启用大标题特性
    UINavigationItemLargeTitleDisplayModeAlways,
    /// Never 
    UINavigationItemLargeTitleDisplayModeNever,
    }

    Navigation 集成 UISearchController

    把你的UISearchController赋值给navigationItem,就可以实现将UISearchController集成到Navigation

    navigationItem.searchController  //iOS 11 新增属性
    navigationItem.hidesSearchBarWhenScrolling //决定滑动的时候是否隐藏搜索框;iOS 11 新增属性

    UINavigationController和滚动交互

    滚动的时候,以下交互操作都是由UINavigationController负责调动的:

    UIsearchController搜索框效果更新
    大标题效果的控制
    Rubber banding效果 //当你开始往下拉,大标题会变大来回应那个滚轮

    所以,如果你使用navigation bar,组装一些整个push和pop体验,你不会得到searchController的集成、大标题的控制更新和Rubber banding效果,因为这些都是由UINavigationController控制的。

    UIToolbar and UINavigationBar— Layout

    在 iOS 11 中,当苹果进行所有这些新特性时,也进行了其他的优化,针对 UIToolbar 和 UINavigaBar 做了新的自动布局扩展支持,自定义的bar button items、自定义的title都可以通过layout来表示尺寸。
    需要注意的是,你的constraints需要在view内部设置,所以如果你有一个自定义的标题视图,你需要确保任何约束只依赖于标题视图及其任何子视图。当你使用自动布局,系统假设你知道你在做什么。

    Avoiding Zero-Sized Custom Views

    自定义视图的size为0是因为你有一些模糊的约束布局。要避免视图尺寸为0,可以从以下方面做:

    • UINavigationBar 和 UIToolbar 提供位置
    • 开发者则必须提供视图的size,有三种方式:

      • 对宽度和高度的约束;
      • 实现 intrinsicContentSize;
      • 通过约束关联你的子视图;

    二. 管理margins 和 insets

    layout margins

    基于约束的Auto Layout,使我们搭建能够动态响应内部和外部变化的用户界面。Auto Layout为每一个view都定义了marginmargin指的是控件显示内容部分的边缘和控件边缘的距离。
    可以用layoutMargins或者layoutMarginsGuide属性获得view的margin,margin是视图内部的一部分。layoutMargins允许获取或者设置UIEdgeInsets结构的marginlayoutMarginsGuide则获取到只读的UILayoutGuide对象。

    在iOS11新增了一个属性:directional layout margins,该属性是NSDirectionalEdgeInsets结构体类型的属性:

    typedef struct NSDirectionalEdgeInsets {  
        CGFloat top, leading, bottom, trailing;
    } NSDirectionalEdgeInsets API_AVAILABLE(ios(11.0),tvos(11.0),watchos(4.0));

    layoutMarginsUIEdgeInsets结构体类型的属性:

    typedef struct UIEdgeInsets {  
    CGFloat top, left, bottom, right;
    } UIEdgeInsets;

    从上面两种结构体的对比可以看出,NSDirectionalEdgeInsets 属性用leading 和 trailing 取代了之前的 left 和 right。

    directional layout margins属性的说明如下:

    directionalLayoutMargins.leading is used on the left when the user interface direction is LTR and on the right for RTL.
     Vice versa for directionalLayoutMargins.trailing.

    例子:当你设置了trailing = 30;当在一个right to left 语言下trailing的值会被设置在view的左边,可以通过layoutMargin的left属性读出该值。如下图所示:


    还有其他一些更新。自从引入layout margins,当将一个view添加到viewController时,viewController会修复view的的layoutMargins为UIKit定义的一个值,这些调整对外是封闭的。从iOS11开始,这些不再是一个固定的值,它们实际是最小值,你可以改变view的layoutMargins为任意一个更大的值。而且,viewController新增了一个属性:viewRespectsSystemMinimumLayoutMargins,如果你设置该属性为"false",你就可以改变你的layoutMargins为任意你想设置的值,包括0,如下图所示:


    安全区域(Safe Area)

    如下图:照片应用程


    从iOS 7以来,我们在整个操作系统中都有这些半透明的bars,苹果鼓励我们通过这些bars绘制内容,我们是通过viewController 的edgesForExtendedLayout属性来做这些的。
    iOS 7 开始,在 UIViewController中引入的 topLayoutGuide 和 bottomLayoutGuide 在 iOS 11 中被废弃了!取而代之的就是safeArea的概念,safeArea是描述你的视图部分不被任何内容遮挡的方法。 它提供两种方式:safeAreaInsetssafeAreaLayoutGuide来提供给你safeArea的参照值,即 insets 或者 layout guide。 safeArea区域如图所示:



    如果有一个自定义的viewController,你可能要添加你自己的bars,增加safeAreaInsets的值,可以通过一个新的属性:addtionalSafeAreaInsets来改变safeAreaInsets的值,当你的viewController改变了它的safeAreaInsets值时,有两种方式获取到回调:

    UIView.safeAreaInsetsDidChange()
    UIViewController.viewSafeAreaInsetsDidChange()

    三. UIScrollView and UITableView的新特性

    Scroll Views

    如果有一些文本位于UI滚动视图的内部,并包含在导航控制器中,现在一般navigationContollers会传入一个contentInset给其最顶层的viewController的scrollView,在iOS11中进行了一个很大的改变,不再通过scrollView的contentInset属性了,而是新增了一个属性:adjustedContentInset,通过下面两种图的对比,能够表示adjustContentInset表示的区域:


     

    新增的contentInsetAdjustmentBehavior属性用来配置adjustedContentInset的行为,该结构体有以下几种类型:

    typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {  
        UIScrollViewContentInsetAdjustmentAutomatic, 
        UIScrollViewContentInsetAdjustmentScrollableAxes,
        UIScrollViewContentInsetAdjustmentNever,
        UIScrollViewContentInsetAdjustmentAlways,
    }
    
    @property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior;
    @property(nonatomic, readonly) UIEdgeInsets adjustedContentInset;
    
       //adjustedContentInset值被改变的delegate
    - (void)adjustedContentInsetDidChange; 
    - (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView;

    Table Views :在iOS 11中默认启用Self-Sizing

    这个应该是UITableView最大的改变。我们知道在iOS8引入Self-Sizing 之后,我们可以通过实现estimatedRowHeight相关的属性来展示动态的内容,实现了estimatedRowHeight属性后,得到的初始contenSize是个估算值,是通过estimatedRowHeight xcell的个数得到的,并不是最终的contenSizetableView不会一次性计算所有的cell的高度了,只会计算当前屏幕能够显示的cell个数再加上几个,滑动时,tableView不停地得到新的cell,更新自己的contenSize,在滑到最后的时候,会得到正确的contenSize。创建tableView到显示出来的过程中,contentSize的计算过程如下图:



     

    Self-Sizing在iOS11下是默认开启的,Headers, footers, and cells都默认开启Self-Sizing,所有estimated 高度默认值从iOS11之前的 0 改变为UITableViewAutomaticDimension

    @property (nonatomic) CGFloat estimatedRowHeight NS_AVAILABLE_IOS(7_0); // default is UITableViewAutomaticDimension, set to 0 to disable

    如果目前项目中没有使用estimateRowHeight属性,在iOS11的环境下就要注意了,因为开启Self-Sizing之后,tableView是使用estimateRowHeight属性的,这样就会造成contentSize和contentOffset值的变化,如果是有动画是观察这两个属性的变化进行的,就会造成动画的异常,因为在估算行高机制下,contentSize的值是一点点地变化更新的,所有cell显示完后才是最终的contentSize值。因为不会缓存正确的行高,tableView reloadData的时候,会重新计算contentSize,就有可能会引起contentOffset的变化。iOS11下不想使用Self-Sizing的话,可以通过以下方式关闭:

    self.tableView.estimatedRowHeight = 0;
    self.tableView.estimatedSectionHeaderHeight = 0;
    self.tableView.estimatedSectionFooterHeight = 0;

    iOS11下,如果没有设置estimateRowHeight的值,也没有设置rowHeight的值,那contentSize计算初始值是 44 * cell的个数,如下图:


    Table Views:separatorInset 扩展

    iOS 7 引入separatorInset属性,用以设置 cell 的分割线边距,在 iOS 11 中对其进行了扩展。可以通过新增的UITableViewSeparatorInsetReference枚举类型的separatorInsetReference属性来设置separatorInset属性的参照值。

    typedef NS_ENUM(NSInteger, UITableViewSeparatorInsetReference) {  
        UITableViewSeparatorInsetFromCellEdges,   //默认值,表示separatorInset是从cell的边缘的偏移量
        UITableViewSeparatorInsetFromAutomaticInsets  //表示separatorInset属性值是从一个insets的偏移量
    }

    下图清晰的展示了这两种参照值的区别:



    Table Views 和 Safe Area

    有以下几点需要注意:

    • separatorInset 被自动地关联到 safe area insets,因此,默认情况下,表视图的整个内容避免了其根视图控制器的安全区域的插入。
    • UITableviewCell 和 UITableViewHeaderFooterView的 content view 在安全区域内;因此你应该始终在 content view 中使用add-subviews操作。
    • 所有的 headers 和 footers 都应该使用UITableViewHeaderFooterView,包括 table headers 和 footers、section headers 和 footers。

    滑动操作(Swipe Actions)

    在iOS8之后,苹果官方增加了UITableVIew的右滑操作接口,即新增了一个代理方法(tableView: editActionsForRowAtIndexPath:)和一个类(UITableViewRowAction),代理方法返回的是一个数组,我们可以在这个代理方法中定义所需要的操作按钮(删除、置顶等),这些按钮的类就是UITableViewRowAction。这个类只能定义按钮的显示文字、背景色、和按钮事件。并且返回数组的第一个元素在UITableViewCell的最右侧显示,最后一个元素在最左侧显示。从iOS 11开始有了一些改变,首先是可以给这些按钮添加图片了,然后是如果实现了以下两个iOS 11新增的代理方法,将会取代(tableView: editActionsForRowAtIndexPath:)代理方法:

    // Swipe actions
    // These methods supersede -editActionsForRowAtIndexPath: if implemented
    - (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView leadingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
    - (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath

    这两个代理方法返回的是UISwipeActionsConfiguration类型的对象,创建该对象及赋值可看下面的代码片段:

     - ( UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {
        //删除
        UIContextualAction *deleteRowAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive title:@"delete" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
            [self.titleArr removeObjectAtIndex:indexPath.row];
            completionHandler (YES);
        }];
        deleteRowAction.image = [UIImage imageNamed:@"icon_del"];
        deleteRowAction.backgroundColor = [UIColor blueColor];
    
        UISwipeActionsConfiguration *config = [UISwipeActionsConfiguration configurationWithActions:@[deleteRowAction]];
        return config;
    }

    创建UIContextualAction对象时,UIContextualActionStyle有两种类型,如果是置顶、已读等按钮就使用UIContextualActionStyleNormal类型,delete操作按钮可使用UIContextualActionStyleDestructive类型,当使用该类型时,如果是右滑操作,一直向右滑动某个cell,会直接执行删除操作,不用再点击删除按钮,这也是一个好玩的更新。

    typedef NS_ENUM(NSInteger, UIContextualActionStyle) {
        UIContextualActionStyleNormal,
        UIContextualActionStyleDestructive
    } NS_SWIFT_NAME(UIContextualAction.Style)

    滑动操作这里还有一个需要注意的是,当cell高度较小时,会只显示image,不显示title,当cell高度够大时,会同时显示image和title。我写demo测试的时候,因为每个cell的高度都较小,所以只显示image,然后我增加cell的高度后,就可以同时显示image和title了。见下图对比:

    总结

    大概介绍了iOS 11的UI方面的一些更新,大部分内容自己代码测试过了,有些更新确实是很实用,可以适配下iOS 11,有的更新可能会给现有APP造成bug,所以学习下这些内容还是很有必要的。

  • 相关阅读:
    pat甲级 1155 Heap Paths (30 分)
    pat甲级 1152 Google Recruitment (20 分)
    蓝桥杯 基础练习 特殊回文数
    蓝桥杯 基础练习 十进制转十六进制
    蓝桥杯 基础练习 十六进制转十进制
    蓝桥杯 基础练习 十六进制转八进制
    51nod 1347 旋转字符串
    蓝桥杯 入门训练 圆的面积
    蓝桥杯 入门训练 Fibonacci数列
    链表相关
  • 原文地址:https://www.cnblogs.com/mafeng/p/7605381.html
Copyright © 2011-2022 走看看