0

I have 2 buttons with different constraints write in the storyboard. In my code, I have to tell my first button to take the same constraint of the second button (height, width, and position) but I do not know how to do this without necessarily passing by "subview". I have 2 outlets for my buttons.

@IBOutlet weak var likesButton: UIButton!    
@IBOutlet weak var shareButton: UIButton!

My interface is build like that :

-

VIEW
     a)...View
     b)...View
     c) ContentView
         1)likeButton
         2)shareButton
         3)...
     d)...

I tried like this :

      self.contentView.translatesAutoresizingMaskIntoConstraints = false
        self.shareButton.removeConstraints(self.shareButton.constraints) // Old Constraints
        self.shareButton.addConstraints(self.likeButton.constraints) // New Constraints
        self.shareButton.updateConstraintsIfNeeded()
        self.shareButton.layoutIfNeeded()

And after I have this error :

 *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to install constraint on view.  Does the constraint reference something from outside the subtree of the view?  That's illegal. constraint:<NSLayoutConstraint:0x14cfe7600 H:[UIButton:0x14cfb7bd0(40)]> view:<UIButton: 0x14e30fa60; frame = (494 528; 26 31); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x14e30f410>>'

Thx in advance.

CizooDev
  • 81
  • 15
  • Constraints are added to the super view which holds the view you are trying to add constraints to and not the view itself. – Rikh Nov 17 '16 at 17:03
  • Okay however I say to addConstraints to the shareButton and not the view ? – CizooDev Nov 17 '16 at 17:15

2 Answers2

1

Does the constraint reference something from outside the subtree of the view?

This is as if it said...

Does the constraints of likeButton reference something from outside the subtree of the shareButton?

So, the likeButton constraint references (like the likeButton itself) are completely outside the shareButtons view hierarchy. They can't be added this way.

You could instead do:

self.view.removeConstraints(self.shareButton.constraints)
self.view.addConstraints(self.likeButton.constraints)

But this won't accomplish whatever you're trying to do.

A constraint is more than just a number and dimension. It also defines which two objects are constrained. So trying to map one views constraints to another like that won't work because it doesn't change which views are referenced in the constraint.

You can probably hack out a way to do it using outlets to the various constraints and pulling the constraint constants out of one view and assigning them to the other.

Frankie
  • 11,508
  • 5
  • 53
  • 60
  • Thx for giving me the idea of hacking out the way to do it using outlets constraints ! That help a lot ! – CizooDev Nov 17 '16 at 17:55
1

If this is your scenario:

  • Your buttons and constraints have been set up in IB, and you wish to change or replace the constraints one by one in code

Then you can set up an IBOutlet for each constraint and do your editing (changing, replacing, or even deactivating them) by name.

@IBOutlet weak var likesConstraintLeading: NSLayoutConstraint!
@IBOutlet weak var likesConstraintTop: NSLayoutConstraint!
@IBOutlet weak var likesConstraintWidth: NSLayoutConstraint!

likesConstraintLeading.isActive = false
likesConstraintTop.constant = 20
likesConstraintWidth.multiplier = 3.5
  • Your buttons and constraints have been set up in IB, and you wish to replace the entire constraints set up for a single IBOutlet-named button

You can set up the new set of constraints in code as an array and activate them after removing the old constraints.

@IBOutlet weak var likesConstraintTop: NSLayoutConstraint!
@IBOutlet weak var likesConstraintLeading: NSLayoutConstraint!
@IBOutlet weak var likesConstraintWidth: NSLayoutConstraint!

var newConstraints = [NSLayoutConstraint]()

newConstraints.append(likesButton.leadingAnchor.constraint(equalTo: view.leadingAnchor))
newConstraints.append(likesButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 20))
newConstraints.append(likesButton.widthAnchor.constraint(equalToConstant: 100))

likesConstraintLeading.isActive = false
likesConstraintTop.isActive = false
likesConstraintWidth.isActive = false
NSLayoutConstraint.activate([newConstraints])

Please note - you still need to deactivate things one-by-one because these constraints (except for the width) is added to the control's superview and there are likely many more constraints in it!

  • You have buttons set up in IB, but you wish to maintain the one set of constraints through code.

This IMHO is the best setup you can do, because you can still add all of your other constraints through IB and limit your code to what you wish to change.

var startingConstraints = [NSLayoutConstraint]()
var endingConstraints = [NSLayoutConstraint]()

override func viewDidLoad() {
    startingConstraints.append(likesButton.leadingAnchor.constraint(equalTo: view.leadingAnchor))
    startingConstraints.append(likesButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 20))
    startingConstraints.append(likesButton.widthAnchor.constraint(equalToConstant: 100))
    endingConstraints.append(likesButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30))
    endingConstraints.append(likesButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 40))
    endingConstraints.append(likesButton.widthAnchor.constraint(equalToConstant: 80))
    NSLayoutConstraint.activate([startingConstraints])
}

func changeConstraints() {
    NSLayoutConstraint.deactivate([newConstraints])
    NSLayoutConstraint.activate([newConstraints])
}