Home > Enterprise >  UIKit Programmatically: Help on different approaches when writing UI Code
UIKit Programmatically: Help on different approaches when writing UI Code

Time:03-31

Lately, I started learning UIKit programmatically, and I noticed different approaches of writing and organizing your UI code.

The first one is, declaring a constant with a closure, which will execute immediately and returns a certain UI element.

Example (1st Approach):

let mainLabel: UILabel = {
    let v = UILabel()
    v.translatesAutoresizingMaskIntoConstraints = false
    v.text = "dummy text..."
    //...
    return v
}()

The second one is, declaring a UI element computed var, which is a read-only var that returns a certain UI element.

Example (2nd Approach):

var mainLabel: UILabel {
    let v = UILabel()
    v.translatesAutoresizingMaskIntoConstraints = false
    v.text = "dummy text..."
    //...
    return v
}

And the third one is, declaring a factory function to create the UI element, and return it.

Example (3rd Approach):

private func createMainLabel(with text:String) -> UILabel {
    let v = UILabel()
    v.translatesAutoresizingMaskIntoConstraints = false
    v.text = text
    //...
    return v
}

Now, I assume 2nd and 3rd options are quite similar, both creating a new UI element instance every time, and the 1st approach is creating that element only once. But I'd like to understand if there's more to it, why would I use one over the other, what counts as best practice, and also, are there more preferred ways to write UI code that I didn't mention here?

CodePudding user response:

The first one is a common and - in my opinion - a very good approach. Everything you need to know about that UI element is in one place. But note that if you need to reference self there, you need to make the UI element a lazy var, because self is not available before all properties (that are not lazy or optional) have been initialized. For example:

let text = "Hello"

lazy var mainLabel: UILabel = {
    let v = UILabel()
    v.translatesAutoresizingMaskIntoConstraints = false
    v.text = self.text  // <-- here
    //...
    return v
}()

The second one seems strange to me. I can't think of a scenario where that would be useful. Every time you access the variable you deal with a new copy. I think this is at least confusing.

The third one is especially useful when you don't know in advance how many instances of that UI element you're going to need.

A common approach that is missing is something like this:

let mainLabel = UILabel()
let secondaryLabel = UILabel()

override func viewDidLoad() {
    super.viewDidLoad()
    
    mainLabel.translatesAutoresizingMaskIntoConstraints = false
    mainLabel.text = "dummy text..."
    
    secondaryLabel.translatesAutoresizingMaskIntoConstraints = false
    secondaryLabel.text = "secondary dummy text ..."

    // build view hierarchy
    // set AutoLayout constraints
}

I think it's mainly a matter of taste if you prefer the first approach or this last one. The last one makes it easier to see all the properties at a glance without scrolling. If you want to know more about a certain UI element, you see everything, including its AutoLayout constraints, in the viewDidLoad method (or in some setupView() method that you call there).

  • Related