NanoFrame
通过最小化基于帧的布局和UIKit工具回归基础。(即不使用自动布局的自动布局)。
为什么这样做?
“一切都应该尽可能简单,但不能过于简单”。这句由爱因斯坦提出的格言,不仅适用于解决科学中最深奥的谜题,我们还可以将其作为软件开发中的指南。事实上,“简洁”与“沟通”、“反馈”、“勇气”和“尊重”一样,是极限编程的五大核心价值之一。
为什么不使用自动布局呢?
我们可以使用它,然而首先理解基本原理是有好处的!
在自动布局出现之前,有简单的基于帧的布局方式,自动布局是为了解决不同屏幕尺寸下如何布局视图相对其他视图的问题。然而,使用基于帧的布局相对也不难地拖动视图相对位置。我们需要理解两个基本原理;
UIView
的边界是其自身的坐标空间中的CGRect
frame。坐标原点CGPoint
(0,0)位于左上角。- 一个
UIView
的 frame 是相对于其父视图的 CGRect。我们可以在某个其它 UIView 的 frame 中放置一个UIView
、在坐标 (100, 50)。
注意上面的字 relative。现在我们可以使用 frame 和 bounds 来相对排列视图。但仅仅在 CGRect
阶段的抽象上工作会变得冗长,并且需要大量的繁琐计算。计算机喜欢繁琐的计算,因此我们为 UIView
添加一些扩展函数以执行常规操作。现在我们可以按照以下方式进行布局
override func layoutSubviews() {
super.layoutSubviews()
contentView.frame = bounds
glowView.frame = contentView.bounds.insetBy(dx: 10, dy: 6)
let backgroundColors = [UIColor(hex: 0x424243), UIColor(hex: 0x212121)]
glowView.backgroundColor = UIColor(topToBottom: backgroundColors, inFrame: glowView.frame)
glowView.layer.shadowPath = UIBezierPath(rect: glowView.bounds).cgPath
titleLabel.width = glowView.width - 30
titleLabel.centerVerticallyInSuperView()
titleLabel.x = glowView.x + 10
starButton.centerVerticallyInSuperView()
starButton.right = glowView.right - 20
}
在上面的例子中,我们相对于父视图排列了 titleLabel
和 starButton
,就像我们使用自动布局一样。事实上,如果我们把 x
和 right
成员变量改名为 leading
和 trailing
,它看起来会更加像自动布局。
区别在于我们必须在 layoutSubviews()
函数中这样做(在使用自动布局的情况下,我们可以在任何地方声明约束),但我们可以认为这是一个特性。现在我们知道在哪里查找与布局相关的代码了。
组合
当提及图形时,我们之所以经常遇到“组合”这个词,有一个原因。这种模式在渲染中很有用——从一个根节点开始,然后向外分支。它可以并行处理。因此,与其有一个复杂的祖先等级(DogView
继承自 AnimalView
... 继承自 UIView
),我们不如用简单的组件组合复杂的视图。
上述方法对于视图的组合很有效。如果你看到一段很长的 layoutSubviews()
函数,那么提取可重用子视图将有助于提高重用性。现在你可以相对于父视图布局这个子视图。并且变得简单易推理。
你的顶级视图的模型可以处理在应用 领域的 模型,因此如果例如你有顶级的天气报告视图,你可以向该视图提供一个天气报告模型。
动画
现在我们知道了如何使用组合来相对排列视图,我们也可以对它们进行动画处理。
CATransaction.flush()
UIView.animate(withDuration: 0.28, delay: 0, options: [.curveEaseIn], animations: { [self] in
titleLabel.x = starButton.x - 30
})
我们使用了一个内置动画曲线。然而,我们很容易将牛顿物理学的函数应用于近似重力、摩擦等因素。