iOS 9 by Tutorials 笔记(七)

Chapter 7:UIStackView & AutoLayout changes

从 iOS 8 开始,Apple 就一直改进 AutoLayout 的体验,iOS 9 UIKit 团队推出了 UIStackView,让自动化布局变得更加简单

Stack View 提供了一些垂直和水平方向上的布局方式,通过设置这些属性如 alignment,distribution,spacing 你可以定义这些 contained views 之间的距离

stack view 可看作是一个容器,包含着需要布局的 views

现在来玩第一个 Stack View

选中这三个 view,然后将他们加入 Stack View

将 view 加入 stackView 会将原有的约束全部清除掉,因此我们需要重新添加约束,不过这一次是在 stackView 上添加

Top: 20, Leading: 0, Trailing: 0, Bottom: 0  

解决了外部,再回到 stack view 内部,内部 view 之前的间距太近了,我们来添加一些 Spacing,方法如下:

照例,运行看下效果:

Stack views are just better

如果你有一堆 views,相互之间都设好约束。如果其中一个 view 被隐藏掉,为了保证显示效果,你需要对约束做一些调整。而现在有了 stack view,你只需要将 view 的 hidden 设为 true,隐藏掉就好了,其余的事情交给 stack view 来处理。

除了在 Auto Layout 上的改进,iOS 9 还放出了两个新玩意: layout anchorslayout guides,分别来自 NSLayoutAnchor 类和 UILayoutGuide

Layout anchors

Layout anchors 提供了一种直观的方式来创建约束,想象下你有两个 labels,bottomLabeltopLabel。你想让 bottomLabel 位于 topLabel 的下方,且二者直接的距离为 8 points。在 iOS 9 之前,你可能要这么写:

let constraint = NSLayoutConstraint(  
  item: topLabel,
  attribute: .Bottom,
  relatedBy: .Equal,
  toItem: bottomLabel,
  attribute: .Top,
  multiplier: 1,
  constant: 8
)

而有了 Layout anchors 允许我们这么做

let constraint = topLabel.bottomAnchor.constraintEqualToAnchor(  
    bottomLabel.topAnchor, constant: 8)

constraintEqualToAnchor:constant: 方法属于类 NSLayoutAnchor 的实例方法,这个类还有几个类似的方法:

- constraintEqualToAnchor:
- constraintGreaterThanOrEqualToAnchor:
- constraintGreaterThanOrEqualToAnchor:constant:
- constraintLessThanOrEqualToAnchor:
- constraintLessThanOrEqualToAnchor:constant:

NSLayoutAnchor 主要优势在于你不用直接创建 NSLayoutConstraint了,而是先选择你要添加约束的对象包括(UIView、NSView、UILayoutGuide),然后用这些对象的 anchor 属性通过上面的方法来构建你的约束

再回到上面的例子中,现在 view 有 layout anchor 对象(bottomAnchor)来表示 .Bottom attribute 了,而 bottomAnchor 属于 NSLayoutYAxisAnchor,继承自 NSLayoutAnchor

NSLayoutAnchor 有四个子类以及分别对应了如下 anchor

  • NSLayoutDimension
    • heightAnchor
    • widthAnchor
  • NSLayoutXAxisAnchor
    • centerXAnchor
    • leadingAnchor
    • leftAnchor
    • rightAnchor
    • trailingAnchor
  • NSLayoutYAxisAnchor
    • bottomAnchor
    • centerYAnchor
    • topAnchor

如果你查看文档,会发现 NSLayoutDimension 多了一些方法,增加了 multiplierConstant

func constraintEqualToConstant(_:)  
func constraintEqualToAnchor(_:multiplier:)  
func constraintEqualToAnchor(_:multiplier:constant:)

func constraint[Less|Greater]ThanOrEqualToConstant(_:)  
func constraint[Less|Greater]ThanOrEqualToAnchor(_:multiplier:)  
func constraint[Less|Greater]ThanOrEqualToAnchor(  
  _:multiplier:constant:)

heightAnchor 和 widthAnchor 都是属于 NSLayoutDimension,而文档的意思只有涉及这两种才会用到 multiplier

还有注意的一点就是使用这些 NSLayoutAnchor 构建约束时,类型必须一致。即 constraint[Equal|LessThanOrEqual|GreaterThanOrEqual]ToAnchor 这些方法的两个对象的 anchor 类型必须一致。

  • NSLayoutDimension 只能和 NSLayoutDimension
  • NSLayoutXAxisAnchor 只能和 NSLayoutXAxisAnchor
  • NSLayoutYAxisAnchor 只能和 NSLayoutYAxisAnchor

不一致 OC 会警告

而 Swift 会直接报错

Layout guides

layout guide 解决了你之前用 dummy view 才能解决的布局问题,将 layout guide 想象成一个隐形的矩形或 view 层级上的框架,你可以利用矩形的边缘来布局,就和你之前加个 dummy view 用法完全一样,你可以在上面添加约束什么的。

这样做的好处就是轻量,没有副作用,之前 dummy view 虽然也是隐形的,但毕竟在 view 层级结构中,还是要参与消息传递过程。

UILayoutGuide 定义的这个矩形区域也能很好的与 Auto Layout 交互

下面来解决一个布局问题

红框圈出的 cell 文字没有居中,原因也很简单,设置了 label 的 top constant 为固定值(15),这个 value 在单行文字正常,多行就有问题了。

在 iOS 9 之前,你可能会创建一个 dummy container view 来居中,现在则完全可以用 layout guide 来做

目前 layout guide 还只能通过代码添加,在 awakeFromNib(): 中添加如下代码:

// 1 为 cell 的 contentView 添加 layoutGuide
let layoutGuide = UILayoutGuide()  
contentView.addLayoutGuide(layoutGuide)  
// 2
let topConstraint = layoutGuide.topAnchor  
  .constraintEqualToAnchor(nameLabel.topAnchor)
// 3
let bottomConstraint = layoutGuide.bottomAnchor  
  .constraintEqualToAnchor(locationNameLabel.bottomAnchor)
// 4
let centeringConstraint = layoutGuide.centerYAnchor  
  .constraintEqualToAnchor(contentView.centerYAnchor)
// 5
NSLayoutConstraint.activateConstraints(  
  [topConstraint, bottomConstraint, centeringConstraint])

首先为 cell 的 contentView 添加 layoutGuide,其实就相当于给 cell 的 contentView 加了一个隐形的矩形框(尺寸等于 contentView 的 frame),接着利用 layoutGuide 的 Anchor 对象创建需要的约束,最后一步激活这些约束。

activateConstraints(_:) 是从 iOS 8 开始,苹果推荐使用的

再次运行,居中问题似乎解决了

不过仔细一看,之前 cell 的内容确实居中了,不过 下面的 locationNameLabel 被压扁了。还记得之前提到过『label 的 top constant 被设为固定值(15)』吗?我们现在已经通过 layout guide 设置了居中,不再需要这个 top 约束了。

直接在 Xcode 中删除的话,会报错缺失约束,这是因为我们通过 layout guide 添加的那些约束只有在运行时才会生效,所以暂时还不能删掉这个 top 约束,还好,Xcode 已经提供了解决方案,在 top 约束Placeholder 上打上勾即可。

这样,top 约束 会在运行时移除,Xcode 也不会报错了

最后运行,一切 OK


-EOF-

如果感觉此文对你有帮助,请随意打赏支持作者 😘

chengway

认清生活真相之后依然热爱它!

Subscribe to Talk is cheap, Show me the world!

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!