Mastering iOS auto structure anchors programmatically from Swift

[ad_1]

Searching for finest practices of utilizing structure anchors? Let’s learn to use the iOS autolayout system within the correct method utilizing Swift.

iOS

Creating views and constraints programmatically

To begin with I would wish to recap the UIViewController life cycle strategies, you’re would possibly aware of a few of them. They’re being known as within the following order:

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

Within the pre-auto structure period, you needed to do your structure calcuations contained in the viewDidLayoutSubviews methodology, however since this can be a professional auto structure tutorial we’re solely going to concentrate on the loadView & viewDidLoad strategies. πŸ€“

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

  • 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 structure constraints NSLayoutConstraint.activate
  • Use loadView as an alternative of viewDidLoad for creating views with constraints
  • Handle reminiscence administration through the use of weak properties
  • Set each different property like background colour, and many others. in viewDidLoad

Sufficient principle, 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 = .purple
    }
}

Fairly easy, huh? Only a few traces of code and you’ve got a set dimension heart aligned view with a devoted class property reference. If you happen to create the very same via interface builder, the system will “make” you the loadView methodology at no cost, however you will should setup an IBOutlet reference to the view.

The everlasting dilemma: code vs Interface Builder.

It actually would not issues, be at liberty to selected your path. Typically I like enjoying round with IB, however in many of the circumstances I favor the programmatic method of doing issues. πŸ˜›


Widespread UIKit auto structure constraint use circumstances

So I promised that I am going to present you tips on how to make constraints programmatically, proper? Let’s do this now. To begin with, I exploit nothing however structure anchors. You may waste your time with the visible format language, however that is undoubtedly a lifeless finish. So mark my phrases: use solely anchors or stack views, however nothing else! πŸ˜‡

Listed below are the most typical patterns that I exploit to create good layouts. πŸ˜‰

Set fastened with or peak

First one is the simplest one: set a view’s peak 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 may merely outline the speed by the multiplier.

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

Heart horizontally & vertically

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

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

Stretch | fill inside view with padding

The one difficult half right here is that trailing and backside constraints behave a bit of bit completely different, than prime & main if it involves the constants. Normally you need to work with detrimental values, however after a couple of tries you will 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 peak

If you happen to do not need to work with fixed values, you need to use the multiplier.

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

Utilizing protected space structure guides

With the most recent iPhone you will want some guides with a purpose to hold you protected from the notch. That is the rationale why views have the safeAreaLayoutGuide property. You may get all the same old anchors after calling out to the protected 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 structure constraints

Animation with constraints is simple, you should not consider what others would possibly say. I made some guidelines and an instance that’ll allow you to understanding the fundamental ideas of animating fixed values of a constraint, plus toggling varied constraints. πŸ‘

Guidelines:

  • Use customary UIView animation with layoutIfNeeded
  • All the time 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 = .purple

        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 unhealthy, subsequent: adaptivity and supporting a number of machine display sizes. πŸ€”


Find out how to create adaptive layouts for iOS?

Even Apple is fighting adaptive layouts within the built-in iOS purposes. If you happen to take a look at apps which are made with assortment views – like pictures – layouts are fairly okay on each machine. Nonetheless there are a couple of different ones, that – for my part – are horrible experiences on a much bigger display. #justusecollectionviewforeverything. 🀐

Rotation assist

Your first step to adaptive structure is supporting a number of machine orientations. You’ll be able to test my earlier article about iOS auto structure there are many nice stuff inside that article about rotation assist, working with layers inside auto structure 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 corresponding to dimension courses, show scale, consumer interface idom and plenty of extra. Many of the instances you’ll have to test the vertical & horizontal dimension courses. There’s a reference of machine dimension courses and all of the potential variations made by Apple, see the exterior sources part beneath. πŸ˜‰

This little Swift code instance beneath is demonstrating tips on how to test dimension courses 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
    }

    non-public 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 = .purple
    }

    

    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()
    }
}

Gadget detection

You may also test the consumer interface idom via the UIDevice class (aka. is that this freakin’ machine an iPhone or an iPad?)Β to set for instance font sizes primarily based on it. πŸ“±

UIDevice.present.userInterfaceIdiom == .pad

Display screen dimension

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


UIScreen.major.nativeBounds   
UIScreen.major.bounds         

Normally I am attempting to maintain myself to those guidelines. I do not actually keep in mind a situation the place I wanted greater than all of the issues I’ve listed above, however if in case you have a particular case or questions, please do not hesitate to contact me. πŸ˜‰



[ad_2]

Leave a Reply