Code-Based Layout in Swift
CBL in SwiftShota Nakagami
View Slide
Talking about "CBL"
!
CBLCode-Based layout
I!Code-Based Layout
How to layout ?4 StoryBoard4 XIB4 Code-BasedʢProgrammaticallyʣ
Storyboard & XIB features4 Ease of use☺4 Segue☺4 Visual (multi device screen size)4 Constraint Checker☺4 Non-Reusable"4 Hard to Resolve Conflict"4 Hard to review (XML)"
Testable?!class ViewController: UIViewController {let dependency: Dependency// Dependency Injection with Initializerinit(dependency: Dependency) { self.dependency = dependency }}4 NG: Storyboard☠no initializer...4 OK: XIB"4 OK: CBL"
CBL features4 Reusable☺4 Speedy☺4 Easy to Resolve Conflict☺4 Easy to Review☺4 Cost of Learning"4 Non-Visual"
StoryBoard XIB Codebased! Segue ✅# Visual ✅ ✅$ Compiler Check ✅ ✅% Testable (DI) ✅ ✅♻ Reusable ✅' Less Conflict ✅( Reviewable ✅
6 Tips for CBL
1. Initialization Closure2. "Then"3. Custom View Class4. lazy var5. "SnapKit"6. UIStackView
class ViewController: UIViewController {let priceLabel = UILabel()let imageView = UIImageView()override func viewDidLoad() {super.viewDidLoad()priceLabel.numberOfLines = 2priceLabel.textColor = .redpriceLabel.font = .boldSystemFont(ofSize: 14)imageView.contentMode = .scaleAspectFillimageView.clipsToBounds = trueimageView.layer.cornerRadius = 4// addSubview, AutoLayout...}}
messy viewDidLoad...
1. Initialization Closure
class ViewController: UIViewController {let priceLabel: UILabel = {let label = UILabel()label.numberOfLines = 2label.textColor = .redlabel.font = .boldSystemFont(ofSize: 14)return label}()let imageView: UIImageView = {let view = UIImageView()view.contentMode = .scaleAspectFillview.clipsToBounds = trueview.layer.cornerRadius = 4return view}()override func viewDidLoad() {super.viewDidLoad()// addSubview, AutoLayout...}}
viewDidLoad is clean, but...
class ViewController: UIViewController {let priceLabel: UILabel = { // type annotationlet label = UILabel() // renaminglabel.numberOfLines = 2label.textColor = .redlabel.font = .boldSystemFont(ofSize: 14)return label // return}()let imageView: UIImageView = { // type annotationlet view = UIImageView() // renamingview.contentMode = .scaleAspectFillview.clipsToBounds = trueview.layer.cornerRadius = 4return view // return}()override func viewDidLoad() {super.viewDidLoad()// addSubview, AutoLayout...}}
2. "Then"https://github.com/devxoul/Then
class ViewController: UIViewController {let priceLabel = UILabel().then {$0.numberOfLines = 2$0.textColor = .red$0.font = .boldSystemFont(ofSize: 14)}let imageView = UIImageView().then {$0.contentMode = .scaleAspectFill$0.clipsToBounds = true$0.layer.cornerRadius = 4}override func viewDidLoad() {super.viewDidLoad()// addSubview, AutoLayout...}}
3. Custom View Class
class ViewController: UIViewController {let priceLabel = PriceLabel()let imageView = ItemImageView()override func viewDidLoad() {super.viewDidLoad()}}
private let captureButton = CaptureButton().then {$0.onTapped = { [weak self] inself?.camera.capture()}}
private let captureButton = CaptureButton().then {$0.onTapped = { [weak self] in // Compile Errorself?.camera.capture() // Compile Error}}
4. lazy var
private lazy var captureButton = CaptureButton().then {$0.onTapped = { [weak self] inself?.camera.capture() // OK}}
Codebased AutoLayout
NSLayoutAnchor ?// addSubview, AutoLayout...view.addSubview(button)button.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor).active = truebutton.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor).active = truebutton.topAnchor.constraintEqualToAnchor(view.topAnchor).active = truebutton.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor).active = true
5. "SnapKit"https://github.com/SnapKit/SnapKit
view.addSubview(button)// button.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor).active = true// button.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor).active = true// button.topAnchor.constraintEqualToAnchor(view.topAnchor).active = true// button.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor).active = truebutton.snp.makeConstraints { $0.edges.equalToSuperview() }
// readablebutton.snp.makeConstraints {$0.center.equalToSuperview()$0.size.equalTo(CGSize(width: 64, height: 64))}// Support Safe AreatableView.snp.makeConstraints {$0.top.bottom.equalTo(view.safeAreaLayoutGuide)$0.leading.trailing.equalToSuperview()}
6. UIStackView
UIStackView = Stack "Layouted" ViewLess Constraints!stackView.addArrangedSubview(label)stackView.addArrangedSubview(imageView)stackView.addArrangedSubview(button)label.snp.makeConstraints { $0.height.equalTo(20) }button.snp.makeConstraints { $0.height.equalTo(60) }
Summary
CBL: Code-Based layout4 Testable, Speedy, and Reusable4 Use "6 Tips" to clean codeWhen to Use4 Use CBL for simple layout4 Use XIB for complicated layout4 Use StoryBoard for segue