Back

InterfaceBuilder.swift

mmackh

TL;DR

InterfaceBuilder.swift lets you quickly build complex UIKit layout in code, speed up native app development and is completely open source. This project is in beta and not officially supported by Apple. SPM URL: https://github.com/mmackh/BaseComponents.git

Why not SwiftUI?

The SwiftUI approach to layout in code is objectively fantastic and has inspired many aspects of this library. On the flip side, actual SwiftUI layout behaviour is very similar to self-sizing elements in HTML and abstracts many complexities to the underlying mystery layout engine. As with HTML, minor changes to the engine result in widely different layout behaviour. Whether this approach makes sense or not is debatable, but one thing is for certain: iOS has not (initially) been designed for, or particularly good at, self-sizing. In conjunction with several ways to cause unexpected redraws through Combine, it’s no wonder performance remains an issue. Even on devices that can technically run a desktop-class OS (iPhones), scrolling performance is often bad.

Since SwiftUI (plus new APIs) come bundled with OS releases, this is a recipe for broad range of inconsistencies. One could understand Apple’s strategy to get users up-to-date by forcing developers to aggressively drop older releases (and thus dropping support for still great, but older, phones). Terrible workarounds aside, this objectively sucks. Unfortunately, there’s not a lot Apple can do to fix such fundamental flaws. If SwiftUI weren’t compiled, this would be a whole different story.

InterfaceBuilder.swift is built on top of UIKit. No missing components, a predictable layout engine that is completely open source and with solid performance.

Implementation Details

InterfaceBuilder.swift is a @resultsBuilder syntax abstraction for SplitView + ScrollingView and comes bundled with the BaseComponents Swift Package.

Build your layout tree in your UIViewController’s viewDidLoad(). Use the convenience method build(). It’s important to avoid retain cycles when working with closures and here it’s no different. If you’re certain that there’s no long running network task within your layout closures, you can get away with [unowned self] most of the time. To animate any changes or perform a layout pass, call invalidateLayout() on the Tree.

VSplit, HSplit, VScroll & HScroll

These components are the foundation building blocks of InterfaceBuilder.swift and are the only classes capable of having sub components.

Split vs Scroll

SplitView is similar to UIStackView with a more predictable layout API. The fundamental idea is to predictably layout subviews within the bounds of a given super view. Use ScrollingView, a UIScrollView subclass, when you expect that not all content will fit within the bounds of the super view.

Split Fundamentals

Component type layout priority:

Fixed > Automatic > Percentage > Equal

With this in mind, here’s how you’d pin a view to the bottom of the screen:

VSplit {
	Percentage(100) {
		UIView()
	}
	Fixed(44) {
		MyToolbarView()
	}
}

Scroll Fundamentals

Equal & Percent components are not available in Scroll. Automatic, Dynamic and Fixed are the building blocks to layout your views along a scroll view. If Scroll is the root view in a tree, apply a modifier to automatically adhere to the super view's layoutMargins (or safeAreaInsets). This ensures that your UI works well in both portrait and landscape orientation.

modifier: { scrollingView in
	scrollingView.alwaysBounceVertical = true
	scrollingView.automaticallyAdjustsLayoutMarginInsets = true
}

Conclusion

The aim for InterfaceBuilder.swift is:

• A concise & approachable syntax

• Predictable layout across OS versions

• Solid performance

If you have any suggestions, please send me a tweet (@mmackh) or file an issue on GitHub.