DSL library that implements hierarchy of views and constraints declaratively
- iOS 13+
- Swift 5.4+
SwiftLayout supply SPM only
dependencies: [
.package(url: "https://github.com/ioskrew/SwiftLayout", from: "1.0.0"),
],
DSL by @resultBuilder make easy allow relation of superview and subviews and autolayout constraints declaratively.
let root: UIView
let red: UIView
let blue: UIView
// root.addSubview(red)
root {
red
}
// root.addSubview(red)
// root.addSubview(blue)
root {
red
blue
}
// root.addSubview(red)
// red.addSubview(blue)
root {
red {
blue
}
}
Anchors is declarative model of relation of constraints.
- hiding attributes, is treated as having same attribute between two views.
- if only use Anchors(...), that will make constraints with superview to second items.
let root: UIView
let red: UIView
root {
red.anchors {
// top, leading, trailing, bottom of red constraint attributes is equal to superview(root)
// 4 constraints of red firstItem and root secondItem are made by below codes.
Anchors(.top, .leading, .trailing, .bottom)
// or more specifically
Anchors(.top, .leading, .trailing, .bottom).equalTo(root)
// or individually
Anchors(.top) // Anchors(.top).equalTo(root) or Anchors(.top).equalTo(root, attribute: .top)
Anchors(.leading) // Anchors(.top).equalTo(root)
Anchors(.traliling) // Anchors(.top).equalTo(root)
Anchors(.equal) // Anchors(.top).equalTo(root)
}
}
also you can make custom Anchors property
let root: UIView
let red: UIView
extension Anchors {
static var boundary: Anchors { .init(.top, .leading, .trailing, .bottom) }
}
root {
red.anchors {
Anchors.boundary // top and leading and trailing and bottom constraints of red has equal relation to same attributes of root view.
}
}
// or be able root view to red view relations like below
root.anchors {
Anchors.boundary.equalTo(red)
}.subviews { // subviews should be in LayoutBuilder in subviews function after anchors.
red
}
// two DSL declaration bring same result.
do you want red is up blue is down and same height?
good you can write like this.
let root: UIView
let red: UIView
let blue: UIView
root {
red.anchors {
Anchors(.top, .leading, .trailing)
}
blue.anchors {
Anchors(.leading, .trailing, .bottom)
Anchors(.top).equalTo(red, attribute: .bottom)
Anchors(.height).equalTo(red) // or Anchors(.height).equalTo(red, attribute: .height)
}
}
Sometimes for a few reasons, you want the object may want to create a view directly, without containing the property of the view.
in this case, Anchors cannot have constraint with variable name. so you can allow string label to view.
root {
RedLabel().identifiying("red").anchors {
Anchors.cap
}
blue.anchors {
Anchors.shoe
Anchors(.top).equalTo("red", attribute: .bottom)
Anchors(.height).equalTo("red")
}
}
you can also get view from Deactivable.
let deactivable = root {
UILabel().identifying("red")
}.active()
let label = deactivable.viewForIdentifier("red") as? UILabel
layouts can use conditional blocks.
let showRed = true
root {
if showRed {
red
} else {
blue
}
}
root {
red.anchors {
Anchors.boundary
}
}
in this time, DSL declaration is just structure of views and constraints. none of this added to superview or active of constraints.
you must call active() function for complete all this. and result of Deactivable should has retained.
final class ViewController: UIViewController {
let red = UIView()
var deactivable: Deactivable?
override func viewDidLoad() {
super.viewDidLoad()
deactivable = view { // view is that of ViewController
red.anchors {
Anchors.boundary // boundary is custom property in samples.
}
}.active()
}
}
release deactivable or call of deactivate?.deactive() make release all subviews and constraints.
protocol LayoutBuilding make easy to updating.
final class ViewController: UIViewController, LayoutBuilding {
var showRed: Bool = true
let red: UIView
let blue: UIView
var deactivable: Deactivable?
var layout: some Layout {
view {
if showRed {
red
} else {
blue
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
updateLayout() // updateLayout is procedure for active and call some apis
// or you can animation by below
updateLayout(animated: true)
}
}
-
call animationDisable() make that blocks to escape from animations.
root { red.anchors { Anchors.boundary }.animationDisable() }
SwiftLayout also providing an simple solution for preview of SwiftUI.
If your view or view controller implement protocol of LayoutBuilding. you write like below.
class ViewController: UIViewController, LayoutBuilding {...}
extension View[Controller]: LayoutView[Controller]Representable {} // more than this is not required.
struct ViewController_Previews: PreviewProvider {
static var previews: some View {
ViewController() // and enable preview features like previewDevice
}
}
- greater than or equal, less than or equal
- UILayoutGuide also possible assign to item
root {
red.anchors {
Anchors.boundary.eqaulTo(root.safeAreaLayoutGuide)
}
}