BoxView-适用于iOS的便捷自动布局

我想共享一个库,用于基于自动布局为iOS应用程序有效地构建用户界面。



尽管随着SwiftUI的到来,自动布局的相关性正在迅速减少,而这种机制仍在积极使用,并且该库对于直接在代码中创建(或更改)UI的人员可能有用。



这种构建接口的方法具有许多限制其使用的缺点:



  • 非常不方便地组织了NSLayoutConstraint元素的创建。
  • 可见性差-查看代码很难理解UI的外观。
  • 大量的例程代码。放置每个视图需要平均创建约3个约束。三行相同类型的代码。
  • 创建动态更改接口的工作量很大:需要将约束保存在单独的变量中,以便以后可以更改它们,并且经常会创建冗余约束并“关闭”不必要的约束。


通过将用于创建约束的标准方法包装在更人性化的内容中,可以轻松解决第一个问题。这已经很好地实现了,例如在SnapKitTinyConstraints和其他类似的库中。



但是您仍然必须编写许多相同类型的代码,并且在可见性和布局动态更改方面仍然存在问题。 UIStackView很好地解决了这些问题,但是不幸的是,在UIStackView中,单个元素的布局非常有限。因此,提出了一个容器UIView的想法,它管理其子视图堆栈的布局,但具有分别配置每个子视图位置的能力。

BoxView是这种方法的基础,并且事实证明它是非常有效的。 BoxView允许您几乎完全消除手动创建的约束,几乎整个用户界面都是由嵌套BoxViews系统组成的。结果,代码变得更短,更可见,对于动态UI而言,其增益尤为明显。



BoxView在许多方面与标准UIStackView相似,但是它使用不同的规则放置子视图:在其中可以分别为每个子视图设置缩进和大小。要创建布局,BoxView使用BoxItem类型的元素数组,其中包含需要显示的所有视图以及有关如何排列它们的信息。而且这根本不需要太多代码-默认情况下大多数布局参数都是采用的,并且仅明确指定必要的值。



BoxView的基本属性是,它仅为添加的子视图创建指定的约束,而没有其他设置。因此,可以与其他任何库和布局方法一起使用而不受任何限制。



作为示例,考虑使用BoxView创建一个简单的登录表单(完整的示例代码及其逐步说明可在github上BoxViewExample项目中找到)。



图片


几行代码足以在BoxView上创建这样的布局:



        nameBoxView.items = [nameImageView.boxed.centerY(), nameField.boxed]
        passwordBoxView.items = [passwordImageView.boxed.centerY(), passwordField.boxed]
        boxView.insets = .all(16.0)
        boxView.spacing = 20.0
        boxView.items = [
            titleLabel.boxed.centerX(padding: 30.0).bottom(20.0),
            nameBoxView.boxed,
            passwordBoxView.boxed,
            forgotButton.boxed.left(>=0.0),
            loginButton.boxed.top(30.0).left(50.0).right(50.0),
        ]


BoxItem是使用框式变量从任何UIView创建的,之后可以将其设置为4边距,对齐方式以及绝对或相对尺寸的填充。



可以自由添加和删除任何布局元素(包括动画),而不会影响其余元素的位置。例如,我们添加了对空输入字段的检查,如果发生错误,我们将直接在空字段下方显示消息:



图片


而且,尽管消息应该“嵌入”到现有布局中,但它甚至不需要更改现有代码!



    func showErrorForField(_ field: UITextField) {
        errorLabel.frame = field.convert(field.bounds, to: boxView)
        let item = errorLabel.boxed.top(-boxView.spacing).left(errorLabel.frame.minX - boxView.insets.left)
        boxView.insertItem(item, after: field.superview, z: .back)
        boxView.animateChangesWithDurations(0.3)
    }
    
    @objc func onClickButton(sender: UIButton) {
        for field in [nameField, passwordField] {
            if field.text?.isEmpty ?? true {
                showErrorForField(field)
                return
            }
        }
        // ok, can proceed with login
    }
    
    @objc func onChangeTextField(sender: UITextField) {
        errorLabel.removeFromSuperview()
        boxView.animateChangesWithDurations(0.3)
    }


BoxView支持所有自动布局工具:元素之间的距离,绝对和相对大小,优先级以及对RTL语言的支持。除了UIView,您还可以使用不可见的对象UILayoutGuides作为布局元素。也可以使用Flex布局。当然,布局方案本身(以UIView的嵌套堆栈系统的形式)并不能100%涵盖元素的相对排列的所有可能选项,但这不是必需的。这对于绝大多数典型的用户界面来说是很好的,对于更特殊的情况,您可以随时以任何其他方式添加相应的附加约束。该库中还包含几种实用方法,例如,用于创建宽高比约束。



另一个小例子在github上可用的代码(〜100行代码!)说明了嵌套BoxView系统与其他约束设置方法以及BoxView参数的动画更改的结合使用。



图片


GitHub上的BoxView项目



All Articles