A small architecture pattern for view building
ViewBuilder is an architectural pattern that can help you to make your views adaptive by design. By "adaptiveness" I mean that view can easily adapt to changing specification.
The main idea is to namely stop subclassing the UIView class for creating your own views by using the view builder interface implementation, that uses proper layout model, layout placer and layout arranger to build, construct and layout your view according to design. When the specification changes, you can just add new type of your layout model/placer/arranger and pass it to the builder.
These instructions will get you a copy of the project and will allow you running it on your local machine for development and testing purposes. See Deployment for notes on how to deploy the project on a live system.
- iOS 10.0+
- Xcode 10.0+
- Swift 4.2
CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:
$ gem install cocoapodsTo integrate ViewBuilder into your Xcode project using CocoaPods, specify it in your Podfile:
platform :ios, '10.0'
source 'https://github.com/CocoaPods/Specs.git'
target '<Your Target Name>' do
use_frameworks!
pod 'UIViewBuilder', '<release version>'
endThen, run the following command:
$ pod install --repo-updateIf you prefer not to use either of the aforementioned dependency managers, you can integrate ViewBuilder into your project manually by running:
git clone https://github.com/gitvalue/ViewBuilder.gitand importing the sources by hand.
Here you will see how to deploy the project on the live system.
When using ViewBuilder you create a layout model, placer and the arranger to build your view.
Layout model is a structure that holds all the subviews forming your view, configured in a right manner:
///
/// Set of subviews forming the `SomeView`
///
class SomeViewLayoutModel {
/// First label
let firstLabel: UILabel
/// Second label
let secondLabel: UILabel
/// Third label
let thirdLabel: UILabel
/// The designated initializer
init() {
firstLabel = UILabel()
firstLabel.font = .systemFont(ofSize: 14.0, weight: .regular)
secondLabel = UILabel()
secondLabel.font = .systemFont(ofSize: 15.0, weight: .bold)
thirdLabel = UILabel()
thirdLabel.font = .systemFont(ofSize: 16.0, weight: .heavy)
}
}Layout placer is an object that knows how to place the subviews forming your view in the subview hierarchy properly:
///
/// Placer of the `SomeView` subviews
///
class SomeViewLayoutPlacer: LayoutPlacer {
func place(layoutModel: LayoutModel, onView view: UIView) {
guard let layoutModel = layoutModel as? SomeViewLayoutModel else { return }
view.addSubview(layoutModel.firstLabel)
view.addSubview(layoutModel.secondLabel)
view.addSubview(layoutModel.thirdLabel)
}
}Finally, Layout Arranger is an object that knows how to properly place the subviews forming your view in the coordinate space:
import UIKit
import SnapKit
///
/// Arranges the subviews forming the `SomeView` by the 'A' version
///
class SomeViewLayoutArranger: LayoutArranger {
func arrange(layoutModel: LayoutModel, inView view: UIView) {
guard let layoutModel = layoutModel as? SomeViewLayoutModel else { return }
layoutModel.firstLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(10.0)
make.centerX.equalToSuperview()
}
layoutModel.secondLabel.snp.makeConstraints { make in
make.top.equalTo(layoutModel.firstLabel.snp.bottom).offset(10.0)
make.centerX.equalToSuperview()
}
layoutModel.thirdLabel.snp.makeConstraints { make in
make.top.equalTo(layoutModel.secondLabel.snp.bottom).offset(10.0)
make.centerX.equalToSuperview()
make.bottom.equalToSuperview().offset(-10.0)
}
}
}Now, all we need to do to create our view is to pass instances of classes above to the builder:
let model = SomeViewLayoutModel()
let builder: ViewBuilder = DefaultViewBuilder()
builder.set(layoutModel: model)
builder.set(layoutPlacer: SomeViewLayoutPlacer())
builder.set(layoutArranger: SomeViewLayoutArranger())
let someView = builder.build()This repo uses SemVer for versioning. For the versions available, see the tags on this repository.
Feel free to make any improvements needed by developing them in separate feature-branch and creating a pull-request with Dmitry Volosach as a reviewer.
For any questions please fell free to contact Dmitry Volosach.
- Dmitry Volosach - Initial work - dmitry.volosach@gmail.com