Auto Layout

iOS的主动布局,多少有点陌生了,平常也都是用三方库比较多,今天就看一下Apple在布局上的一些要害API,看能不能到达彻底运用原生API完结这个工作。

iOS6年代(VFL)

Auto Layout 是Apple在iOS6引进的一个技能,也让我们在frame之外有了一个对UI布局多了个新的视角。尽管它刚出来的时分也不好用,乃至没啥人在用,这也是有这么多Auto Layout三方库出现的原因。回到过去,看一下它的具体运用

let contentView = UILabel()
contentView.backgroundColor = .orange
contentView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(contentView)
let views: [String: Any] = [
    "contentView": contentView
]
let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|-20-[contentView]-20-|", metrics: nil, views: views)
let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[contentView]-20-|", metrics: nil, views: views)
view.addConstraints(verticalConstraints)
view.addConstraints(horizontalConstraints)

与我现在对Layout的认知比较,比较跳脱的点有这几个

  • 需求一个数组记载有哪些subviewlet views: [String: Any] = [ "contentView": contentView ]
  • 布局代码的语法,也便是所谓的VFL,写法真的是……
  • 界说的束缚verticalConstraints和horizontalConstraints是增加到整个superView上的,可能是在创立的时分传入了一切subview的数组,内部有个映射关系

NSLayoutConstraint APIs

应该是Apple也看到了VFL没有推广下去,在NSLayoutConstraint上供给了一套更友爱的API来创立束缚

let leadingConstraint = NSLayoutConstraint(item: contentView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 40)
let bottomConstraint = NSLayoutConstraint(item: contentView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: -20)
let topConstraint = NSLayoutConstraint(item: contentView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: 20)
let trailingConstraint = NSLayoutConstraint(item: contentView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: -20)
view.addConstraints([
    leadingConstraint,
    trailingConstraint,
    topConstraint,
    bottomConstraint
])

这个API和写法,能处理一切的布局束缚,唯一难过的便是写起来比较繁琐,尽管API界说很清楚,但这样的重复再重复也是很容易出错的。

iOS8年代(active state)

在iOS8中,Apple对NSLayoutConstraint增加了active state的特点,只有active的constraint才会在终究的Layout计算中收效,能够enable或许disable一个constraint,而不用像之前要remove或许re-add一个constraint。

NSLayoutConstraint()创立出来的constraint目标默认active是false的,你能够单独设置它的active state,比方leadingConstraint.isActive = true 。当然假如它被addConstraints里了,也会被置为active。

可是整体上的写法仍是没有太多改进,仍是用addConstraints增加一切的束缚目标,假如需求布局的元素多起来,一切的布局目标都增加到view身上,我乃至不确定是在给哪个subview或许该怎么给指定的subview增加束缚。

你收到过 Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Impossible to set up layout with view hierarchy unprepared for constraint.'这种过错吗,头不头疼,根本便是由于给过错的父目标增加束缚。为了缓解这个问题,iOS8中,Apple给出了active(_:) api。

active(_:)

会主动将束缚增加到正确的view上,马不停蹄的删了一切的addConstraints,取而代之是的NSLayoutConstraint.activate

NSLayoutConstraint.activate([
    leadingConstraint,
    trailingConstraint,
    topConstraint,
    bottomConstraint
])

相对应的,能够运用deactivate(_:) 取代之前的removeConstraint(_:) (这种看起来就不是很聪明的API)。运用active API,根本在Layout的工作上就很少有crash的问题了,除非是你运用之前,没有将constraint目标增加到view上,那你可能会收到类似 Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors <NSLayoutXAxisAnchor:0x600002cb44c0 "UIView:0x7f803d407450.leading"> and <NSLayoutXAxisAnchor:0x600002ca4200 "UIView:0x7f803d705d40.leading"> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.' 的crash信息

iOS9年代 Layout Anchors

NSLayoutConstraint.activate([
    contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
    contentView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),
    contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
    contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20)
])

这个版本的束缚写法根本跟我认知中的Layout该有的姿态比较像了

  • 运用NSLayoutConstraint.active,直接把束缚目标增加到指定的subview上,而不是addConstraints增加到super View上,这避免了给过错的super view增加束缚造成的crash
  • Anchors的运用,能够理解为是subview(要增加束缚的目标)上的特定点,创立束缚的时分将其作为参考点,创立constraint的写法也变得简单高效。

NSLayoutAnchor创立constraint时,有一整套的面向目标的API可用

NSLayoutConstraint:Apple主动布局的发展史

相对于运用NSLayoutConstraint会愈加直观,比方将view的leading与另一个view的leading对齐

NSLayoutAnchor: view1.leadingAnchor.constraint(equalTo: view2.leadingAnchor)

NSLayoutConstraint: NSLayoutConstraint(item: view1, attribute: .leading, relatedBy: .equal, toItem: view2, attribute: .leading, multiplier: 1.0, constant: 0.0)

到这一步其实已经很接近Snapkit的写法了,所以在项目中,你会直接运用这些Apple自带的Layout API吗