快速将约束分配给视图的最简单方法?

Posted

技术标签:

【中文标题】快速将约束分配给视图的最简单方法?【英文标题】:Easiest way for assigning constraints to a view in swift? 【发布时间】:2017-12-19 17:10:21 【问题描述】:

我使用以下方法将视图添加为子视图并以编程方式添加其约束。 这就是我创建视图的方式:

// In class
let view: UIView = 
    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
()

这就是我添加约束的方式:

addSubview(view)

view.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
view.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
view.widthAnchor.constraint(equalToConstant: 100).isActive = true
view.heightAnchor.constraint(equalToConstant: 100).isActive = true

有没有什么方法可以让我更容易或用更少的代码行来实现这一点。我需要以编程方式创建约束,我不建议仅为此目的使用其他库。

【问题讨论】:

目前我创建了一个名为“AutoLayoutProxy”的 cocoapod 库,我在所有项目中都在使用它。 【参考方案1】:

不是代码行少,而是.isActive = true逐行,我觉得activate更简洁一些:

NSLayoutConstraint.activate([
    view.leadingAnchor.constraint(equalTo: leadingAnchor),
    view.trailingAnchor.constraint(equalTo: trailingAnchor),
    view.widthAnchor.constraint(equalToConstant: 100),
    view.heightAnchor.constraint(equalToConstant: 100)
])

或者,如果您经常这样做,请编写自己的扩展:

extension UIView 
    func activate(leading: NSLayoutAnchor<NSLayoutXAxisAnchor>? = nil,
                  trailing: NSLayoutAnchor<NSLayoutXAxisAnchor>? = nil,
                  top: NSLayoutAnchor<NSLayoutYAxisAnchor>? = nil,
                  bottom: NSLayoutAnchor<NSLayoutYAxisAnchor>? = nil,
                  centerX: NSLayoutAnchor<NSLayoutXAxisAnchor>? = nil,
                  centerY: NSLayoutAnchor<NSLayoutYAxisAnchor>? = nil,
                  width: CGFloat? = nil,
                  height: CGFloat? = nil) 
        translatesAutoresizingMaskIntoConstraints = false

        if let leading = leading    leadingAnchor.constraint(equalTo: leading).isActive = true 
        if let trailing = trailing  trailingAnchor.constraint(equalTo: trailing).isActive = true 
        if let top = top            topAnchor.constraint(equalTo: top).isActive = true 
        if let bottom = bottom      bottomAnchor.constraint(equalTo: bottom).isActive = true 
        if let centerX = centerX    centerXAnchor.constraint(equalTo: centerX).isActive = true 
        if let centerY = centerY    centerYAnchor.constraint(equalTo: centerY).isActive = true 
        if let width = width        widthAnchor.constraint(equalToConstant: width).isActive = true 
        if let height = height      heightAnchor.constraint(equalToConstant: height).isActive = true 
    

然后一行代码就可以搞定:

view.activate(leading: leadingAnchor, trailing: trailingAnchor, width: 100, height: 100)

【讨论】:

【参考方案2】:

您可以使用视觉格式语言。这样一来,您可以获得简洁性,但对于不了解语法的开发人员来说会失去清晰度。

如果您想使用 VFL 为视图设置上述约束,代码如下。

var constraints = [NSLayoutConstraint]()
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[view(100)]|", options: [], metrics: nil, views: ["view":view])
constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:[view(100)]", options: [], metrics: nil, views: ["view":view])
NSLayoutConstraint.activate(constraints)

如您所见,对于这么简单的一组约束,您并没有获得太多收益。但是,考虑到您必须构建一个复杂的 UI,对不同的对象有许多约束,所有这些约束都相互关联,VFL 将使这样做更容易和更简洁。

VFL 是一种非常喜欢或讨厌它的东西,但我建议您阅读它,以便您做出明智的决定。 ThisRayWendelich 指南特别有用。

【讨论】:

options 的默认值为[],因此您可以从上述调用中省略options: [] 我过去使用过视觉约束。但是这些在旋转时不会保留这些约束。所以,我停止使用这些。 @BibinJacob - 你过去的尝试一定有其他问题,因为当设备旋转时,通过 VFL 创建的约束绝对会得到尊重。 VFL 只是一种用于创建约束的方便语法,但不会更改约束的内容。在我看来,VFL 语法的主要缺点是你(还)不能表示某些类型的约束,例如乘数、centerX/centerY 等。但在 VFL 足够的情况下,它可能非常方便。我不会因为你过去遇到的那些问题而忽略它。【参考方案3】:

我写了一些我认为可以帮助所有开发者的扩展。

extension UIView 

    func addView(view: UIView, top: NSLayoutYAxisAnchor?, leading: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, trailing: NSLayoutXAxisAnchor?, padding: UIEdgeInsets? = .zero, width: CGFloat?, height: CGFloat?) 

        view.translatesAutoresizingMaskIntoConstraints = false

        if !self.subviews.contains(view) 
            addSubview(view)
        

        if let top = top 
            view.topAnchor.constraint(equalTo: top, constant: padding?.top ?? 0).isActive = true
        
        if let leading = left 
            view.leadingAnchor.constraint(equalTo: leading, constant: padding?.left ?? 0).isActive = true
        
        if let bottom = bottom 
            view.bottomAnchor.constraint(equalTo: bottom, constant: -(padding?.bottom ?? 0)).isActive = true
        
        if let trailing = right 
            view.trailingAnchor.constraint(equalTo: trailing, constant: -(padding?.right ?? 0)).isActive = true
        
        if let width = width, width != 0 
            view.widthAnchor.constraint(equalToConstant: width).isActive = true
        
        if let height = height, height != 0 
            view.heightAnchor.constraint(equalToConstant: height).isActive = true
        
    

    func anchorSizeTo(width: NSLayoutDimension?,and height: NSLayoutDimension?) 
        if let width = width 
            self.widthAnchor.constraint(equalTo: width).isActive = true
        
        if let height = height 
            self.heightAnchor.constraint(equalTo: height).isActive = true
        
    

    func anchorCenterTo(x: NSLayoutXAxisAnchor?, y: NSLayoutYAxisAnchor?,with padding: CGPoint = .zero) 
        if let anchor = x 
            self.centerXAnchor.constraint(equalTo: anchor, constant: padding.x).isActive = true
        
        if let anchor = y 
            self.centerYAnchor.constraint(equalTo: anchor, constant: padding.y).isActive = true
        
    

用法:

superView.addView(view: subView, top: upperView.bottomAnchor, leading: leftView.trailingAnchor, bottom: superView.bottom, trailing: rightView.trailingAnchor, padding: UIEdgeInsetsMake(12, 12, 12, 12), width: 0, height: nil)

【讨论】:

【参考方案4】:

NSLayoutAnchor 类使编写 AutoLayout 代码变得更加容易,但它仍然冗长且重复。您可以在 UIView 上创建扩展,并在 AutoLayout 周围添加可用于所有 UIViewController 的包装器。例如,我在下面添加了尺寸和固定边缘的方法

extension UIView 

  func size(width: CGFloat, height: CGFloat) 
    NSLayoutConstraint.activate([
        self.widthAnchor.constraint(equalToConstant: width),
        self.heightAnchor.constraint(equalToConstant: height)
    ])
  

  func edges(_ edges: UIRectEdge, to view: UIView, offset: UIEdgeInsets) 
    if edges.contains(.top) || edges.contains(.all) 
        self.topAnchor.constraint(equalTo: view.topAnchor, constant: offset.top).isActive = true
    

    if edges.contains(.bottom) || edges.contains(.all) 
        self.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: offset.bottom).isActive = true
    

    if edges.contains(.left) || edges.contains(.all) 
        self.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: offset.left).isActive = true
    

    if edges.contains(.right) || edges.contains(.all) 
        self.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: offset.right).isActive = true
    
  

现在您只需 2 行即可为您的视图设置约束

view.edges([.left, .right], to: self.view, offset: .zero)
view.size(width: 100, height: 100)

【讨论】:

我喜欢这个扩展可以给你正确的答案。因为你真的减少了重复。但是,在某些情况下,我只需要宽度和高度的约束之一,还有 centerXAnchor 和 centerYAnchor。 您可以为单个约束创建类似的方法来解决问题【参考方案5】:

除了 Rob 提到的NSLayoutConstraint.activate 方法之外,您还可以使用更通用的方法来消除多个.isActive = true 语句:

[
    view.leadingAnchor.constraint(equalTo: leadingAnchor),
    view.trailingAnchor.constraint(equalTo: trailingAnchor),
    view.widthAnchor.constraint(equalToConstant: 100),
    view.heightAnchor.constraint(equalToConstant: 100),
].forEach $0.isActive = true

将为数组的每个元素调用提供的内联函数(闭包)$0.isActive = true

$0 这里是一个简写的参数名。在我们的例子中,它持有一个数组元素的引用,一个 NSConstraint 对象。

【讨论】:

以上是关于快速将约束分配给视图的最简单方法?的主要内容,如果未能解决你的问题,请参考以下文章

将自定义输入视图设置为全局所有文本字段的最简单方法

在全局范围内将自定义输入视图设置为所有textFields的最简单方法

嵌入 Python 时共享数组的最简单方法

Android:将形状遮盖到视图上的最简单方法是啥? [复制]

将所有主机环境变量传递给 docker 容器的最简单方法

快速更新表格视图单元格