快速将约束分配给视图的最简单方法?
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的最简单方法