关于刘海打理这种事儿,美团点评的iOS工程师早就有经验了,不信你看!

背景

iPhone X 刘海机于9月13日发布,给科技小春晚带来一波高潮。作为开发人员却多出来一份忧虑,iPhone X 怎么适配?我们 App 的脑袋会不会也长一刘海出来?Tabbar 会不会被圆角?先来看一下美团 App 的表现:

图1.1 启动时的 App 表现

图1.2 下拉刷新之后的表现

图1.3 搜索的表现

图1.4 “我的Tab”表现

在图1.1中乍一看表现还不错,可是在图1.2中,下拉刷新之后,我们的导航栏还是被刘海挡住了。搜索也中枪,搜索首页没有办法取消,“热门搜索区域”也多出来一块儿空白。另外,“我的Tab”页部分如图1.3、图1.4所示,导航栏回不去了,右上角的三个UIBarButtonItem也不见了。其他还有很多UI上的Bug,等着我们去一一发现并修改。

针对可能出现的问题,苹果在 developer.apple.com 上给出了一些建议。其中一个是 HIG (Human Interface Guideline)。另外 WWDC 会议官方 App 的作者,也给出了适配时的一些经验

我们来看看他们是怎么说的。

HIG部分

首先看一下各个机型尺寸的变化。

图2.1 各版本 iPhone 的尺寸

下图是 iPhone X 对比其他机型的变化部分。iPhone X 和 iPhone 8 的宽度一致,在垂直方向上多了145pt,这就意味着首页可以展示更多的内容,多出来的这20%的垂直空间,也许可以挂上更高价值的运营位。

图2.2 iPhone X 和其他设备的尺寸对比

布局

注意图2.2蓝色部分,你会发现这些都算在了展示内容的区域。所以我们在设计的时候,要避免内容被圆角、刘海给挡住。Like this:

图2.3 CGRectMake(0,0,100,100)

iPhone X 的坐标系统以及能显示内容的区域如下图所示:

图2.4 iPhone X 的显示区域

Status Bar

iPhone X 上的 StatusBar 高度比之前的 iPhone 高一些,也就是说,我们如果写死20pt高度的 frame 布局,都要大面积修(tu)改(xue)。在 iPhone X 上,通过打印 [[UIApplication sharedApplication] statusBarFrame] 可以看到,高度是44pt。

图2.5 iPhone X 的状态栏高度

"如果你的 App 是隐藏 StatusBar 的,建议重新考虑。iPhone X 为用户在垂直空间上提供了更多展示余地,且状态栏中也包含了用户需要知道的信息,除非能通过隐藏状态栏带给用户额外的价值,否则苹果建议大家将状态栏还给用户。"

另外还有一点,用户在使用 iPhone X 打电话的时候,StatusBar 的高度也不会发生变化了。

屏幕底部

因为没有了 Home 键,iPhone X 的底部是预留给系统功能的一个区域 - Home Indicator,这部分的高度是34pt。

图2.6 iPhone X 的 Home Indicator 区域

“如果你的底部是 TabBar,那么 Home Indicator 背景会来自于 TabBar 背景的延伸,如果我们是一个 feed 流的页面,那么底部会展示 feed 流的局部。”

意思是如果有 TabBar,那么那个区域会延展你的 barTintColor;没有的话,就显示透明的(参照 Setting)。之所以这么设计,是为了让 indicator 清晰可见,告诉用户你可以滑动这部分区域。所以苹果不建议我们的 UI 元素过于靠近这部分区域。

图2.7 有 TabBar 的 Home Indicator 区

SafeArea

iOS 11 废弃了 iOS 7 之后出现的 topLayoutGuide/bottomLayoutGuide,取而代之的是safeLayoutGuide 概念。我们的UI元素都应该布局在这些区域之内,避免被各种 bar(NavgationBar、ToolBar、TabBar、StatusBar)遮挡。

图2.8 iPhone 的 SafeArea

如果我们用了 AutoLayout,并且开启了 safeAreaLayoutGuide,布局会自动加上这些 safeLayoutGuide,你的视图不会超出这部分 SafeArea。如2.9所示,如果你需要增加 Guide 的区域,那么可以设置 self.additionalSafeAreaInsets 来增加区域。

图2.9 默认的 SafeArea 和 self.additionalSafeAreaInsets = UIEdgeInsetsMake(64, 0, 0, 0);

其他

还有其他的一些改变,比如图片的 Aspect Ratio 在 iPhone X 上的表现也会有所不同了;

刘海两边的区域都能响应不同的手势,最好不要和自己的 App 发生冲突。

来自Session 201的建议

① xib 里适配 iPhone X 的话,可以开启 UseSafeAreaLayoutGuides(但是这个是需要在 iOS 9 之后才能用,需要看你的 App 最低支持的版本)。

图3.1 xib 属性

② 如果用的系统 SearchViewController,发现没有灰色蒙层了,可以这么试试。

图3.2 iOS 11 UISearchViewController适配

之所以可以这么改,是因为 iOS 11 的 NavigationBar 和 SearchViewController 集成在一块儿了。

③ 横屏下的 UITableView,SenctionHeader 的背景颜色不是设置的那个颜色。

图3.3 iOS 11 横屏 Tableview 的作用方式

这个问题的原因是:横屏下的 UITableView,Cell 都是和屏幕一样宽,但是 Cell 的 ContentView 会被 inset 到 SafeArea 区域。

解决方法是:可以通过调整 Tableview 的默认行为,改变 contentView 的属性(如上图 inset To SafeArea)来让 contentview 顶到边缘,弊端是会改变整个 cell 的内容显示,而且 contentView 的 layoutMargin 依然还是相对于 SafeArea 的。

最佳方案是:改变 UITableViewHeaderFooterView.backgroundView 的 backgroundColor。

图3.4 iOS 11 修改前后的样式对比

刘海打理初体验

① 我们来看下开头说的那个刷新之后首页顶上去的问题怎么处理。经过排查,这个问题属于“状态栏变高系列”,解决方案就是把固定的20pt高度改成 [[UIApplication sharedApplication] statusBarFrame].size.height]。

② 搜索页面输入框的位置发生了偏移,这是因为 iOS 11 的导航栏的视图层级结构发生了变化,和 iPhone X 的并无直接关系。iOS 11 导航栏的视图层级关系如下:

图4.1 iOS 11 之后的 NavigationBar

图4.2 iOS 11 之前的 NavigationBar

适配方式是:取到这个 _UIButtonBarStackView 的位置和尺寸信息,然后更改 PFBNavigationBarContainerView 的 X 坐标。

③ “我的Tab” 页面多出来一块儿灰色的区域,经过排查发现这个是 Tableview 的背景色。也就是说其实是 Tableview 向下偏移了。

图4.3 iOS11 下“我的Tab” 页面 Tableview 发生偏移

出现这个的原因是:iOS 11 之后 scrollview 多出来一个 adjustedContentInset 区域。

图4.4 iOS 11下 ScrollView 的新属性

通过打印这个值,我们发现正好和 contentoffset.y 相符合。

图4.5 这个新属性在 iPhone X 上的值

那为什么会发生偏移?这个偏移的值又是怎么确定的?其实是当 Tableview 的 frame 超出了 safeArea 范围之后,系统会调整内容的位置。系统通过设置 adjustedContentInset 为 safeAreaInset 的值让 Tableview 偏移。

图4.6 iPhone X 上 safeAreaInset 的值

注意一下这个 adjustedContentInset 是 readOnly 的属性。我们可以通过设置 Tableview.contentInsetAdjustmentBehavior=UIScrollViewContentInsetAdjustmentNever 来纠正这个位置。当然还可以通过设置 tableview.contentOffset 来抵消这个值,但还是推荐第一种。

④ “我的Tab” 导航栏上,右边那个按钮全都发生了偏移,导致无法点击。这个问题也是在新的导航栏结构视图下会出现,原因是新的导航栏结构用了 AutoLayout 布局,我们这个并不是用常规的 UIBarButtonItem 方式实现的,而是一个 UIBarButtonItem ,他的 customView 包含了三个 Button,这几个 Button 都是 frame 布局,从而导致了在 AutoLayout 下的布局问题。

正常的解决方式是:修改成一个一个添加 UIBarButtonItem 和 UIBarButtonSystemItemFixedSpace。但是这样引出来另外一个问题,iOS 11 之前那种设置负宽度的 fixedspace 来调整间距的 trick 方式已经失效了!详情见https://forums.developer.apple.com/thread/80075

我们这边的方式是:依然用那种一个 CustomView 里包含三个 CustomButton 的方式,然后分别加上约束。CustomView 只需要加上宽高,包含的 Button 加上 left、top 和 size。

图4.7 加约束修正后样式

以下是尝试修复这部分问题的代码:

// offset 问题
if (@available(iOS 11.0, *)) {
        self.contentViewController.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    }
// UIBarButtonItem 问题
if (@available(iOS 11.0, *)) {
        [messageButtonsContainerView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.size.mas_equalTo(CGSizeMake(themeButton.width + settingButton.width + messageButton.width, 44));
        }];
        [themeButton mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(messageButtonsContainerView);
            make.left.equalTo(messageButtonsContainerView);
            make.size.mas_offset(CGSizeMake(44, 44));
        }];
        [settingButton mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(messageButtonsContainerView);
            make.left.equalTo(themeButton.mas_right);
            make.size.mas_offset(CGSizeMake(44, 44));
        }];
        [messageButton mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(messageButtonsContainerView);
            make.left.equalTo(settingButton.mas_right).offset(-10);
            make.size.mas_offset(CGSizeMake(44, 44));
        }];
    }

    UIBarButtonItem *rightBarItem = [[UIBarButtonItem alloc] initWithCustomView:messageButtonsContainerView];
    self.navigationItem.rightBarButtonItems = @[rightBarItem];

总结

当前发现这些问题的环境是 Xcode 9 GM版本(9A235)的模拟器。归结起来是三类问题:

  1. StatusBar 变高并且绝对布局。
  2. 导航栏的视图层级结构发生变化而导致 UI(titleView、UIBarButtonItem) 问题。(iPhone 6s iOS 11 上依然是旧的结构,是因为现在 AppStore 上的包依然是用 iOS 10 的 SDK 打出来的)。
  3. safeAreaInset 导致 Scrollview 偏移。

至于 Tabbar ,因为我们用的是系统的,所以目前并没有发现什么奇怪的地方。希望我们踩的这些坑可以让各位在适配的过程中少走一些弯路!

原文发布于微信公众号 - 美团点评技术团队(meituantech)

原文发表时间:2017-09-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我杨某人的青春满是悔恨

开源项目——『看知乎』iOS 版

前段时间无意中发现了看知乎,一个知乎答案和用户的精选站。网站开发者是知乎用户苏莉安,他写了个爬虫从知乎抓取数据,而且还提供了 API 文档。我大致看了下文档,感...

1245
来自专栏IMWeb前端团队

html结构的拆与合

写下这篇标题的时候,脑海中突然闪过《三国演义》的纲领——天下大势分久必合合久必分。也许html结构也如是,总是在拆拆合合中演绎着一段段神奇的故事。 玩过七巧板的...

1829
来自专栏Coco的专栏

【技巧篇】解决悬浮的<header>、<footer>遮挡内容的处理技巧

1435
来自专栏程序员互动联盟

你所不知道的html5与html中的那些事(四)——文本标签

文章简介: 关于html5相信大家早已经耳熟能详,但是他真正的意义在具体的开发中会有什么作用呢?相对于html,他又有怎样的新的定义与新理念在里面呢?为什么...

3399
来自专栏娱乐心理测试

Ios常用第三方框架(一)

1043
来自专栏向治洪

React 动画框架简介

由于 React 加持了虚拟 DOM 等诸多特性,所以在 React 上实现常规的动画效果有一些特别之处。本文不会深入探讨 React 对动画的处理逻辑,只会简...

2387
来自专栏娱乐心理测试

Ios常用第三方动画框架(三)

603
来自专栏向治洪

React 动画框架简介

由于 React 加持了虚拟 DOM 等诸多特性,所以在 React 上实现常规的动画效果有一些特别之处。本文不会深入探讨 React 对动画的处理逻辑,只会简...

2507
来自专栏HTML5学堂

pageResponse - 移动端适配框架

HTML5学堂:移动端布局一直都是一个问题,各种各样的分辨率使得移动端的布局需要耗费大量的时间。今天介绍一个框架pageResponse.js可以让页面在各种分...

2776
来自专栏程序员的知识天地

javascript对点击事件和拖动事件的区分

1.项目中,为了更好的服务用户,经常会设计一个便捷的通道,这个通道一般都是“悬浮”的。

1203

扫码关注云+社区