Programmatic Auto Layout (part 2 - final!)

The last few videos from this 'workshop'...hopefully it will be less confusing than where we got to last time!

Complex Format Strings

Pasan talks through the code required for layouts. The benefit is adding several constraints, all in one go!






Orientation - horizontal or vertical. Optional as it would default to horizontal. H for horizontal; V for vertical.

Leading relationship - superview connection (will come back to)

View - this is required. Needs to refer to this.

Type of connection - between view and any other view.

Trailing relationship - can be optional (again linked to superview).


Use of the |..... This is used for showing the superview. Known as the 'pipe' key apparently!

"V:|-[redView(viewHeight)]"

This is a strange one. 

    NSLayoutConstraint.constraints(withVisualFormat: "H:|-(customHorizontalSpacing)-[redView(viewWidth)]-customHorizontalSpacing-[greenView(redView)]", options: [], metrics: metrics, views: views).map { $0.isActive = true }


Right so now this one string is adding FOUR constraints to the view hierarchy!

class VisualFormatExampleController: UIViewController {
    
    let redView = UIView()
    let greenView = UIView()
    let blueView = UIView()
    let orangeView = UIView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        redView.backgroundColor = .red
        greenView.backgroundColor = .green
        blueView.backgroundColor = .blue
        orangeView.backgroundColor = .orange
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        
        redView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(redView)
        
        greenView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(greenView)
        
        blueView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(blueView)
        
        orangeView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(orangeView)
        
        // Constraint Code
        let views: [String: Any] = [
            "redView": redView,
            "greenView": greenView,
            "blueView": blueView,
            "orangeView": orangeView
        ]
        
        let metrics: [String: Any] = [
            "viewWidth": 100,
            "viewHeight": 100,
            "customVerticalSpacing": 50,
            "customHorizontalSpacing": 50
        ]
        
        NSLayoutConstraint.constraints(withVisualFormat: "H:|-(customHorizontalSpacing)-[redView(viewWidth)]-customHorizontalSpacing-[greenView(redView)]", options: [], metrics: metrics, views: views).map { $0.isActive = true }
        
        NSLayoutConstraint.constraints(withVisualFormat: "V:|-(customVerticalSpacing)-[redView(viewHeight)]-customVerticalSpacing-[blueView(redView)]", options: [], metrics: metrics, views: views).map { $0.isActive = true }
        
         NSLayoutConstraint.constraints(withVisualFormat: "V:|-(customVerticalSpacing)-[greenView(redView)]-customVerticalSpacing-[orangeView(greenView)]", options: [], metrics: metrics, views: views).map { $0.isActive = true }
        
          NSLayoutConstraint.constraints(withVisualFormat: "H:|-(customHorizontalSpacing)-[blueView(redView)]-(customHorizontalSpacing)-[orangeView(blueView)]", options: [], metrics: metrics, views: views).map { $0.isActive = true }
        
        
    }
}

However....ALL OF THAT CODE?! For four coloured boxes?! What an utter waste of time! I honestly do not see the point of using this programmatic auto layout, unless you absolutely have to!

Alignment Masks


So there are plenty of options in code for these!!


Pasan has just stated we will not be using visual format strings, so what's the point?!

The cons far outweigh the pros in my opinion. The debugging factor, how verbose it is, so many strings being used, typos can break the layout....too many things. 

The point of this was just to understand what visual format is, although I will never actually utilise this. 

Layout Guides

Remember that is is an empty rectangle to help us sort out the layout. 


class LayoutGuidesController: UIViewController {
    let viewA = UIView()
    let viewB = UIView()
    let viewC = UIView()
    
    let firstLayoutGuide = UILayoutGuide()
    let secondLayoutGuide = UILayoutGuide()
    let thirdLayoutGuide = UILayoutGuide()
    let fourthLayoutGuide = UILayoutGuide()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        viewA.backgroundColor = .green
        viewB.backgroundColor = .green
        viewC.backgroundColor = .green
    }

This looks like more long-winded and verbose code.

override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        
        
        viewA.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(viewA)
        
        viewB.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(viewB)
        
        viewC.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(viewC)
        
        NSLayoutConstraint.activate([
            viewA.widthAnchor.constraint(equalToConstant: 100),
            viewA.heightAnchor.constraint(equalToConstant: 100),
            viewA.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            viewB.widthAnchor.constraint(equalToConstant: 100),
            viewB.heightAnchor.constraint(equalToConstant: 100),
            viewB.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            viewC.widthAnchor.constraint(equalToConstant: 100),
            viewC.heightAnchor.constraint(equalToConstant: 100),
            viewC.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        ])
        
        view.addLayoutGuide(firstLayoutGuide)
        view.addLayoutGuide(secondLayoutGuide)
        view.addLayoutGuide(thirdLayoutGuide)
        view.addLayoutGuide(fourthLayoutGuide)
        
        NSLayoutConstraint.activate([
            firstLayoutGuide.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            firstLayoutGuide.bottomAnchor.constraint(equalTo: viewA.topAnchor),
            secondLayoutGuide.topAnchor.constraint(equalTo: viewA.bottomAnchor),
            secondLayoutGuide.bottomAnchor.constraint(equalTo: viewB.topAnchor),
            thirdLayoutGuide.topAnchor.constraint(equalTo: viewB.bottomAnchor),
            thirdLayoutGuide.bottomAnchor.constraint(equalTo: viewC.topAnchor),
            fourthLayoutGuide.topAnchor.constraint(equalTo: viewC.bottomAnchor),
            fourthLayoutGuide.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            fourthLayoutGuide.heightAnchor.constraint(equalTo: thirdLayoutGuide.heightAnchor),
            thirdLayoutGuide.heightAnchor.constraint(equalTo: secondLayoutGuide.heightAnchor),
            secondLayoutGuide.heightAnchor.constraint(equalTo: firstLayoutGuide.heightAnchor),
            firstLayoutGuide.heightAnchor.constraint(equalTo: fourthLayoutGuide.heightAnchor)
        ])
    }

Layout guided act as 'dummy views'. I have skimmed over this mostly as it was mostly Pasan typing out code which I doubt I will ever need to do!

Safe Area Layout Guides

class SafeAreaInsetsController: UIViewController {
    let customView = UIView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        customView.backgroundColor = .red
        view.backgroundColor = .yellow
        
        let insets = UIEdgeInsets(top: 50, left: 50, bottom: 0, right: 0)
        additionalSafeAreaInsets = insets
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        
        customView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(customView)
        
        NSLayoutConstraint.activate([
            customView.widthAnchor.constraint(equalToConstant: 200),
            customView.heightAnchor.constraint(equalToConstant: 200),
            customView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            customView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
        ])
    }

}

More code - this time it doesn't look quite as mental!

let insets = UIEdgeInsets(top: 50, left: 50, bottom: 0, right: 0)
        additionalSafeAreaInsets = insets

This bit specifically uses the UIEdgeInsets class. Positive values cause the value to be shrunk by the amount specified. So we are trying to shrink the top and left inset area. 

So that's all for Treehouse! It's been a HUGELY beneficial few weeks - checking my blog records it's been since 4th June that I switched to it. Just to recap, from 4th to 16th June it was all recap of the basics, then from 17th to now it's been learning 'new' material. All of which has been of great use - thanks Pasan! Now my plan is to pause my Treehouse subscription (what a great feature that is) and choose one of the Udemy courses to focus on, which will hopefully give a greater depth of understanding and get more hands-on experience. This is only just the start! Next time, I will map out a basic plan of what I want to achieve this Summer....

Comments

Popular posts from this blog

*Xcode Project Entry 2* F1 Quiz - part 1

Angela Yu Course Part 10 (up to lesson 112)

Angela Yu Xcode 12 Course - Part 7 (lectures 74 to 79)