将 NSLayoutConstraint 常量视为乘数?

Treat NSLayoutConstraint constant as multiplier?

我在容器 UIView 中有三个水平对齐的 UIView。这三个视图应该跨越上面 UIImageView 的整个宽度。然而,问题是有时只应显示一个或两个子视图。

我这样设置视图层次结构:

由于子视图设置为等于第一个子视图的宽度(将始终显示),我只是将第一个子视图的宽度设置为 UIImageView 宽度的一小部分.因此,如果应显示三个子视图,则第一个子视图的乘数将为 UIImageView 宽度的 1/3。如果应显示两个子视图,则乘数将为 1/2。如果只有一个,则乘数为 1.

这似乎是一个完美的解决方案,但是乘数 属性 是只读的。我第一次尝试解决这个问题是通过创建三个不同的 NSLayoutConstraints 附加到第一个子视图,所有这些都具有不同的乘数,其中 2/3 被关闭。然后,在运行时,我将根据我想要显示的视图数量使用适当的乘数启用适当的常量。

这导致了很多丑陋的错误,我的第二个解决方案也是如此:

var constraint = NSLayoutConstraint(item: color1, attribute:NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: imageView, attribute: NSLayoutAttribute.Width, multiplier: 1/2, constant: 0)
view.addConstraint(constraint)

我将根据我想要的乘数向视图添加新约束。这当然会导致错误:

When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled.

因此,

我的问题 是,我是否可以像对待 multiplier 属性 一样对待 constant 属性。然而,我担心这样做是因为如果我为第一个子视图的宽度设置 constant,当 phone 旋转时它不会更新它的宽度。

是否有更好的解决方案?

首先,在您的问题中,您使用的是 IB,但您似乎暗示每次移动到视图控制器时可能会有不同数量的视图,这就是为什么我决定以编程方式创建 NSLayoutConstraints .

其次,我的解决方案很好,前提是您在使用视图控制器时不打算更改视图数量。如果你这样做了,那么这需要更多的工作。

在您的视图控制器中:

var viewWidthConstraints : [NSLayoutConstraint] = []

override func viewDidLoad() {
    super.viewDidLoad()

    let numberOfViews = 3
    var previousView: UIView = self.view

    for i in 0..<numberOfImages {
        let myView = UIView()
        myView.setTranslatesAutoresizingMaskIntoConstraints(false)
        myView.backgroundColor = UIColor.grayColor().colorWithAlphaComponent(CGFloat(i) * (1/CGFloat(numberOfImages)) + 0.1)
        self.view.addSubview(myView)

        let heightConstraint = NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|",
                                options: .DirectionLeadingToTrailing,
                                metrics: nil,
                                views: ["view" : myView])

        let widthConstraint = NSLayoutConstraint(item: myView, 
                                attribute: .Width, 
                                relatedBy: .Equal, 
                                toItem: nil, 
                                attribute: .NotAnAttribute, 
                                multiplier: 1.0, 
                                constant: self.view.frame.width / CGFloat(numberOfImages))

        let attribute: NSLayoutAttribute = (i == 0) ? .Left : .Right
        let leftConstraint = NSLayoutConstraint(item: myView,
            attribute: .Left,
            relatedBy: .Equal,
            toItem: previousView,
            attribute: attribute,
            multiplier: 1.0,
            constant: 0.0)

        self.view.addConstraints(heightConstraint)
        self.view.addConstraint(leftConstraint)
        myView.addConstraint(widthConstraint)

        viewWidthConstraints.append(widthConstraint)

        previousView = myView
    }
}

func updateWidthConstraints() {
    if viewWidthConstraints.count > 0 {
        let width = self.view.frame.width / CGFloat(viewWidthConstraints.count)

        for constraint in viewWidthConstraints {
            constraint.constant = width
        }
    }
}

override func viewWillLayoutSubviews() {
    updateWidthConstraints()
}

viewDidLoad 中,您将 UIView 添加到视图并设置它们的约束。您可以更改垂直约束以使 UIView 显示在 UIImageView 下方。并更改numberOfViews以增加或减少观看次数。

然后在 viewWillLayoutSubviews 中,您使用宽度约束更新每个视图的宽度。这将确保,如果设备旋转,每个视图占据屏幕的正确比例。

这是水平方向的样子。

和垂直方向。