관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 1. FlexLayout과 PinLayout 사용 방법 - UIStackView 개선, 속도 향상, 기능 추가, 선언형 본문

iOS 응용 (swift)

[iOS - swift] 1. FlexLayout과 PinLayout 사용 방법 - UIStackView 개선, 속도 향상, 기능 추가, 선언형

jake-kim 2023. 2. 15. 22:58

1. FlexLayout과 PinLayout 사용 방법 - UIStackView 개선, 속도 향상, 기능 추가, 선언형

2. FlexLayout과 PinLayout 사용 방법 - 여백(margin, padding), 특정 뷰(Cell, scrollView), 기타(grow, shrink)

3. FlexLayout과 PinLayout 사용 방법 - 특정 뷰(Cell, scrollView), 기타(grow, shrink)

FlexLayout 이란?

  • UIKit의 UIStackView를 개선한 컴포넌트 (속도 향상, 기능 추가, 선언형)
  • PinLayout과 같이 사용
  • FlexLayout은 UIStackView처럼 axis를 정하고, alignment, distribution을 설정하여 UIStackView와 동일하게 사용

https://github.com/layoutBox/FlexLayout

퍼포먼스 (벤치마크)

  • 아이폰에서의 UICollectionView 렌더링 퍼포먼스
  • UIKit에서 제공하는 UIStackView와 Auto Layout의 퍼포먼스가 가장 안좋다는 것을 확인

https://github.com/layoutBox/LayoutFrameworkBenchmark

  • 구체적인 소요 시간 수치 확인 - flex layout은 auto layout보다 10배정도 빠른 성능
    • UIStackView는 0.14, 0.25, ...
    • auto layout은 0.1, 0.2, ...
    • flex layout은 0.01, 0.02 ...

https://docs.google.com/spreadsheets/d/1sUNdGWBM-d_W13yC7VcfkRXC3owCVsnIublnfW-4xn4/edit#gid=1032991425

  • UIScrollView + UIStackView로 UITableView처럼 만들때 UI들이 버벅이는 현상이 있을때 FlexLayout을 사용하면 용이할 것으로 기대

사용 방법

  • cocoapod
pod 'FlexLayout'
pod 'PinLayout'
  • 대부분 UIView의 extension으로 구현되어 있기 때문에 일반적인 UIView 선언
    • flexView는 UIStackView처럼 사용될 것
import UIKit
import FlexLayout
import PinLayout

class ViewController: UIViewController {
    let flexView = UIView()
}
  • flexView안에 들어갈 UILabel 선언
private let label1: UILabel = {
    let label = UILabel()
    label.text = "label1"
    label.font = .systemFont(ofSize: 20, weight: .regular)
    label.numberOfLines = 1
    return label
}()
private let label2: UILabel = {
    let label = UILabel()
    label.text = "label2"
    label.font = .systemFont(ofSize: 20, weight: .regular)
    label.numberOfLines = 1
    return label
}()
private let label3: UILabel = {
    let label = UILabel()
    label.text = "label3"
    label.font = .systemFont(ofSize: 20, weight: .regular)
    label.numberOfLines = 1
    return label
}()
  • flexLayout을 사용하기 위한 3가지 
    • 1) addItem: 스택뷰의 addArrangedSubview()와 같은 역할
    • 2) flexView의 pin layout 잡기: flexView의 위치 잡기 (일반적인 autolayout사용의 constraint로 잡지 않는것임을 주의)
    • 3) flexView의 children 레이아웃 잡기

1) addItem: 스택뷰의 addArrangedSubview()와 같은 역할

  • .flex.define { } 클로저 안에서 정의
override func viewDidLoad() {
    super.viewDidLoad()
    
    view.addSubview(flexView)
    
    // 1. addItem: addSubview
    flexView.flex.define {
        $0.addItem(label1)
        $0.addItem(label2)
        $0.addItem(label3)
    }
}

2) flexView의 pin layout 잡기: flexView의 위치 잡기 (일반적인 autolayout사용의 constraint로 잡지 않는것임을 주의)

  • viewDidLayoutSubviews나 layoutSubview같은 곳에서 pin을 가지고 flexView의 레이아웃을 고정
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    // 2. pin으로 레이아웃 잡기
    flexView.pin.all(view.pin.safeArea)
}

3) flexView의 children 레이아웃 잡기

  • 2번에서 flexView의 레이아웃을 잡고, 이 단계에서 flexView의 children에 대해서 레이아웃을 설정하는 것
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    // 2. pin으로 레이아웃 잡기
    flexView.pin.all(view.pin.safeArea)
    
    // 3. flexView의 children 레이아웃 잡기
    flexView.flex.layout()
}

(결과)

 

FlexLayout 활용

  • direction - 방향 설정
    • flex.direection(.column)이나 flex.direection(.row)으로 사용
    • column이 디폴트값
flexView.flex.direction(.row).define {
    $0.addItem(label1)
    $0.addItem(label2)
    $0.addItem(label3)
}

flex.direection(.row)

  •  padding
    • flexView와 안에 들어갈 contentView사이의 inset값을 의미
    • padding(20)을 넣으면 UIEdgeInset(top: 20, left: 20, bottom: 20, right: 20)을 넣은것과 동일
// spacing
flexView.backgroundColor = .lightGray
flexView.flex.padding(20).define {
    $0.addItem(label1)
    $0.addItem(label2)
    $0.addItem(label3)
}

  • paddLeft(), paddingRight()... 으로 특정 부분만 패딩값을 간편하게 사용 가능
flexView.flex.paddingLeft(10).define {
    $0.addItem(label1).padding(30, 10)
    $0.addItem(label2).paddingStart(50)
    $0.addItem(label3)
}

  • addItem().width()
    • width를 사용하면 특정 뷰의 너비 설정도 간편하게 관리 가능
flexView.flex.define {
    $0.addItem(label1).width(40)
    $0.addItem(label2)
    $0.addItem(label3)
}

width(40)

  • aspectRatio(of:)
    • 파라미터에 imageView를 넣으면 그 이미지뷰의 비율을 참고하여 자동으로 나머지 한쪽 길이를 맞추어 주는 것
    • width(100).aspectRatio(of: someImageView)를 사용할 경우, someImageView의 비율을 참고하여 너비가 100이 되었을때 높이가 자동으로 계산되도록 하는 것
flexView.flex.define {
    $0.addItem(label1).width(100).aspectRatio(of: someImageView)
    $0.addItem(label2)
    $0.addItem(label3)
}

 

* 전체 코드: https://github.com/JK0369/ExFlexLayout

 

* 참고

https://github.com/layoutBox/PinLayout

https://github.com/layoutBox/FlexLayout

https://docs.google.com/spreadsheets/d/1sUNdGWBM-d_W13yC7VcfkRXC3owCVsnIublnfW-4xn4/edit#gid=1032991425

https://github.com/layoutBox/LayoutFrameworkBenchmark

Comments