0
votes

I have a blank Swift project in Xcode and am not using storyboard. I have a view that I've added in code under viewDidLoad:

var view1 = UIView()
view1.backgroundColor = .red
view.addSubview(view1)
view1.translatesAutoresizingMaskIntoConstraints = false

I then add constraints:

NSLayoutConstraint.activate([
            view1.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),
            view1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            view1.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            view1.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20)
        ])

The view loads and looks fine in the simulator. But I've been having some issues with some other views in another project so I have built the above for testing. So what I've discovered is that even though the above builds and works, if I check for ambiguous layouts using:

print(view1.hasAmbiguousLayout)

I get errors logged:

2021-02-14 15:26:34.919048+1100 autolayoutextensions[6603:4438601] [LayoutConstraints] View has an ambiguous layout. See "Auto Layout Guide: Ambiguous Layouts" for help debugging. Displaying synopsis from invoking -[UIView _autolayoutTrace] to provide additional detail.

*UIView:0x13360c3b0- AMBIGUOUS LAYOUT for UIView:0x13360c3b0.Width{id: 9}, UIView:0x13360c3b0.Height{id: 12}

Legend: * - is laid out with auto layout + - is laid out manually, but is represented in the layout engine because translatesAutoresizingMaskIntoConstraints = YES • - layout engine host true

The only way I can get these errors to NOT get logged, is by also setting the ViewController's main view as follows:

view.translatesAutoresizingMaskIntoConstraints = false

By doing this, I know get the following log output:

false

So my question is this... I haven't read anywhere in Apple's documentation (or here, Googling etc) that says you have to set the viewController's view.translatesAutoresizingMaskIntoConstraints to false. Have I missed something? Apologies if this is just my understanding but would be good to get this clarified.

1
Yes. If you are going to manually set the constraints you need to set that variable to false. If you don’t set that, UIKit will try to figure it out automatically the necessary constraints for a given auto-layout. You can read that in the official documentation. developer.apple.com/documentation/uikit/uiview/… - Jacobo
Thanks. Yes I've read that but it doesn't really talk about the superview. And I'm not really trying to control/manage the superviews constraints/sizing etc. That's what's confusing me. - Anthony

1 Answers

0
votes

First you should never set .translatesAutoresizingMaskIntoConstraints = false on the main view of a view controller (unless you load the controller as a child, but that's not what you're talking about in your question).

You need to read-up on view / view-controller life-cycle.

In viewDidLoad(), auto-layout HAS NOT yet run to do its management of the view and subview(s) layout.

In viewDidLayoutSubview(), auto-layout HAS run, but depending on how dynamic your design is (among other factors) this may be called more than once.

In viewDidAppear(), all the layout has been completed (unless your code is running an async process to animated views, add additional views, whatever).

So, try running this simple example - it will print the name of the currently executing function along with the output from .hasAmbiguousLayout:

class TestLayoutViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        var view1 = UIView()
        view1.backgroundColor = .red
        view.addSubview(view1)
        view1.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            view1.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),
            view1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            view1.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            view1.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20)
        ])
        
        print() // blank line
        print(#function)
        print() // blank line
        print("In viewDidLoad(), does subview have ambigous layout?")
        print(view1.hasAmbiguousLayout)

    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        guard let v = view.subviews.first else {
            return
        }
        
        print() // blank line
        print(#function)
        print() // blank line
        print("In viewDidLayoutSubviews(), does subview have ambigous layout?")
        print(v.hasAmbiguousLayout)
        
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        guard let v = view.subviews.first else {
            return
        }
        
        print() // blank line
        print(#function)
        print() // blank line
        print("In viewDidAppear(), does subview have ambigous layout?")
        print(v.hasAmbiguousLayout)
        
    }
    
}

This is what you should see in the debug console:

viewDidLoad()

In viewDidLoad(), does subview have ambigous layout?
2021-02-14 08:41:46.694337-0500 PanZoom[43869:6978294] [LayoutConstraints] View has an ambiguous layout. See "Auto Layout Guide: Ambiguous Layouts" for help debugging. Displaying synopsis from invoking -[UIView _autolayoutTrace] to provide additional detail.

*UIView:0x7fd472604960- AMBIGUOUS LAYOUT for UIView:0x7fd472604960.Width{id: 90}, UIView:0x7fd472604960.Height{id: 93}

Legend:
    * - is laid out with auto layout
    + - is laid out manually, but is represented in the layout engine because translatesAutoresizingMaskIntoConstraints = YES
    • - layout engine host
true

viewDidLayoutSubviews()

In viewDidLayoutSubviews(), does subview have ambigous layout?
false

viewDidAppear(_:)

In viewDidAppear(), does subview have ambigous layout?
false

As you can see, during viewDidLoad() we get auto-layout ambiguities, but NOT in viewDidLayoutSubviews() or in viewDidAppear().