Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SwiftLayout을 소개합니다

kakao
December 08, 2022

SwiftLayout을 소개합니다

#iOS #OpenSource

SwiftLayout은 UIKit을 사용하면서 아쉬웠던 부분들을 개선하기 위해 선언적인 구문으로 UIKit을 사용할 수 있도록 도와주는 오픈소스 라이브러리입니다.
SwiftLayout의 사용법을 코드와 함께 살펴보며 저희가 라이브러리를 개발하는 과정에서 격은 여러 의미 있었던 경험도 함께 공유하고자 합니다.

발표자 : aiden.h
저는 카카오톡을 개발하고 있는 iOS 개발자 aiden 입니다.

jay.choi
저는 카카오톡을 개발하고 있는 iOS 개발자 jay 입니다.

kakao

December 08, 2022
Tweet

More Decks by kakao

Other Decks in Programming

Transcript

  1. 선언형 문법으로 가독성 높은 UIKit 코드 작성하기 SwiftLayout을 소개합니다. 이희종

    aiden.h, 최광훈 jay.choi 카카오 if(kakao)2022 Copyright 2022. Kakao Corp. All rights reserved. Redistribution or public display is not permitted without written permission from Kakao.
  2. - Interface Builder - 유지보수가 어렵다. - Con fl ilct

    발생시 해결이 쉽지 않다. - Code - 명령형 코드로 화면의 형태를 파악하기 어렵다. - 분기처리로 코드의 파편화가 심해질 수 있다. UIKit
  3. @LayoutBuilder var layout: some Layout { view.sublayout { thumbnailImageView contentView.sublayout

    { titleLabel descriptionLabel menuDetailBottomView } } } View Thumbnail ContentView Title Description BottomView
  4. @LayoutBuilder var layout: some Layout { view.sublayout { thumbnailImageView.anchors {

    ... } contentView.sublayout { titleLabel descriptionLabel menuDetailBottomView } } }
  5. @LayoutBuilder var layout: some Layout { view.sublayout { thumbnailImageView.anchors {

    Anchors.top.equalToSuper() } contentView.sublayout { titleLabel descriptionLabel menuDetailBottomView } } }
  6. @LayoutBuilder var layout: some Layout { view.sublayout { thumbnailImageView.anchors {

    Anchors.top.equalToSuper(constant: 5) } contentView.sublayout { titleLabel descriptionLabel menuDetailBottomView } } }
  7. @LayoutBuilder var layout: some Layout { view.sublayout { thumbnailImageView.anchors {

    Anchors.top.equalTo(view) } contentView.sublayout { titleLabel descriptionLabel menuDetailBottomView } } }
  8. @LayoutBuilder var layout: some Layout { view.sublayout { thumbnailImageView.anchors {

    Anchors.top.equalTo(view.safeAreaLayoutGuide) } contentView.sublayout { titleLabel descriptionLabel menuDetailBottomView } } }
  9. @LayoutBuilder var layout: some Layout { view.sublayout { thumbnailImageView.anchors {

    Anchors.bottom.equalTo(contentView, attribute: .top, constant: -20) } contentView.sublayout { titleLabel descriptionLabel menuDetailBottomView } } }
  10. @LayoutBuilder var layout: some Layout { view.sublayout { thumbnailImageView.anchors {

    Anchors.height.equalTo(thumbnailImageView, attribute: .width) Anchors.cap(self.view.safeAreaLayoutGuide, offset: 20) } contentView.anchors { Anchors.top.equalTo(thumbnailImageView, attribute: .bottom, constant: 20) Anchors.shoe(self.view.safeAreaLayoutGuide, offset: 20) }.sublayout { titleLabel.anchors { Anchors.top Anchors.leading } descriptionLabel.anchors { Anchors.top.equalTo(titleLabel, attribute: .bottom, constant: 5) Anchors.horizontal() } menuDetailBottomView.anchors { Anchors.shoe() } } } }
  11. final class MenuDetailViewController: UIViewController { ... @LayoutBuilder var layout: some

    Layout { ... } override func viewDidLoad() { super.viewDidLoad() // Activate Layout } ... }
  12. final class MenuDetailViewController: UIViewController { ... @LayoutBuilder var layout: some

    Layout { ... } override func viewDidLoad() { super.viewDidLoad() // Activate Layout self.layout.finalActive() } ... }
  13. final class MenuDetailViewController: UIViewController { ... @LayoutBuilder var layout: some

    Layout { ... } var activation: Activation? override func viewDidLoad() { super.viewDidLoad() // Activate Layout self.activation = self.layout.active() } ... }
  14. final class MenuDetailViewController: UIViewController { ... @LayoutBuilder var layout: some

    Layout { ... } var activation: Activation? override func viewDidLoad() { super.viewDidLoad() // Activate Layout self.activation = self.layout.active() } ... }
  15. final class MenuDetailViewController: UIViewController { ... @LayoutBuilder var layout: some

    Layout { ... } var activation: Activation? override func viewDidLoad() { super.viewDidLoad() // Activate Layout self.activation = self.layout.active() } func someMethod() { self.activation = self.layout.update(fromActivation: self.activation) } ... }
  16. final class MenuDetailViewController: UIViewController { ... @LayoutBuilder var layout: some

    Layout { ... } var activation: Activation? override func viewDidLoad() { super.viewDidLoad() // Activate Layout self.activation = self.layout.active() } func someMethod() { self.activation?.deactive() } ... }
  17. final class MenuDetailViewController: UIViewController { ... @LayoutBuilder var layout: some

    Layout { ... } var activation: Activation? override func viewDidLoad() { super.viewDidLoad() // Activate Layout self.activation = self.layout.active() } func someMethod() { self.activation = nil } ... }
  18. @LayoutBuilder var layout: some Layout { self.sublayout { toppingTitleLabel.anchors {

    ... } showToppingButton .config { $0.setImage(.init(systemName: isShowTopping ? "chevron.down": "chevron.up"), for: .normal) } .anchors { ... } if isShowTopping { toppingStackView .anchors { ... } .sublayout { ... } } orderButton.anchors { if isShowTopping { Anchors.top.equalTo(toppingStackView, attribute: .bottom, constant: 15) } else { Anchors.top.equalTo(toppingTitleLabel, attribute: .bottom, constant: 15) } ... } } }
  19. final class MenuDetailBottomView: UIView { ... var activation: Activation? @LayoutBuilder

    var layout: some Layout { ... } var isShowTopping: Bool = false override init(frame: CGRect) { super.init(frame: frame) self.activation = self.layout.active() } ... }
  20. final class MenuDetailBottomView: UIView { ... var activation: Activation? @LayoutBuilder

    var layout: some Layout { ... } var isShowTopping: Bool = false override init(frame: CGRect) { super.init(frame: frame) self.activation = self.layout.active() } ... }
  21. final class MenuDetailBottomView: UIView { ... var activation: Activation? @LayoutBuilder

    var layout: some Layout { ... } var isShowTopping: Bool = false { didSet { self.activation = self.layout.update(fromActivation: self.activation!) } } override init(frame: CGRect) { super.init(frame: frame) self.activation = self.layout.active() } ... }
  22. final class MenuDetailBottomView: UIView, Layoutable { ... var activation: Activation?

    @LayoutBuilder var layout: some Layout { ... } var isShowTopping: Bool = false { didSet { self.activation = self.layout.update(fromActivation: self.activation!) } } override init(frame: CGRect) { super.init(frame: frame) self.activation = self.layout.active() } ... }
  23. final class MenuDetailBottomView: UIView, Layoutable { ... var activation: Activation?

    @LayoutBuilder var layout: some Layout { ... } var isShowTopping: Bool = false { didSet { self.activation = self.layout.update(fromActivation: self.activation!) } } override init(frame: CGRect) { super.init(frame: frame) self.activation = self.layout.active() } ... }
  24. final class MenuDetailBottomView: UIView, Layoutable { ... var activation: Activation?

    @LayoutBuilder var layout: some Layout { ... } var isShowTopping: Bool = false { didSet { self.sl.updateLayout() } } override init(frame: CGRect) { super.init(frame: frame) self.sl.updateLayout() } ... }
  25. final class MenuDetailBottomView: UIView, Layoutable { ... var activation: Activation?

    @LayoutBuilder var layout: some Layout { ... } @LayoutProperty var isShowTopping: Bool = false override init(frame: CGRect) { super.init(frame: frame) self.sl.updateLayout() } ... }
  26. final class MenuDetailBottomView: UIView, Layoutable { ... var activation: Activation?

    @LayoutBuilder var layout: some Layout { ... } @AnimatableLayoutProperty(duration: 0.5) var isShowTopping: Bool = false override init(frame: CGRect) { super.init(frame: frame) self.sl.updateLayout() } ... }