Search code examples
iosswiftautoresizingmaskautolayout

NSAutoresizingMaskLayoutConstraint errors in an AutoLayout-based application


Due to the sheer amount of AutoLayout-related questions I'm not entirely sure I am asking a new question, but I just can't get this to work. I'm trying to animate a view using AutoLayout, but I'm already failing at just translating it. I have worked with AL before using storyboards, this is my first try at using them programmatically.

So, to the problem: Starting off with a newly created master-detail iOS application, I deleted everything from the storyboard, inserted a new view controller and set its view's class to BaseView. The code for base view is as follows:

class BaseView: UIView {
  @IBOutlet var secondaryView: UIView! = nil

  override func awakeFromNib() {
    setTranslatesAutoresizingMaskIntoConstraints(false)
    backgroundColor = UIColor.redColor()
    secondaryView = UIView(frame: frame)
    secondaryView.backgroundColor = UIColor.blueColor()
    addSubview(secondaryView)
    let views = ["secondaryView": secondaryView, "self": self]
    addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-(100)-[secondaryView]", options: NSLayoutFormatOptions.AlignAllLeading, metrics: nil, views: views))
    addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[secondaryView(==self)]", options: NSLayoutFormatOptions(0), metrics: nil, views: views))
    addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[secondaryView(==self)]", options: NSLayoutFormatOptions(0), metrics: nil, views: views))
  }
}

I also tried setting translatesAutoresizingMaskIntoConstraints to false via the storyboard's user defined runtime attributes, to no avail. What I would expect is to see a red square (the base view) on the left side of the simulator, and the rest of the simulator screen filled with the color blue. Instead everything is blue, and the log gives me the following errors:

2014-08-20 22:19:00.392 AutoLayoutAnimationTest[5585:473139] 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:0x7fa8c94b5f50 H:|-(100)-[UIView:0x7fa8c94b2540]   (Names: '|':AutoLayoutAnimationTest.BaseView:0x7fa8c94afa80 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x7fa8c94bdfc0 h=--& v=--& UIView:0x7fa8c94b2540.midX == + 300>",
    "<NSAutoresizingMaskLayoutConstraint:0x7fa8c94be1d0 h=--& v=--& H:[UIView:0x7fa8c94b2540(600)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fa8c94b5f50 H:|-(100)-[UIView:0x7fa8c94b2540]   (Names: '|':AutoLayoutAnimationTest.BaseView:0x7fa8c94afa80 )>

I have no idea where these NSAutoresizingMaskLayoutConstraints come from, but from my understanding they are preventing the secondaryView from moving. What am I doing wrong?


Solution

  • Updated for Swift 2

    By making the presumption that secondaryView doesn't already exist in the Storyboard and is just a property without any IBOutlet, I was able to make the following code work without any debugger complaint:

    class BaseView: UIView {
        //@IBOutlet var secondaryView: UIView! = nil
        var secondaryView: UIView!
    
        override func awakeFromNib() {
            backgroundColor = UIColor.redColor()
    
            secondaryView = UIView()
            secondaryView.translatesAutoresizingMaskIntoConstraints = false // Swift 2
            // secondaryView.setTranslatesAutoresizingMaskIntoConstraints(false) // Swift 1.2
            secondaryView.backgroundColor = UIColor.blueColor()
            addSubview(secondaryView)
    
            let views = ["secondaryView": secondaryView, "self": self]
            addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-(100)-[secondaryView]", options: NSLayoutFormatOptions.AlignAllLeading, metrics: nil, views: views))
            addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[secondaryView(==self)]", options: NSLayoutFormatOptions(0), metrics: nil, views: views))
            addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[secondaryView(==self)]", options: NSLayoutFormatOptions(0), metrics: nil, views: views))
        }
    }
    

    Therefore, I think that the following code is (a much concise) equivalent of the previous code:

    class BaseView: UIView {
        var secondaryView: UIView!
    
        override func awakeFromNib() {
            backgroundColor = UIColor.redColor()
    
            secondaryView = UIView()
            secondaryView.translatesAutoresizingMaskIntoConstraints = false // Swift 2
            // secondaryView.setTranslatesAutoresizingMaskIntoConstraints(false) // Swift 1.2
            secondaryView.backgroundColor = UIColor.blueColor()
            addSubview(secondaryView)
    
            let views = ["secondaryView": secondaryView]
            addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-(100)-[secondaryView]|", options: NSLayoutFormatOptions(0), metrics: nil, views: views))
            addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[secondaryView]|", options: NSLayoutFormatOptions(0), metrics: nil, views: views))
        }
    }