Monday, October 23, 2023
HomeiOS DevelopmentiOS Auto Structure tutorial programmatically

iOS Auto Structure tutorial programmatically


Rotation help

In case your utility goes to help a number of system orientations, it’s best to implement the next strategies inside your view controller.

class ViewController: UIViewController {

    override var shouldAutorotate: Bool {
        return false
    }

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }

    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
        return .portrait
    }
}

Clearly you possibly can change the return values to help not simply portrait, however panorama mode as nicely. That is fairly simple, nonetheless in case your controller is embedded inside a navigation or a tab bar controller the rotation stops working. On this case, you must subclass the UINavigationController, and you must return the proper values from the highest view controller.

class NavigationController: UINavigationController {

    override var shouldAutorotate: Bool {
        if let shouldRotate = topViewController?.shouldAutorotate {
            return shouldRotate
        }
        return tremendous.shouldAutorotate
    }

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if let orientation = topViewController?.supportedInterfaceOrientations {
            return orientation
        }
        return tremendous.supportedInterfaceOrientations
    }

    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
        if let orientation = topViewController?.preferredInterfaceOrientationForPresentation {
            return orientation
        }
        return tremendous.preferredInterfaceOrientationForPresentation
    }
}

The identical logic applies if in case you have a UITabBarController, however as a substitute of the highest view controller, you must use the selectedIndex, and return the properties based mostly on the chosen view controller.

class TabBarController: UITabBarController {

    override var shouldAutorotate: Bool {
        if let viewController = viewControllers?[selectedIndex] {
            return viewController.shouldAutorotate
        }
        return tremendous.shouldAutorotate
    }

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if let viewController = viewControllers?[selectedIndex] {
            return viewController.supportedInterfaceOrientations
        }
        return tremendous.supportedInterfaceOrientations
    }

    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
        if  let viewController = viewControllers?[selectedIndex] {
            return viewController.preferredInterfaceOrientationForPresentation
        }
        return tremendous.preferredInterfaceOrientationForPresentation
    }
}

This manner your embedded controller can management the supported orientations. Oh, by the best way you should utilize this methodology to vary the standing bar fashion.

Constraints

To be able to perceive constraints and the present state of the Auto Structure engine, we should always return to in time and begin the story from the start.

Springs and struts

Bear in mind the primary iPhone? One display screen to rule all of them! 320x480, no constraints, no adaptivity, simply frames and bounds. Positioning views on a set dimension canvas is completely a no brainer, right here is an instance.

class ViewController: UIViewController {

    weak var sq.: UIView!

    var squareFrame: CGRect {
        let midX = view.bounds.midX
        let midY = view.bounds.midY
        let dimension: CGFloat = 64
        return CGRect(
            x: midX-size/2, 
            y: midY-size/2, 
            width: dimension, 
            peak: dimension
        )
    }

    override func loadView() {
        tremendous.loadView()

        let sq. = UIView()
        view.addSubview(sq.)
        sq. = sq.
    }

    override func viewDidLoad() {
        tremendous.viewDidLoad()

        sq..backgroundColor = .yellow
    }

    override func viewDidLayoutSubviews() {
        tremendous.viewDidLayoutSubviews()

        sq..body = squareFrame
    }
}

With the viewDidLayoutSubviews methodology it is tremendous handy to help rotation, I simply need to re-calculate the body of the view each time if the bounding rectangle adjustments. You would possibly suppose hey, that is simple, however what occurs if you must help a lot of system sizes?

Do the maths!

For one single object it is really easy to make the calculations, however normally you have got a couple of view on display screen. These views can have relations to one another, and a simple arithmetic trick can lead you to a whole chaos of body calculations, do you even like arithmetic? There have to be a greater method!

Auto Structure

With iOS6 Apple introduced us the holy grail of format applied sciences. It was the right successor of the earlier system. Everybody adopted it quick, that is why Apple engineers utterly eliminated body based mostly format APIs within the subsequent launch… #justkidding

Aside from the joke, it was the start of a brand new period, an increasing number of gadgets have been born, and with Auto Structure constraints it was tremendous simple to take care of views. Now we should always refactor the earlier instance with format constraints.

class ViewController: UIViewController {

    weak var sq.: UIView!

    override func loadView() {
        tremendous.loadView()

        let sq. = UIView()
        view.addSubview(sq.)
        sq..translatesAutoresizingMaskIntoConstraints = false
        view.addConstraints([
            NSLayoutConstraint(
                item: square, 
                attribute: .width, 
                relatedBy: .equal, 
                toItem: nil, 
                attribute: .width, 
                multiplier: 1.0, 
                constant: 64
            ),
            NSLayoutConstraint(
                item: square, 
                attribute: .height, 
                relatedBy: .equal, 
                toItem: nil, 
                attribute: .height, 
                multiplier: 1.0, 
                constant: 64
            ),
            NSLayoutConstraint(
                item: square,
                 attribute: .centerX, 
                 relatedBy: .equal, 
                 toItem: view, 
                 attribute: .centerX, 
                 multiplier: 1.0, 
                 constant: 0
            ),
            NSLayoutConstraint(
                item: square, 
                attribute: .centerY, 
                relatedBy: .equal, 
                toItem: view, 
                attribute: .centerY,
                multiplier: 1.0, 
                constant: 0
            ),
        ])
        self.sq. = sq.
    }

    override func viewDidLoad() {
        tremendous.viewDidLoad()

        sq..backgroundColor = .yellow
    }
}

As you possibly can see we needn’t manually calculate the body of the view, nonetheless creating constraints programmatically is just not so handy. That is why Apple made the constraint Visible Format Language.

VFL = WTF?

Really this VFL is so dangerous that I do not even need to demo it, however anyway…

class ViewController: UIViewController {

    weak var sq.: UIView!

    override func loadView() {
        tremendous.loadView()

        let sq. = UIView()
        view.addSubview(sq.)
        sq..translatesAutoresizingMaskIntoConstraints = false

        let views: [String:Any] = [
            "view": view, 
            "subview": square
        ]
        let vertical = NSLayoutConstraint.constraints(
            withVisualFormat: "V:[view]-(<=1)-[subview(==64)]", 
            choices: .alignAllCenterX, 
            metrics: nil, 
            views: views
        )

        let horizontal = NSLayoutConstraint.constraints(
            withVisualFormat: "H:[view]-(<=1)-[subview(==64)]",
            choices: .alignAllCenterY, 
            metrics: nil, 
            views: views
        )
        view.addConstraints(vertical)
        view.addConstraints(horizontal)
        self.sq. = sq.
    }

    override func viewDidLoad() {
        tremendous.viewDidLoad()

        sq..backgroundColor = .yellow
    }
}

God forbid the engineer who invented this black magic. 😅

In order you possibly can see we positively have an issue with constraints. Creating all of your constraints sucks, at the very least it may price many many strains of code. After all you should utilize the magical interface builder, however the place’s the enjoyable in the event you simply drag strains?

Creating constraints programmatically isn’t any higher than calculating frames, it can lead you to the identical degree of complexity and even worse, that is why so many third get together frameworks got here alive and ultimately Apple issued the issue as nicely.

I’ve a tremendous article about mastering Auto Structure anchors, I extremely suggest studying it if you wish to get accustomed to anchors. 📖

Anchors

Anchors have been born as a result of Auto Structure had some building flaws.

The NSLayoutAnchor class is a manufacturing unit class for creating NSLayoutConstraint objects utilizing a fluent API. Use these constraints to programmatically outline your format utilizing Auto Structure.

class ViewController: UIViewController {

    weak var sq.: UIView!

    override func loadView() {
        tremendous.loadView()

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

    override func viewDidLoad() {
        tremendous.viewDidLoad()

        sq..backgroundColor = .yellow
    }
}

See, completely rocks! Anchors are the easiest way of utilizing for Auto Structure constraints.

Adaptive format

If you happen to take a look at the present state of built-in apps offered by Apple, you possibly can see that solely a few of them are responsive / adaptive. Generally, apps that utilizing assortment views are easier to adapt for larger screens, or totally different system orientations.

At all times use assortment views, besides if it is only one view on the middle of the display screen, it’s best to construct up your person interfaces utilizing assortment views. It provides you with reusability, decrease reminiscence overhead, scrolling and lots of extra advantages. You do not even need to calculate the silly index paths in case you are utilizing my CollectionView micro framework.

Auto Structure with layers

Auto Structure is nice, however generally you must work with layers instantly. Now on this state of affairs, you continue to need to do some calculations. You’ll be able to simply override the bounds property and replace frames within the didSet block in case you are coping with a view subclass.

override var bounds: CGRect {
    didSet {
        gradientLayer.body = bounds
    }
}

An alternative choice is to override the viewDidLayoutSubviews methodology contained in the view controller, and set the body of the layer based mostly on the brand new bounds.

override func viewDidLayoutSubviews() {
    tremendous.viewDidLayoutSubviews()

    gradientView.gradientLayer.body = gradientView.bounds
}

It’s also possible to use plain previous Key-Worth Observing to watch an objet’s bounds property and replace the body of the layer based on that.


addObserver(
    self, 
    forKeyPath: "bounds", 
    choices: .new, 
    context: nil
)

override func observeValue(
    forKeyPath keyPath: String?, 
    of object: Any?, 
    change: [NSKeyValueChangeKey : Any]?, 
    context: UnsafeMutableRawPointer?
) {
    guard keyPath == "bounds" else {
        return tremendous.observeValue(
            forKeyPath: keyPath, 
            of: object, 
            change: change, 
            context: context
        )
    }
    gradientLayer.body = bounds
}

deinit {
    removeObserver(self, forKeyPath: "bounds")
}

Animating nook radius

Initially if you wish to animate a view whereas utilizing constraint based mostly layouts, you must do one thing like this.

widthConstraint.fixed = 64
UIView.animate(withDuration: 0.5, animations: {
    view.layoutIfNeeded()
}, completion: nil)

Now if you wish to animate the nook radius of a view, you possibly can at all times use the standard method, and set the cornerRadius property of the layer on a bounds change.

However, we have this fancy new UIViewPropertyAnimator API since iOS 10.

self.imageView.layer.cornerRadius = 16
UIViewPropertyAnimator(length: 2.5, curve: .easeInOut) {
    self.imageView.layer.cornerRadius = 32
}.startAnimation()

It is fairly easy, you possibly can even apply a cornerMask to spherical simply a few of the corners. The layer based mostly format examples are contained in the offered supply code for the article alongside with a whole pattern for every Auto Structure method. You’ll be able to obtain or clone it from the The.Swift.Dev tutorials repository.



Supply hyperlink

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments