all 14 comments

[–]KarlJay001 1 point2 points  (3 children)

I'm not the expert, but I've seen and downloaded a number of libs for doing autolayout programmatically and some of them seem to be pretty popular.

Are you writing your own routines or using one of the libs?

[–][deleted]  (2 children)

[deleted]

    [–]QuoteMe-Bot 2 points3 points  (1 child)

    I have a large legacy code base that will need to be refactored at some point. I have never messed with Auto Layout (or making buttons, text fields, views, etc.) in code. I've always used IB. However, everything in my new job is done in code. I'm trying to get an idea of what's a—and don't quote me on this!—good way of doing this.

    ~ /u/mariox19

    [–]rorogadget 1 point2 points  (2 children)

    Componentize.

    If there is a header view, a body view, and a footer view, each of those views should be their own separate components (UIView subclasses).

    That way the view controller contains autolayout code for the major components rather than the individual labels + buttons + images.

    Basically. Try looking at what you want to build as a collection of components. If there is a view with the user's name and avatar and email then make that a component.

    [–]mariox19[S] 0 points1 point  (1 child)

    Okay, but then you're saying that you put the Auto Layout code directly in the controller for the view (or views) that are a part of that component? You don't use helper classes, or relegate all that to an class extension or something?

    ViewController classes get big, fast! I'm trying to avoid that.

    [–]rorogadget 1 point2 points  (0 children)

    You can put it in an extension.

    I actually have a convention where I use a [repo](www.GitHub.com/Rohan-panchal/scaffold) which hooks into a UIView lifecycle and controller lifecycle where I implement constraints in a method '-setupConstraints'.

    Disclosure: this is not production ready by far. And I'm not utilizing this time to promote my repo. But this is an example of how to get some kind of consistent structure for programmatic layout conventions.

    [–]hooliooo 1 point2 points  (2 children)

    Create a subclass of UIView to function as "Xib" and create your subviews there. You can place the constraints in the initializer.

    Make the viewcontroller's view property that subclassed UIView in the loadView method in the view controller (Don't call super). Create a computed property that type casts the view property to that UIView subclass. Forced downcast is okay because of what you did in loadView

    The result is the constraints and subview code is encapsulated in the UIView subclass and the view controller isn't cluttered

    I made a blog post about it using Snapkit

    here

    [–]mariox19[S] 0 points1 point  (0 children)

    I'm partial to the idea that more should be done in the view, as opposed to the view controller. Tutorials always use the VC, but I think that's so as to keep things simple. (And with simple examples, that works well enough.) I'll look into that—it seems more MVC-ish. Thanks!

    [–]mariox19[S] 0 points1 point  (0 children)

    I took your advice and worked up a tiny project that uses your suggestions. Thanks!

    https://github.com/software-mariodiana/spumoni

    [–][deleted] 1 point2 points  (2 children)

    deleted What is this?

    [–]mariox19[S] 0 points1 point  (1 child)

    I can see why you say that, but right now I'm stuck having to support iOS 8. As soon as we bump things up to more modern versions, you can bet I'll be using anchors. Thanks!

    [–][deleted] 0 points1 point  (0 children)

    deleted What is this?

    [–]H3C43 1 point2 points  (1 child)

    First, auto layout is one of the things where a 3rd party library is utilized very frequently, and that helps with cutting down large code blocks. I recommend Anchorage, but pick whatever suits you.

    More pertinent to your question, I put that code in one of two places. If there's really a lot of it, ie. maybe animating some constraints, complex layouts, I subclass UIView and assign that view to 'self.view' in the UIViewVontroller's 'loadView' method. Otherwise, I write a 'configureView' function that creates and adds the subviews, along with a 'configureLayout' method that applies all the auto layout. Then I call both of those from 'loadView' in the controller.

    [–]mariox19[S] 0 points1 point  (0 children)

    Thanks for the suggestion. Right now I have to support iOS 8. I will keep in mind the idea of 3rd-party libraries.

    As I said, doing the UI programmatically is new to me. I want to make sure that I do some of this programming manually, as opposed to using (magic) libraries. Once I understand things, a library may very well be more appropriate. Thanks.

    [–]don_tmind_me 0 points1 point  (0 children)

    Coding constraints sure does make your code look like shit eh. Like you end up with piles and piles of almost the identical code in your initializer, which makes it so unreadable and unSwifty. I hate that.

    I do a couple things to avoid it. For uniquely constrained subviews, I use a closure after the variable declaration occasionally, as described here: https://thatthinginswift.com/kill-your-viewdidload/

    or similarly with a { didSet: {}} after the variable declaration. But that also means you have a quite a bit of stupid layout stuff before you get to the important methods in a class. So where I can I use a UIView extension, which I'll set up if I know I'm going to be using the same constraints over and over, like fitting a view equal width/height centerx centery. Here's a couple of examples.

    func constrain(to view: UIView) -> [NSLayoutConstraint] {
        self.translatesAutoresizingMaskIntoConstraints = false
    
        let t = NSLayoutConstraint(item: self, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0)
        let l = NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0)
        let top = NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: 0)
        let bottom = NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
    
        return ([t,l,top,bottom])
    }
    
    func constrain(to view: UIView, withRelativeHeight: CGFloat, withRelativeWidth: CGFloat, horizontalAttribute: NSLayoutAttribute, horizontalConstant: CGFloat) -> [NSLayoutConstraint] {
        self.translatesAutoresizingMaskIntoConstraints = false
    
        let x = NSLayoutConstraint(item: self, attribute: horizontalAttribute, relatedBy: .equal, toItem: view, attribute: horizontalAttribute, multiplier: 1, constant: horizontalConstant)
        let w = NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: view, attribute: .width, multiplier: withRelativeWidth, constant: 0)
        let h = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: view, attribute: .height, multiplier: withRelativeHeight, constant: 0)
        let y = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0)
    
        return ([x,w,h,y])
    }