iOS 11 中 UIBarButtonItem 的变化

随着 iOS 11 和 Xcode 9 的发布,导航栏UINavigationBar 里面的控件 UIBarButtonItem 有一些变化,下面我们一起看看有哪些变化。

变化

  • UIBarButtonItems 的custom view的点击区域更小了
  • 返回按钮的点击区域也更小了
  • 如果上一个屏幕的 title 有变化,则返回按钮的 label 不再自动更新(iOS 10 前会自动更新)
  • UIBarButtonSystemItemFixedSpace 类型的 UIBarButtonItem 现在至少有 8 个点的最小宽度,负数的 width 不再生效
  • 重写 alignmentRectInsets 可能会导致 custom views 超出点击区域

下面会详细的讲解上面几点变化。

点击区域(Tap Areas)

我们假设有一个 iOS 10 程序,其中包含两个有 custom view 的 UIBarButtonItem。然后我们使用 Debug View Hierarchy 去检查 custom 的真实大小。

1.png

他们的点击区域实际上会更大,和下面的红色方块区域大小一致。

2.png

然而在 iOS 11 上,显示的效果和上面的效果就不同了。点击区域大小和 custom view 的大小一致。

3.png

iOS 11 上,这种点击区域的大小的确会使得用户很难点击。为了扩大点击区域,custom view 的需要扩大边距。我们可以创建一个拥有最小的size 约束的 Wrapper view。

class WrapperView: UIView {
    let minimumSize: CGSize = CGSize(width: 44.0, height: 44.0)
    let underlyingView: UIView
    init(underlyingView: UIView) {
        self.underlyingView = underlyingView
        super.init(frame: underlyingView.bounds)

        underlyingView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(underlyingView)

        NSLayoutConstraint.activate([
            underlyingView.leadingAnchor.constraint(equalTo: leadingAnchor),
            underlyingView.trailingAnchor.constraint(equalTo: trailingAnchor),
            underlyingView.topAnchor.constraint(equalTo: topAnchor),
            underlyingView.bottomAnchor.constraint(equalTo: bottomAnchor),
            heightAnchor.constraint(greaterThanOrEqualToConstant: minimumSize.height),
            widthAnchor.constraint(greaterThanOrEqualToConstant: minimumSize.width)
        ])
    }
    // ...
}
4.png

返回按钮(Back Button)

点击区域(Tap Area)

当 view controller 的 title 很长的时候,iOS 11 上的返回按钮点击区域会变得更小。

在 iOS 10 上的返回按钮有一个比较大的最小宽度,利于用户点击。

5.png

然而在 iOS 11 上,返回按钮的点击区域变得很小。更糟糕的是 view controller 的 title,有时看起来像是返回按钮的 label。

6.png

动态更新(Label)

iOS 11 上面另外一个更新是返回按钮的 label。

举个例子,假设有一个能够自动更新 title 的 First(4) 页面 (UIViewController),

7.png

界面从 First(4) 页面 push 到 Second 页面,

8.png

在 iOS 10 上,当 First(4)的 title 从 First(4) 变化到 First(6) 之后,返回按钮也变成了 First(6),

9.png

而在 iOS 11 上,返回按钮的 label 并没有自动更新,而是保持不变。

10.png

自动布局(Auto Layout)

UINavigationBar 现在能够使用自动布局(Auto Layout)来布局它的 subviews(包括 custom views)。在今年的 WWDC上中的 Updating Your Apps for iOS 11 里面有提到。

在 iOS 11 中,我们能够从 Debug View Hierarchy 检查 UINavigationBar 看到,UINavigationBar 内部的 buttons 都被放置在一个 stack view 中。

11.png

当我们正确的实现了 custom view 的 sizeThatFitsintrinsicContentSizeUINavigationBar也会如以前那样工作正常。

但是,需要注意的是,当我们把custom view 的 translatesAutoresizingMaskIntoConstraints 设成 false,在 iOS 10(如果你的 App 还支持 iOS 10的话)上,可能出现一些莫名其妙的问题。比如可能导致custom view 的 top-left 方向布局错位。这种情况下,一个简单的保护代码如下:

let customView = createCustomView()
if #available(iOS 11, *) {
    customView.translatesAutoresizingMaskIntoConstraints = false
}
navigationItem.rightBarButtonItem = UIBarButtonIte(customView: customView)

自定义对齐(Custom Alignment)

很多时候,我们想能够自由的控制 custom UIBarButtonItem 的布局(对齐),特别是和屏幕两边的边距大小。在 iOS 10(及以前)来说,我们经常用到两种小技巧:

  • 使用fixed space item,即UIBarButtonItem(barButtonSystemItem: .fixedSpace …),并且赋值一个负数,比如-15
  • 重写 custom view 的 alignmentRectInsets

但是在 iOS 11 上,上面两个方法都有点变化,第一个办法已经失效,而第二个办法也有点问题。

Fixed Space Items

当我们使用 custom view 的时候, 默认的边距是16个点,

12.png

假设我们想将边距减少到 8 个点,那么在 iOS 10 上面,我们需要设置fixed space UIBarButtonItem 的宽度为 -8 就可以达到我们想要的效果。

13.png
let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
spacer.width = -8
let barButtonsItems = [
    spacer,
    UIBarButtonItem(customView: createCustomButton()),
    UIBarButtonItem(customView: createCustomButton())
]
navigationItem.rightBarButtonItems = barButtonItems
14.png

然而在 iOS 11 上,修改fixed space UIBarButtonItem 的宽度为 -8 会不生效。

Alignment Rect Insets

而重写 custom view 的 alignmentRectInsets,在 iOS 10 上面也能够修改布局边距

class CustomView: UIView {
    var alignmentRectInsetsOverride: UIEdgeInsets?
    override var alignmentRectInsets: UIEdgeInsets {
        return alignmentRectInsetsOverride ?? super.alignmentRectInsets
    }
    // ...
}

设置 alignmentRectInsets 的值为 UIEdgeInsets(top: 0, left: -8, bottom: 0, right: 8),也能够和前面的修改起到相同的作用

15.png

上面的代码在 iOS 11 上面依然能够起到作用,但是有一个小的问题是,item 里面有一小部分超出了 stack view 的范围。超出的区域不能点击,也就意味着点击区域变小了。

16.png

Workaround

根据前面fixed space的例子,我们发现相对于未使用 custom view 的情况,边距有所变小。

17.png

当我们未使用 custom view 的时候,边距到屏幕边缘是 8 个点

18.png

当我们使用了 custom view 的时候,边距到屏幕是 16 个点

19.png

根据上面的发现,我们的目标是:让 custom view 的边距在 iOS 11 上面依然是 8 个点。添加一个 fix space item 将会使得内部的 stack view 减少边距,那么修改 alignmentRectInsets 的值去移动 custom view 的 frame,可以使内部的 stack view 对齐。

func alignedBarButtonItems() -> [UIBarButtonItem] {
    let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
    spacer.width = 8
    let barButtonsItems = [
        spacer,
        UIBarButtonItem(customView: createCustomButton(offset: spacer.width)),
        UIBarButtonItem(customView: createCustomButton(offset: spacer.width)),
        ]
    return barButtonsItems
}

func createCustomButton(offset: CGFloat = 0) -> UIButton {
    let button = CustomButton(frame: CGRect(x:0, y: 0, width: 24, height: 24))
    button.alignmentRectInsetsOverride = UIEdgeInsets(top: 0, left: -offset, bottom: 0, right: offset)
    button.translatesAutoresizingMaskIntoConstraints = false
    return button
}
20.png

目前来说,这个 workaround 只能将边距设置为 8,如果我们将这个值设为一个更小的值,那么custom view 依然会超出 stack view。

参考

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