1

Today the multiplier property is read-only. In order to change it, you have to remove the constraint and create a new one.

let newConstraint = NSLayoutConstraint(
    item: constraint.firstItem,
    attribute: constraint.firstAttribute,
    relatedBy: constraint.relation,
    toItem: constraint.secondItem,
    attribute: constraint.secondAttribute,
    multiplier: newMultiplier,
    constant: constraint.constant
)

In this example, first the multiplier will be applied and then the constant. For example:

Imagine it's a width constraint to the superview.

This means we'll have view.width = superview.width * multiplier - 2. (which also means that a multiplier = 0 will break).

What I need is view.width = (superview.width - 2) * multiplier. How do I create such constraint?

I also don't want to do something like:

let newConstraint = NSLayoutConstraint(
    item: constraint.firstItem,
    attribute: constraint.firstAttribute,
    relatedBy: constraint.relation,
    toItem: constraint.secondItem,
    attribute: constraint.secondAttribute,
    multiplier: newMultiplier,
    constant: constant * newMultiplier
)

Because I end up losing the constraint.constant that comes from the storyboard.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Rodrigo Ruiz
  • 4,248
  • 6
  • 43
  • 75
  • `view.width = (superview.width - 2) * multiplier` this one also could crash – Vasilii Muravev Jun 05 '17 at 19:17
  • FWIW you're writing your constraints using the old harder way. Use anchors instead. See [here](https://www.raywenderlich.com/125718/coding-auto-layout) and [here](https://stackoverflow.com/questions/44074872/programatic-constraints-not-obeyed/44078258#44078258) – mfaani Jun 05 '17 at 19:26
  • In this case I don't have (or want to) a pointer to the view, so only to the constraint. So how would I use anchor in this case? – Rodrigo Ruiz Jun 05 '17 at 20:01
  • To get the result you want, you *probably* will need to embed your view inside another view. Set "OuterView" width constraint to superview.width + constant of `-2`, and set "InnerView" width constraint to *its* superview.width * multiplier. – DonMag Jun 05 '17 at 20:14
  • Thank you @DonMag I thought of that, but feels like a hack to add another view just for that. – Rodrigo Ruiz Jun 06 '17 at 15:01
  • It depends a lot on what all you're trying to do. If you browse through some of Apple's auto-layout examples, it's not unusual to see embedded views. For that matter, it wouldn't surprise me to find out that "under the hood" `UIStackView` uses similar techniques. – DonMag Jun 06 '17 at 15:10

4 Answers4

1

Why do you want to change a multiplier value? Change constant value instead..

declare inside the class

var yourViewWidthConstraint: NSLayoutConstraint?

Instantiate this constraint

yourViewWidthConstraint = yourView.widthAnchor.constraint(self.view.widthAnchor)
yourViewWidthConstraint?.isActive = true

Now, change yourViewWidthConstraint.constant anywhere..

MAGiGO
  • 599
  • 5
  • 13
  • Because I want the width of one view to be proportional to the width of another. How can I do that with constant since I don't have the correct frame at the time I set the constraint? – Rodrigo Ruiz Jun 05 '17 at 19:54
0

Let's say your superView is main screen.

newConstraint.constant = (UIScreen.mainScreen().bounds.size.width - 2) * your_multiplier
elk_cloner
  • 2,049
  • 1
  • 12
  • 13
  • It isn't the main screen and the bounds property is not correct at the time I need to update the constraint. – Rodrigo Ruiz Jun 05 '17 at 19:28
  • That's why i used 'let's say' However it does not matter. Knowing the width of the view is easy depends on how you have created it. – elk_cloner Jun 05 '17 at 19:34
0

You could try next:

class MyConstraint: NSLayoutConstraint {

    fileprivate var storyboardConstant: CGFloat!

    override func awakeFromNib() {
        super.awakeFromNib()
        storyboardConstant = constant
    }

    func copy(with newMultiplier: CGFloat) -> MyConstraint? {
        guard let viewWidth = constraint.secondItem?.bounds?.width,
              let _ = storyboardConstant else {
            return nil
        }
        let newConstraint = MyConstraint(
            item: constraint.firstItem,
            attribute: constraint.firstAttribute,
            relatedBy: constraint.relation,
            toItem: constraint.secondItem,
            attribute: constraint.secondAttribute,
            multiplier: newMultiplier,
            constant: storyboardConstant * newMultiplier
        )
        return newConstraint
    }
}
Vasilii Muravev
  • 3,063
  • 17
  • 45
0

In these images, Purple is the "SuperView" ... Orange is the "OuterView" ... Blue is the "InnerView"

For your use, Purple and Orange will likely have .clear background color.

The Width Constraint of "SuperView" controls the width of its two subviews - in this example, Orange will be -2 of Purple's width, and Blue will be 90% of Orange's width (0.9 multiplier).


"SuperView" is simply set to a Width and Height, and centered in its parent view.

enter image description here


"OuterView" is set Equal Width to its superview (Purple / "SuperView"), with a Constant of -2 (no multiplier)

enter image description here


"InnerView" is set Equal Width to its superview (Orange / "OuterView"), with desired Multiplier - I used 0.9 in this example.

enter image description here

DonMag
  • 69,424
  • 5
  • 50
  • 86