Skip to content

WeZZard/CTVFL

Repository files navigation

CTVFL: Compile-Time Visual Format Language

Build Status Carthage Compatible

中文

CTVFL is a tiny framework offers compile-time safe Visual Format Langauge to make Auto Layout painless on Apple platforms.

Examples

// 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);

Performance Benchmark

10000 times constraitns build time on iPhone X:

1 View Constraining 2 Views Constraining 3 Views Constraining
  • See more in CTVFLBenchmark app in CTVFLBenchmark/ folder.

Advantages

  • 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.

Usages

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.

Making Native Cocoa Constraints

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)

Making and Installing Constraints with a Collective Control Point

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

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.

Known Issues

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)

License

MIT License