Is the ghost of NSLayoutConstraint haunting my view hierarchy?

I'm trying to programmatically change the autodetect constraints to move the table view up, but only for iPhone 6 Plus in landscape mode, because I couldn't achieve the exact visual effect I wanted on all devices using Xcode 6.2 beta 3 Interface Builder autolayout (got right from IB for other supported devices / orientations. Only that iPhone 6 Plus is slightly different from iPhone and iPad, which is a little more complicated)

Once removed, what appears to be the removed constraint is removed (for example, disappears from the content constraints as expected), however the layout manager still seems to find it and warns that it is in conflict with other constraints and violates the constraint at runtime with a successful result that the app produces the intended visual output, but an unfortunate side effect of the ugly console warning, which I want to fix because it's ugly and Apple documentation blames such warnings for user code errors.

My code intercepts the orientation change (iPhone 6 Plus only), and then:

=============

• Iterating over constraints in the table owner view

• Prints the properties of any constraint with the .Top attribute

• Removes center Y constraint with IBOutlet, for table

• Removes the constraint with the .Top attribute

• Adds a new attribute .Top with a different multiplier

============

Here's a quick code in my view controller:

    override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
        switch(toInterfaceOrientation) {
        case .LandscapeLeft:
            fallthrough
        case .LandscapeRight:
            var deviceType = UIDevice().deviceType

            if (deviceType == .iPhone6plus || deviceType == .simulator) {
                if centerYconstraint != nil {
                    self.view.removeConstraint(centerYconstraint)
                    centerYconstraint = nil
                    for constraint in self.view.constraints() {
                        if (constraint.firstItem as NSObject == self.tableView) {
                            if (constraint.firstAttribute == NSLayoutAttribute.Top) {
                                println("found item \(constraint)")
                                let view1 = constraint.firstItem as UIView
                                let attr1 = constraint.firstAttribute
                                let view2 = constraint.secondItem as UIView
                                let attr2 = constraint.secondAttribute
                                let relation = constraint.relation
                                let constant = constraint.constant
                                let newConstraint = NSLayoutConstraint(
                                    item:       view1,
                                    attribute:  attr1,
                                    relatedBy:  relation,
                                    toItem:     view2,
                                    attribute:  attr2,
                                    multiplier: 0.02,
                                    constant:   constant)
                                self.view.removeConstraint(constraint as NSLayoutConstraint)
                                self.view.addConstraint(newConstraint)
                                self.view.layoutIfNeeded()
                            }
                        }
                    }
                }
            }
        default:
            break
        }
    }

      

Here's the Xcode simulator. Notice the first line "found item" where I am typing the constraint that I am removing.

But you can see the same view1 and view2 as the multiplier and attribute in the list of potential layout managers contflicts complain about later. This is what I'm confused about.

found item <NSLayoutConstraint:0x7f8a05101bb0 UITableView:0x7f8a05853000.top == 0.03*_UILayoutGuide:0x7f8a035517e0.top>


2015-01-03 14:36:35.290 Interphase[46388:74323123] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7f8a0354da40 V:[UITableView:0x7f8a05853000(336)]>",
    "<_UILayoutSupportConstraint:0x7f8a0514df70 V:[_UILayoutGuide:0x7f8a035517e0(49)]>",
    "<_UILayoutSupportConstraint:0x7f8a051908e0 _UILayoutGuide:0x7f8a035517e0.bottom == UIView:0x7f8a03551480.bottom>",
    "<NSLayoutConstraint:0x7f8a051b53d0 'UIView-Encapsulated-Layout-Height' V:[UIView:0x7f8a03551480(414)]>",
    "<NSLayoutConstraint:0x7f8a050d9080 UITableView:0x7f8a05853000.top == 0.02*_UILayoutGuide:0x7f8a035517e0.top>",
    "<NSLayoutConstraint:0x7f8a0354ef80 UITableView:0x7f8a05853000.centerY == UIView:0x7f8a03551480.centerY>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f8a0354da40 V:[UITableView:0x7f8a05853000(336)]>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2015-01-03 14:36:56.720 Interphase[46388:74323123] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<_UILayoutSupportConstraint:0x7f8a0514df70 V:[_UILayoutGuide:0x7f8a035517e0(49)]>",
    "<_UILayoutSupportConstraint:0x7f8a051908e0 _UILayoutGuide:0x7f8a035517e0.bottom == UIView:0x7f8a03551480.bottom>",
    "<NSLayoutConstraint:0x7f8a05101bb0 UITableView:0x7f8a05853000.top == 0.03*_UILayoutGuide:0x7f8a035517e0.top>",
    "<NSLayoutConstraint:0x7f8a051b53d0 'UIView-Encapsulated-Layout-Height' V:[UIView:0x7f8a03551480(736)]>",
    "<NSLayoutConstraint:0x7f8a050d9080 UITableView:0x7f8a05853000.top == 0.02*_UILayoutGuide:0x7f8a035517e0.top>"
)

      

+3


source to share


1 answer


Adding and removing constraints for a view flakes a bit. It never understands what kind they should be added, and then it makes it difficult to find it later when you want to remove it.

The best solution is to keep a reference to the constraints you want (either as outputs if you do this from the interface constructor, or just store them in properties) and then activate or deactivate them as needed.



Enabling constraints instead of adding them also prevents you from having to decide which view to add them to — the system does this automatically.

+2


source







All Articles