Mastering iOS auto format anchors programmatically from Swift


Creating views and constraints programmatically

Initially I might prefer to recap the UIViewController life cycle strategies, you’re would possibly acquainted with a few of them. They’re being referred to as within the following order:

  • loadView
  • viewDidLoad
  • viewWillAppear
  • viewWillLayoutSubviews
  • viewDidLayoutSubviews
  • viewDidAppear

Within the pre-auto format period, you needed to do your format calculations contained in the viewDidLayoutSubviews methodology, however since it is a professional auto format tutorial we’re solely going to deal with the loadView & viewDidLoad strategies. 🤓

These are the fundamental guidelines of making view hierarchies utilizing auto format:

  • By no means calculate frames manually by your self!
  • Initialize your views with .zero rect body
  • Set translatesAutoresizingMaskIntoConstraints to false
  • Add your view to the view hierarchy utilizing addSubview
  • Create and activate your format constraints NSLayoutConstraint.activate
  • Use loadView as an alternative of viewDidLoad for creating views with constraints
  • Handle reminiscence administration by utilizing weak properties
  • Set each different property like background shade, and many others. in viewDidLoad

Sufficient concept, here’s a brief instance:

class ViewController: UIViewController {

    weak var testView: UIView!

    override func loadView() {
        tremendous.loadView()

        let testView = UIView(body: .zero)
        testView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(testView)
        NSLayoutConstraint.activate([
            testView.widthAnchor.constraint(equalToConstant: 64),
            testView.widthAnchor.constraint(equalTo: testView.heightAnchor),
            testView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
            testView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
        ])
        self.testView = testView
    }

    override func viewDidLoad() {
        tremendous.viewDidLoad()

        self.testView.backgroundColor = .crimson
    }
}

Fairly easy, huh? Only a few traces of code and you’ve got a set measurement heart aligned view with a devoted class property reference. In the event you create the very same via interface builder, the system will “make” you the loadView methodology totally free, however you may should setup an IBOutlet reference to the view.

The everlasting dilemma: code vs Interface Builder.

It actually does not issues, be happy to selected your path. Typically I like enjoying round with IB, however in a lot of the circumstances I choose the programmatic method of doing issues. 😛

Frequent UIKit auto format constraint use circumstances

So I promised that I am going to present you the best way to make constraints programmatically, proper? Let’s do this now. Initially, I take advantage of nothing however format anchors. You might waste your time with the visible format language, however that is positively a useless finish. So mark my phrases: use solely anchors or stack views, however nothing else! 😇

Listed here are the most typical patterns that I take advantage of to create good layouts. 😉

Set mounted with or top

First one is the most straightforward one: set a view’s top or a width to a set level.

testView.widthAnchor.constraint(equalToConstant: 320),
testView.heightAnchor.constraint(equalToConstant: 240),

Set side ratio

Settings a view’s side ratio is simply constrainting the width to the peak or vica versa, you possibly can merely outline the speed by the multiplier.

testView.widthAnchor.constraint(equalToConstant: 64),
testView.widthAnchor.constraint(equalTo: testView.heightAnchor, multiplier: 16/9),

Middle horizontally & vertically

Centering views inside one other one is a trivial job, there are particular anchors for that.

testView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
testView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),

Stretch or fill inside view with padding

The one difficult half right here is that trailing and backside constraints behave just a little bit completely different, than high & main if it involves the constants. Often it’s a must to work with destructive values, however after just a few tries you may perceive the logic right here. 😅

testView.topAnchor.constraint(equalTo: self.view.topAnchor, fixed: 32),
testView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, fixed: 32),
testView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, fixed: -32),
testView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, fixed: -32),

Proportional width or top

In the event you do not need to work with fixed values, you should use the multiplier.

testView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 1/3),
testView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 2/3),

Utilizing secure space format guides

With the most recent iPhone you may want some guides with a view to maintain you secure from the notch. That is the rationale why views have the safeAreaLayoutGuide property. You will get all the standard anchors after calling out to the secure space information. 💪

testView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
testView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
testView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
testView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),

Animating format constraints

Animation with constraints is straightforward, you should not imagine what others would possibly say. I made some guidelines and an instance that’ll show you how to understanding the fundamental rules of animating fixed values of a constraint, plus toggling numerous constraints. 👍

Guidelines:

  • Use customary UIView animation with layoutIfNeeded
  • At all times deactivate constraints first
  • Maintain to your deactivated constraints strongly
  • Have enjoyable! 😛

Constraint animation instance:

class ViewController: UIViewController {

    weak var testView: UIView!
    weak var topConstraint: NSLayoutConstraint!
    var bottomConstraint: NSLayoutConstraint!
    var heightConstraint: NSLayoutConstraint!

    override func loadView() {
        tremendous.loadView()

        let testView = UIView(body: .zero)
        testView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(testView)

        let topConstraint = testView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor)
        let bottomConstraint = testView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor)

        NSLayoutConstraint.activate([
            topConstraint,
            testView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
            testView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
            bottomConstraint,
        ])

        let heightConstraint = testView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 0.5)

        self.testView = testView
        self.topConstraint = topConstraint
        self.bottomConstraint = bottomConstraint
        self.heightConstraint = heightConstraint
    }

    override func viewDidLoad() {
        tremendous.viewDidLoad()

        self.testView.backgroundColor = .crimson

        let faucet = UITapGestureRecognizer(goal: self, motion: #selector(self.tapped))
        self.view.addGestureRecognizer(faucet)
    }

    @objc func tapped() {
        if self.topConstraint.fixed != 0 {
            self.topConstraint.fixed = 0
        }
        else {
            self.topConstraint.fixed = 64
        }

        if self.bottomConstraint.isActive {
            NSLayoutConstraint.deactivate([self.bottomConstraint])
            NSLayoutConstraint.activate([self.heightConstraint])

        }
        else {
            NSLayoutConstraint.deactivate([self.heightConstraint])
            NSLayoutConstraint.activate([self.bottomConstraint])
        }

        UIView.animate(withDuration: 0.25) {
            self.view.layoutIfNeeded()
        }
    }
}

It isn’t that dangerous, subsequent: adaptivity and supporting a number of gadget display sizes. 🤔

The right way to create adaptive layouts for iOS? Even Apple is combating adaptive layouts within the built-in iOS functions. In the event you take a look at apps which might be made with assortment views – like pictures – layouts are fairly okay on each gadget. Nevertheless there are just a few different ones, that – for my part – are horrible experiences on an even bigger display. 🤐

Rotation assist

Your first step to adaptive format is supporting a number of gadget orientations. You’ll be able to test my earlier article about iOS auto format there are many nice stuff inside that article about rotation assist, working with layers inside auto format land, and many others. 🌈

Trait collections

Second step is to adapt trait collections. UITraitCollection is there so that you can group all of the environmental particular traits resembling measurement lessons, show scale, consumer interface idiom and lots of extra. A lot of the occasions you’ll have to test the vertical & horizontal measurement lessons. There’s a reference of gadget measurement lessons and all of the doable variations made by Apple, see the exterior sources part under. 😉

This little Swift code instance under is demonstrating the best way to test measurement lessons for setting completely different layouts for compact and common screens.

class ViewController: UIViewController {

    weak var testView: UIView!

    var regularConstraints: [NSLayoutConstraint] = []
    var compactConstraints: [NSLayoutConstraint] = []

    override func loadView() {
        tremendous.loadView()

        let testView = UIView(body: .zero)
        testView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(testView)

        self.regularConstraints = [
            testView.widthAnchor.constraint(equalToConstant: 64),
            testView.widthAnchor.constraint(equalTo: testView.heightAnchor),
            testView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
            testView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
        ]

        self.compactConstraints = [
            testView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
            testView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
            testView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
            testView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
        ]

        self.activateCurrentConstraints()

        self.testView = testView
    }

    personal func activateCurrentConstraints() {
        NSLayoutConstraint.deactivate(self.compactConstraints + self.regularConstraints)

        if self.traitCollection.verticalSizeClass == .common {
            NSLayoutConstraint.activate(self.regularConstraints)
        }
        else {
            NSLayoutConstraint.activate(self.compactConstraints)
        }
    }

    override func viewDidLoad() {
        tremendous.viewDidLoad()

        self.testView.backgroundColor = .crimson
    }

    

    override var shouldAutorotate: Bool {
        return true
    }

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .allButUpsideDown
    }

    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
        return .portrait
    }

    

    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        tremendous.traitCollectionDidChange(previousTraitCollection)

        self.activateCurrentConstraints()
    }
}

System detection

It’s also possible to test the consumer interface idiom via the UIDevice class (aka. is that this freakin’ gadget an iPhone or an iPad?) to set for instance font sizes primarily based on it. 📱

UIDevice.present.userInterfaceIdiom == .pad

Display screen measurement

An alternative choice to determine your atmosphere is checking the measurement of the display. You’ll be able to test the native pixel rely or a relative measurement primarily based in factors.


UIScreen.primary.nativeBounds   
UIScreen.primary.bounds         

Often I am making an attempt to maintain myself to those guidelines. I do not actually keep in mind a state of affairs the place I wanted greater than all of the issues I’ve listed above, however in case you have a particular case or questions, please do not hesitate to contact me. 😉

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles