CTVFL is a tiny framework offers compile-time safe Visual Format Langauge to make Auto Layout painless on Apple platforms.
// Make constraints and install.
constrain {
withVFL(H: view1 - view2)
}
constrain {
withVFL(H: view3 | view4, options: .alignAllCenterX)
}
// Just make constraints.
let constraints = withVFL(H: view1 - 10 - view2);
10000 times constraitns build time on iPhone X:
- See more in CTVFLBenchmark app in
CTVFLBenchmark/
folder.
-
Safe
No runtime exceptions anymore. All errors are eliminated at compile-time.
-
High performance
CTVFL implements a virtual machine to execute compile-time checked Auto Layout syntax.
-
Supports layout guide
Layout guides can only to be placed at the head or tail side of the syntax.
Most of the time, you only have to use three functions in CTVFL:
-
withVFL(H:)
generates horizontal constraints. -
withVFL(V:)
generates vertical constraints. -
constrain
collects constraints generated inside its closure and install those constraints to relative views.
You can just make constraints without installing them to the views with
a call to withVFL
outside the constrain
function.
// [button]-[textField]
let constraint0 = withVFL(H: button - textField)
// [button(>=50)]
let constraint1 = withVFL(H: button.where(>=50))
// |-50-[purpleBox]-50-|
let constraint2 = withVFL(H: |-50 - purpleBox - 50-|)
// V:[topField]-10-[bottomField]
let constraint3 = withVFL(V: topField - 10 - bottomField)
// [maroonView][blueView]
let constraint4 = withVFL(H: maroonView | blueView)
// [button(100@20)]
let constraint5 = withVFL(H: button.where(200 ~ 20))
// [button1(==button2)]
let constraint6 = withVFL(H: button1.where(==button2))
// [flexibleButton(>=70,<=100)]
let constraint7 = withVFL(H: flexibleButton.where(>=70, <=100))
// |-[find]-[findNext]-[findField(>=20)]-|
let constraint8 = withVFL(H: |-find - findNext - findField.where(>=20)-|)
// view.topAnchor.constraint(equalToSystemSpacingBelow: view.safeAreaLayoutGuide.topAnchor)
let constraint9 = withVFL(V: view.safeAreaLayoutGuide - view)
Wrapping withVFL
function calls with constrain
function's closure makes
the framework installs the generated constraints and encapsulates them in
a CTVFLConstraintGroup
instance. You can control the whole group of
generated constriants with this instance.
var view1VerticalConstraints = [NSLayoutConstraint]!
let constraintGroup = constrain {
withVFL(H: |-view1 - 100 - view2-|)
view1VerticalConstraints = withVFL(V: |-view1-|)
withVFL(V: |-view2-|)
}
view1VerticalConstraints.forEach({$0.isActive = false})
// ... something happened
if !constraintGroup.areAllAcrive {
constraintGroup.setActive(true)
}
// ... something happened
constraintGroup.uninstall()
CTVFLTransaction
runs an implicit transaction. Any constraints generated
by withVFL
series functions would not be collected by this implicit
transaction. Once you begin an explicit transaction by calling begin()
,
the transaction begins to collect all the layout constraints generated by
withVFL
series functions. CTVFLTransaction
uses pthread specific data
to store collected constraints. It's independent per thread and thread
safe.
Of course, calls to CTVFLTransaction.begin()
and
CTVFLTransaction.end()
shall be balanced.
The compiler cannot correctly recognize following syntax:
withVFL(H: view1 - view2 - view3)
This is because of a bug in Swift compiler which puts the operator overload:
extension Numeric {
public func - (lhs: Self, rhs: Self) -> Self {
...
}
}
at here. You can use syntax concatenation to workaround.
let view1_2 = view1 - view2
withVFL(H: view1_2 - view3)
MIT License