StackView

윤주현·2023년 7월 19일
0

Auto Layout

목록 보기
8/8

StackView

StacView를 사용하면 contraint를 줄일 수 있다.

Alignment


Distribution

Fill

  • 가능한 공간 다 채우기
  • Default 값
  • 각 구성요소들이 가지고 있는 높이와 너비 유지
  • intrinsic content size, CHCR 사용

Fill Equally

  • 가능한 공간 다 채우기
  • 각 구성요소들 사이즈 모두 동일하게
  • intrinsic content size 사용 안함
    (intrinsic content size는 옵셔널이므로 사용안해도 됨)

Fill Proportionally

  • 가능한 공간 다 채우기
  • 구성 요소들의 intrinsic content size의 비율 유지하면서 fill

Equal Spacing

  • 구성요소 사이의 공간의 크기가 동일하게 정렬

Equal Centering

  • 구성요소의 중심사이의 크기가 동일하게 정렬

Scrollable StackView

import UIKit

class ScrollableView: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
    }
    
    func setupViews() {
        navigationItem.title = "Scrollable"
        
        let stackView = makeStackView(withOrientation: .vertical)
        let scrollView = makeScrollView()
        
        scrollView.addSubview(stackView)
        view.addSubview(scrollView)
        
        for _ in 1...40 {
            let row = RowView()
            stackView.addArrangedSubview(row)
        }
        
        // Pinning to the sides of view
        stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
        stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
        stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
        
        // Pinning scrollview
        scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
    }
}

for _ in 1...40 {
    let row = RowView()
    stackView.addArrangedSubview(row)
    row.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
}
  1. ScrollView를 외부 뷰(뷰컨)에 꽉차게 한다.
  2. StackView를 ScrollView에 꽉차게 한다.
  3. StackView안의 요소들이 생각한대로 배치되지 않았으면 ScrollView나 외부의 뷰를 이용해서 추가 constraints를 준다.(위 코드의 경우엔 rowView의 너비를 ScrollView의 너비와 동일하게 고정해서 해결했다.)

Padding

// Note: We need to adjust our width to take into account the extra padding on the side (16x2 = 32)
row.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: 1, constant: -32).isActive = true

stackView.isLayoutMarginsRelativeArrangement = true
stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 40, leading: 16, bottom: 40, trailing: 16)

Spacer View

for i in 1...40 {
    let row = RowView()
    stackView.addArrangedSubview(row)

    // Add a spacer every 5th row
    if i % 5 == 0 {
        stackView.addArrangedSubview(makeSpacerView(height: 200))
    }

    // Note: We need to adjust our width to take into account the extra padding on the side (16x2 = 32)
    row.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: 1, constant: -32).isActive = true
}
        
// Factories.Swift
public func makeSpacerView(height: CGFloat? = nil) -> UIView {
    let spacerView = UIView(frame: .zero)

    if let height = height {
        spacerView.heightAnchor.constraint(equalToConstant: height).setActiveBreakable()
    }
    spacerView.translatesAutoresizingMaskIntoConstraints = false

    return spacerView
}

public extension NSLayoutConstraint {
    @objc func setActiveBreakable(priority: UILayoutPriority = UILayoutPriority(900)) {
        self.priority = priority
        isActive = true
    }
}

setActiveBreakable 메소드로 constraint의 우선도를 900으로 낮춤으로써 StackView안의 공간이 부족할 경우 spacerView가 줄어든다.

Tip

  • 스택뷰 안에 들어가는 컨트롤들은 스택뷰가 자신의 높이를 확실히 알 수 있게 intrinsic content size를 가지고 있는게 좋다.(예를 들어 custom UIView의 경우 intrinsic content size를 지정해주는 것이 좋다.)

1개의 댓글

comment-user-thumbnail
2023년 7월 19일

소중한 정보 감사드립니다!

답글 달기