관리 메뉴

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

[iOS - swift] UIStackView 레이아웃 팁 (greaterThanOrEqualTo, lessThanOrEqualTo) 본문

iOS 응용 (swift)

[iOS - swift] UIStackView 레이아웃 팁 (greaterThanOrEqualTo, lessThanOrEqualTo)

jake-kim 2023. 1. 22. 22:48

UIStackView 기초

  • UIStackView를 사용할 때 가장 장점은 뷰를 나열할 때 복잡한 레이아웃 없이 내장된 속성을 이용하여 일관된 뷰 표현이 가능
    • axis: 방향
    • spacing: 내부 뷰 사이의 공간
    • alignment: axis와 반대 방향에 관한 속성 (fill - 늘리기, top - 위에 붙이기, leading - 시작 지점에 붙이기 등등)
    • distribution: axis 방향에 관한 사이즈 제어 속성 (fill, fillEqually 등등)

UIStackView 레이아웃 테크닉

  • UIStackView를 사용할 때 주의할 점은 UIStackView레이아웃 설정
    • 만약 axis 방향이 vertical인 경우, leading과 trailing을 superview로 constraint하는 것을 지양
      • > centerX와 greaterThanOrEqualTo, lessThanOrEqualTo를 사용해서 표현할 것
    • 만약 axis 방향이 horizontal인 경우, top과 bottom을 superview로 constraint하는 것을 지양
      • > centerY와 greaterThanOrEqualTo, lessThanOrEqualTo를 사용해서 표현할 것

ex) axis 방향이 vertical이고, 뷰를 나열하려고 할 때 뷰들의 width 크기가 각각 다르도록 하고 싶은 경우?

leading, trailing을 superview에 고정하게 된다면, 스택뷰안에 들어가는 뷰들의 width값을 변경해도 반영이 안되는 단점

class ViewController: UIViewController {
    private let stackView: UIStackView = {
        let view = UIStackView()
        view.axis = .vertical
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    private let myView1: UIView = {
        let view = UIView()
        view.backgroundColor = .green
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    private let myView2: UIView = {
        let view = UIView()
        view.backgroundColor = .blue
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(stackView)
        stackView.addArrangedSubview(myView1)
        stackView.addArrangedSubview(myView2)
        
        NSLayoutConstraint.activate([
            stackView.leftAnchor.constraint(equalTo: view.leftAnchor),
            stackView.rightAnchor.constraint(equalTo: view.rightAnchor),
            stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
        ])
        
        NSLayoutConstraint.activate([
            myView1.heightAnchor.constraint(equalToConstant: 120),
            myView2.heightAnchor.constraint(equalToConstant: 80),
        ])
        
        NSLayoutConstraint.activate([
            myView1.widthAnchor.constraint(equalToConstant: 50),
            myView2.widthAnchor.constraint(equalToConstant: 50),
        ])
    }
}

초록색, 파란색 뷰에 width를 지정해도 무시된 상태

만약 내부 뷰들의 width를 지정하려고 한다면 wrapper를 넣어줘야 하는 번거로움이 존재

view.addSubview(stackView)
let wrapper1 = UIView()
wrapper1.addSubview(myView1)
stackView.addArrangedSubview(wrapper1)
let wrapper2 = UIView()
wrapper2.addSubview(myView2)
stackView.addArrangedSubview(wrapper2)

NSLayoutConstraint.activate([
    myView1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    myView2.centerXAnchor.constraint(equalTo: view.centerXAnchor),
])

wrapper를 이용하여 표현

  • 전체 코드
import UIKit

class ViewController: UIViewController {
    private let stackView: UIStackView = {
        let view = UIStackView()
        view.axis = .vertical
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    private let myView1: UIView = {
        let view = UIView()
        view.backgroundColor = .green
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    private let myView2: UIView = {
        let view = UIView()
        view.backgroundColor = .blue
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(stackView)
        let wrapper1 = UIView()
        wrapper1.addSubview(myView1)
        stackView.addArrangedSubview(wrapper1)
        let wrapper2 = UIView()
        wrapper2.addSubview(myView2)
        stackView.addArrangedSubview(wrapper2)
        
        NSLayoutConstraint.activate([
            stackView.leftAnchor.constraint(equalTo: view.leftAnchor),
            stackView.rightAnchor.constraint(equalTo: view.rightAnchor),
            stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
        ])
        
        NSLayoutConstraint.activate([
            myView1.heightAnchor.constraint(equalToConstant: 120),
            myView2.heightAnchor.constraint(equalToConstant: 80),
        ])
        
        NSLayoutConstraint.activate([
            myView1.widthAnchor.constraint(equalToConstant: 50),
            myView2.widthAnchor.constraint(equalToConstant: 50),
        ])
        
        // wrapper로 인해 추가된 코드
        NSLayoutConstraint.activate([
            myView1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            myView2.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        ])
    }
}

스택뷰를 greaterThan lessThan 으로 레이아웃 표현

  • stackView를 애초에 leading, trailing (또는 left, right)로 고정하지 않고 gretherThan, lessThan, center를 사용하여 표현하면 안에 들어가는 뷰들을 제어하는데 편리
NSLayoutConstraint.activate([
    stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
    stackView.leftAnchor.constraint(greaterThanOrEqualTo: view.leftAnchor),
    stackView.rightAnchor.constraint(lessThanOrEqualTo: view.rightAnchor),
    stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
])
  • wrapper없이 단순하게 표현 가능

(greaterThan, lessThan을 사용한 경우 레이아웃 부분 코드)

view.addSubview(stackView)
stackView.addArrangedSubview(myView1)
stackView.addArrangedSubview(myView2)

NSLayoutConstraint.activate([
    stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
    stackView.leftAnchor.constraint(greaterThanOrEqualTo: view.leftAnchor),
    stackView.rightAnchor.constraint(lessThanOrEqualTo: view.rightAnchor),
    stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
])

NSLayoutConstraint.activate([
    myView1.heightAnchor.constraint(equalToConstant: 120),
    myView2.heightAnchor.constraint(equalToConstant: 80),
])

NSLayoutConstraint.activate([
    myView1.widthAnchor.constraint(equalToConstant: 50),
    myView2.widthAnchor.constraint(equalToConstant: 50),
])

wrapper없이 greaterThan, lessThan만으로 표현

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

Comments