Updating Your App for iOS 11 - Part2(Safe Area/ScrollView/TableView的新特性)

一、Safe Area

从 iOS 7 开始,我们就在操作系统里提供这样的半透明的栏,并且鼓励你把要显示的内容布局延伸过这些栏,就像下图中照片 App 中做的那样。(注意顶部和底部都带有 Bar,且内容都被 Bar 所覆盖,产生出模糊效果)


image.png

之所以又这样的效果是利用了 UIViewController 的属性 edgesForExtendedLayout,它可以让作为 Container 的ViewControllers 定义这些(translucent bars)栏下View 的大小

edgesForExtendedLayout控制 View 的大小 让 translucent bars 覆盖其上之后带有模糊效果

默认情况下edgesForExtendedLayout 适用于所有的边缘,你可以通过topLayoutGuide 和 bottomLayoutGuide 两个属性来定义悬浮栏的大小。

从 iOS 11开始,系统将取消topLayoutGuide 和 bottomLayoutGuide属性,引入新的布局结构概念,SafeArea。

取消topLayoutGuide 和 bottomLayoutGuide属性 新的布局结构概念,SafeArea。

safeArea是描述你的视图部分不被任何内容遮挡的方法。 它提供两种方式:safeAreaInsets 或 safeAreaLayoutGuide 来提供给你 safeArea 的参照值,这两个属性定义在 UIView 中,它们分别对应 insets 或者 layout guide类型。

例如在你自定义的 ViewController 中添加一些自定义的栏样式 View,此时就需要改变 safeAreaInsets 的值。要想增加或减少safeAreaInsets的值,你可以通过调用 UIViewController 的新属性 additionalSafeAreaInsets (UIEdgeInsets 类型)在对应的位置增加 inset 值进而改变 safeAreaInsets。当你的viewController改变了它的safeAreaInsets值时,有两种方式获取到回调:

UIView.safeAreaInsetsDidChange()
UIViewController.viewSafeAreaInsetsDidChange()

每个 view 都可以改变 safeAreaInsets 的值,包括 UIViewController。

image.png

二、Scroll Views

下面例子中的结构是 UIVIewController + UIScrollView 包在
UINavigationController 里面。

以前如果一个 VIewController 中含有 ScrollView的话, 被
NavigationController 包住的这个 ViewController 会自动地调整 ScrollView 的 contentInset 值(增加64)如下

iOS 11之后这个行为已取消,取而代之的是,使用一个新的属性adjustedContentInset代替。而 contentInset 这个属性代表的概念简单明了,单单是内容的区域的 inset,不再与外界布局有关。

UIScrollView 支持自动布局,让scrollView可以根据所添加的sub-view的大小自动处理其可滚动区域的大小。iOS 11下更是添加了一些新的属性来协助开发中更快速的布局,其中包括 frameLayoutGuide 和 contentLayoutGuide 以及 contentInsetAdjustmentBehavior。

  1. frameLayoutGuide 负责scrollView在屏幕中的大小和位置,也就是你可以约束 scrollView 中的 sub-view 如下图中的 Page 1 labelView。当你滚动时,该 page 1 labelview 是固定不动的。
约束 scrollView 中的 sub-view Page 1 labelView 当内容滚动后,Page 1 位置不变
  1. contentLayoutGuide,你可以约束 sub-view 来控制器 scrollView 中可滚动区域的大小或者让内容随着滚动而移动。
指定 contentLayoutGuide 发生滚动时
  1. contentInsetAdjustmentBehavior属性用来配置adjustedContentInset的行为,该结构体有以下几种类型:
typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {
    UIScrollViewContentInsetAdjustmentAutomatic, // Similar to .scrollableAxes, but for backward compatibility will also adjust the top & bottom contentInset when the scroll view is owned by a view controller with automaticallyAdjustsScrollViewInsets = YES inside a navigation controller, regardless of whether the scroll view is scrollable
    UIScrollViewContentInsetAdjustmentScrollableAxes, // Edges for scrollable axes are adjusted (i.e., contentSize.width/height > frame.size.width/height or alwaysBounceHorizontal/Vertical = YES)
    UIScrollViewContentInsetAdjustmentNever, // contentInset is not adjusted
    UIScrollViewContentInsetAdjustmentAlways, // contentInset is always adjusted by the scroll view's safeAreaInsets
} API_AVAILABLE(ios(11.0),tvos(11.0));
/* Configure the behavior of adjustedContentInset.
 Default is UIScrollViewContentInsetAdjustmentAutomatic.
 */
@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior API_AVAILABLE(ios(11.0),tvos(11.0));
/* When contentInsetAdjustmentBehavior allows, UIScrollView may incorporate
 its safeAreaInsets into the adjustedContentInset.
 */
@property(nonatomic, readonly) UIEdgeInsets adjustedContentInset API_AVAILABLE(ios(11.0),tvos(11.0));

当adjustedContentInset 值被改变后回调的代理方法有:

/* Also see -[UIScrollView adjustedContentInsetDidChange]
 */
- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView API_AVAILABLE(ios(11.0), tvos(11.0));

三、Table Views

  1. 我们知道在iOS 8引入Self-Sizing 之后,可以通过实现estimatedRowHeight 相关的属性来展示动态的内容,当实现了estimatedRowHeight 属性后,tableview 会得到的初始 contenSize ,这是一个估算值,是通过estimatedRowHeight * cell的个数得到的,并不是最终的 contenSize。

因为有估算的 contentSize 值,所以tableView就不会一次性计算所有的cell的高度了,只会计算当前屏幕能够显示的cell个数再加上几个。

滑动时,tableView 不停地得到新的 cell,更新自己的 contenSize,在滑到最后的时候,会得到正确的 contenSize 。在测试Demo中,创建tableView 到显示出来的过程中,contentSize 的计算过程如下图:

  1. Self-Sizing
    在iOS 11中默认启用 Self-Sizing, 也就是说你 cell、header、footer对应的 estimated heights 默认值都从 iOS 11 之前的0 变为UITableViewAutomaticDimension。
    因为默认开启了 Self-Sizing,你在布局 cell 时需要确保内部子控件具备完整约束来让 tableview 自动计算出其需要的大小或者你在对应的 delegate 方法中返回每一个 cell 的真实高度值。同理也需要处理对应 header 和 footer 问题。

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

如果你想 link 到 iOS 11 而不想使用这个默认开启的新特性(Self-Sizing)的话,你可以取消它,代码如下:

override func viewDidLoad() {
//取消 estimated sizes 功能和 tableview 的 Self-Sizing 功能
 tableView.estimatedRowHeight = 0
 tableView.estimatedSectionHeaderHeight = 0
 tableView.estimatedSectionFooterHeight = 0
}

iOS11下,如果没有设置estimateRowHeight的值,也没有设置rowHeight的值,那contentSize计算初始值是 44 * cell的个数,如下图:rowHeight和estimateRowHeight都是默认值UITableViewAutomaticDimension 而rowNum = 15;则初始contentSize = 44 * 15 = 660;

  1. separatorInset
    tableView 的 readable content guide 概念,它是 View 内的一部分,也是内容布局的推荐区域。即使在大屏幕的 iPad 下,在 readable content guide 内布局的内容都能够获得不错的用户阅读体验。

默认情况下 tableview 在 readable content guide 内有一个 separatorInset,它可以影响 cell 的默认分隔线位置 和 在 cell 内 labels 的位置。

separator.left = 0 separator.left = 30

可见 separatorInset 是对 readable content view 的 inset 处理。

iOS 11 之后,separatorInset 值影响的是,tableview 边框与屏幕的边缘的间隔大小,当设置左右为0时,效果如下

iPad 横屏下,separatorInset.left = 0 和 separatorInset.right = 0

如下是 separatorInset 值使用对别,其中在 iOS 11后添加可设置参照的属性UITableViewSeparatorInsetReference

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

对比使用如下:

UITableViewSeparatorInsetFromCellEdges UITableViewSeparatorInsetFromAutomaticInsets
  1. tableview 与 Safe Area 交互需要注意几点:
  • separatorInset 被自动地关联到 safe area insets,因此,默认情况下,tabelview的整个内容区域避免了ViewController安全区域的插入。
  • UITableviewCell 和 UITableViewHeaderFooterView的 content view 在安全区域内;因此你应该始终在 content view 中使用add-subviews操作
  • 你应该使用带有 content view 的 UITableViewHeaderFooterView类实例作为table headers 和 footers、section headers 和 footers。
  1. Swipe Actions
    1. 新的滚动条:带有 time stamps 时码的滚动条
    2. 实现 full swipe-to-delete 功能
    3. 添加了又滑功能

测试默认开启Self-Sizing的 iOS 11问题。

问题1:如下代码是运行在 iOS 10下正常,但运行在 iOS 11则在 tabelView 上下有留白问题
//
//  ViewController.m
//  ios10TabelView
//
//  Created by Jacob_Liang on 2017/9/21.
//  Copyright © 2017年 Jacob. All rights reserved.
//

#import "ViewController.h"

static NSString * const CELLID = @"CELLID";

@interface ViewController ()<UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, weak) UITableView *tableView;

@end

@implementation ViewController


- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setUpInit];
    [self setUpNav];
    [self setUpTableView];
    
}

- (void)setUpInit {

    self.automaticallyAdjustsScrollViewInsets = NO; //iOS 11下被废弃了,写了也没用
    self.view.backgroundColor = [UIColor purpleColor];
}

- (void)setUpNav {
    self.navigationItem.title = @"出席统计";
}

- (void)setUpTableView {
    
    CGFloat screenW = [UIScreen mainScreen].bounds.size.width;
    CGFloat screenH = [UIScreen mainScreen].bounds.size.height;
    
    UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, screenW, screenH - 64) style:UITableViewStyleGrouped];
    [self.view addSubview:tableView];
    _tableView = tableView;
    tableView.backgroundColor = [UIColor lightGrayColor];
    tableView.delegate = self;
    tableView.dataSource = self;
    
    [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CELLID];

}

#pragma mark - UITableViewDelegate & UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 15;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CELLID forIndexPath:indexPath];
    cell.textLabel.text = [NSString stringWithFormat:@"%@",indexPath];
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 50;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 0.01;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return 0.01;
}

@end

上述代码运行情况,注意此时 tableview 的 Style 为 UITableViewStyleGrouped

在 iOS 10 下 iOS 11下 iOS 11下
self.automaticallyAdjustsScrollViewInsets = NO 有效 self.automaticallyAdjustsScrollViewInsets = NO 无效 self.automaticallyAdjustsScrollViewInsets = NO 无效
没有调用viewForFooterInSection和viewForHeaderInSection运行正常 没有调用viewForFooterInSection和viewForHeaderInSection运行有留白 调用viewForFooterInSection和viewForHeaderInSection运行正常
iOS10NOReturnViewFooterHeader.gif
NOReturnHeaderOrFooterViewQuestion.gif
wihtReturnHeaderOrFooterView.gif

另一宗办法就是,关闭 iOS 11默认打开的 Self-Sizing 功能

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

问题1测试 demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容

  • iOS 11 为整个生态系统的 UI 元素带来了一种更加大胆、动态的新风格。 本文介绍iOS11中在UI方面做了哪...
    阿凡提说AI阅读 586评论 0 1
  • 版权声明:未经本人允许,禁止转载. 1. TableView初始化 1.UITableView有两种风格:UITa...
    萧雪痕阅读 2,906评论 2 10
  • 我们在上一篇《通过代码自定义不等高cell》中学习了tableView的相关知识,本文将在上文的基础上,利用sto...
    啊世ka阅读 1,497评论 2 7
  • 人心里的悲观,只不过是他见到了阴暗给眼前的坎,心生恐惧;又遭遇了黑暗给人生存的艰难,意志消沉。 ...
    汩月阅读 713评论 0 0
  • 下午放学时候,女儿得意的告诉我,数学考试得了110分,满分。紧接着愁眉苦脸说,可是明天她们要考英语,她的英语总是考...
    彭晓芬阅读 242评论 0 3