- Digression: SwiftUI never removes UIKit
- Describing AutoLayout in code makes us hard to understand.
- MondrianLayout can describe layout which imaginable how it lays out in run time without using InterfaceBuilder
- But we still need to use classical style API
- Wrapping up
Digression: SwiftUI never removes UIKit
Apple is working on creating SwiftUI dedicated. but it does not mean they're removing UIKit from our development.
SwiftUI is just an abstraction layer to describe UI, which depends on who renders it. In macOS, AppKit does. In iOS, UIKit does, In the home screen's widget, something special renderer create surface according to the representation of UI which described by SwiftUI interfaces.
Technically, SwiftUI does not have a function of rendering. It's just a representation. UIKit does instead, but actually, UIKit is also an abstraction layer of rendering. Highly optimized CoreAnimation and CoreGraphics do that using CPU and GPU.
Of course, Apple would update SwiftUI every year, we might be able to create more complex applications using only SwiftUI. Even if so they would still use UIKit inside. It's like creating highly customized components in javascript even if using React, we would use UIKit to do that and bridging with SwiftUI.
That's what I mean, UIKit is still important we would continue to use it at least partially.
Describing AutoLayout in code makes us hard to understand.
Using InterfaceBuilder has a lot of advantages and Not using InterfaceBuilder also has a lot, especially the case of using Swift.
If we create UI from only code, we may write the code in order to set the layout something like this.
let backgroundView = UIView.mock(backgroundColor: .neon(.violet))
let box1 = UIView.mock(backgroundColor: .neon(.red), preferredSize: .largeSquare)
let box2 = UIView.mock(backgroundColor: .neon(.yellow), preferredSize: .largeSquare)
view.addSubview(backgroundView)
view.addSubview(box1)
view.addSubview(box2)
backgroundView.translatesAutoresizingMaskIntoConstraints = false
box1.translatesAutoresizingMaskIntoConstraints = false
box2.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
backgroundView.topAnchor.constraint(equalTo: view.topAnchor),
backgroundView.leftAnchor.constraint(equalTo: view.leftAnchor),
backgroundView.rightAnchor.constraint(equalTo: view.rightAnchor),
backgroundView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
box1.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),
box1.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 10),
box1.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10),
box2.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),
box2.leftAnchor.constraint(equalTo: box1.rightAnchor, constant: 10),
box2.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10),
box2.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10)
])
Did you get to see what looks this code creates?
As you can see, describing AutoLayout constraints in code makes developer hard to understand. Additionally, modifying the layout is also hard. Imagine that if you need to swap those 2 boxes, you will write multiple lines again. You know what, updating layout often causes in business.
UIKit got UIStackView
to solve this problem, you need to add another layer though.
MondrianLayout can describe layout which imaginable how it lays out in run time without using InterfaceBuilder
So how MondrianLayout changes it.
let backgroundView = UIView.mock(backgroundColor: .neon(.violet))
let box1 = UIView.mock(backgroundColor: .neon(.red), preferredSize: .largeSquare)
let box2 = UIView.mock(backgroundColor: .neon(.yellow), preferredSize: .largeSquare)
view.mondrian.buildSubviews {
HStackBlock(spacing: 10) {
box1
box2
}
.padding(10)
.background(backgroundView)
}
buildSubviews
does followings:
- Creates
NSLayoutConstraint
- Creates
UILayoutGuide
to lay out with stacking in line. - Adds views which used in the representation to the parent view.
- Hierarchy also respects.
backgroundView
would be added behind the boxes. - Turns off
translatesAutoresizingMaskIntoConstraints
We can swap those boxes by just like this. Changing the order of describing boxes.
HStackBlock(spacing: 10) {
box2
box1
}
But we still need to use classical style API
As explained above section, that uses Structured layout API to describe layout. However we might still need to use the style like using plain API to hold the flexibility of AutoLayout.
To support the case such as, MondrianLayout gives us the API which can describe AutoLayout constraints each condition. Which is like using the plain API but it's more fluently.
let backgroundView = UIView.mock(backgroundColor: .neon(.violet))
let box1 = UIView.mock(backgroundColor: .neon(.red), preferredSize: .largeSquare)
let box2 = UIView.mock(backgroundColor: .neon(.yellow), preferredSize: .largeSquare)
view.addSubview(backgroundView)
view.addSubview(box1)
view.addSubview(box2)
mondrianBatchLayout {
backgroundView.mondrian.layout.edges(.toSuperview)
box1.mondrian.layout
.top(.toSuperview, 10)
.left(.toSuperview, 10)
.bottom(.toSuperview, -10)
box2.mondrian.layout
.top(.toSuperview, 10)
.left(.to(box1).right, 10)
.right(.toSuperview, -10)
.bottom(.toSuperview, -10)
}
Wrapping up
MondrianLayout provides 2 ways to describe layout in code.
- Structured layout API - describing layout ergonomically.
- Classical style layout API - describing layout constraints fully controllable.
Those APIs can integrate each other, we can choose the best way to describe each case.
I hope you enjoy describing layout programmatically using MondrianLayout.