Search code examples
iosxcodeautolayoutscreen-orientationnslayoutconstraint

Controls positioning under the navigation bar issue


I have a question on Auto layout. I'm using xib files and I have a view controller like this.

enter image description here

Its embedded inside a UINavigationController so I have the button positioned with a Top Space to Superview constraint.

The problem is when I rotate the device, it looks like this.

enter image description here

As you can see that constraint still keeps its original value so there's a big gap between the navigation bar edge and the button in landscape mode.

How can I make the button position close to the navigation bar like when its in the portrait and have it that way in both portrait and landscape modes? I'm using Xcode 6 by the way.

Thank you.


Solution

  • If you want to define your auto layout top margin constraints in your Xib file, you can add the following code in the ViewController's class file relative to your Xib:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        if self.respondsToSelector("edgesForExtendedLayout") {
            edgesForExtendedLayout = UIRectEdge.None
        }
    }
    

    Simple. But the problem there is that you won't be able to have a translucent navigation bar.

    Fortunately, there are several alternatives to this. You can define your auto layout top margin as relative to your Top Layout Guide (not to your view) with Storyboard or with code.

    If you move to Storyboard, click on the Pin button, select your top margin constraint and choose The Top Layout Guide (see image below).

    enter image description here

    If you decide to define all your UIButton's constraints with code, you can use Visual Format Language as indicated in the following code:

    import UIKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            var button = UIButton()
            button.backgroundColor = UIColor.blueColor()
    
            button.setTranslatesAutoresizingMaskIntoConstraints(false)
            view.addSubview(button)
    
            var viewsDict = ["button" : button, "topLayoutGuide" : topLayoutGuide]
    
            view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[topLayoutGuide]-20-[button]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
            view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-20-[button]-20-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
        }
    
    }
    

    Finally, there is a fourth (kind of mixed) way to perform what you want to do. Set your constraints in your Xib, drag your top margin constraint and your UIButton to your view controller class (name them topConstraint and button) and set your code as the following:

    import UIKit
    
    class ViewControllerTwo: UIViewController {
    
        @IBOutlet weak var topConstraint: NSLayoutConstraint!
        @IBOutlet weak var button: UIButton!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            view.addConstraint(NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.topLayoutGuide, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 10))
            view.removeConstraint(topConstraint)
        }
    
    }