위의 3개의 UILabel 객체를 그림과 같이 레이아웃을 잡아준다고 가정해보자.
스택뷰를 이용하지 않는다면, 그럼 각각의 레이블에 레이아웃을 걸어줘야한다. 게다가 동적으로 레이아웃을 변화해야한다면 이는 만만치 않은 작업이 된다.
만약, 스택뷰를 이용한다면 스택 뷰의 프로퍼티로 3개의 레이블에 레이아웃을 빠르고 쉽게 걸어줄 수 있다.
즉 스택뷰는 여러 개의 뷰를 정렬하는데에 효과적인 객체이다.
스택 뷰 내부의 뷰들은 arrangedSubviews 프로퍼티에 저장되며, 스택뷰들은 이 뷰들의 부모 뷰가 된다.
스택 뷰의 내부 뷰들은 스택 뷰 자체의 프로퍼티로 레이아웃을 설정할 수 있지만, 스택 뷰 자체의 위치나 크기등은 직접 설정해야한다.
UIStackView의 axis는 스택뷰 내부의 뷰들을 정렬할 축의 방향이다.
스택 뷰의 axis 방향으로 스택 뷰 내부의 서브 뷰들의 위치나 크기를 결정한다.
fillEqually: 서브 뷰들은 모두 같은 크기로 resize된다. 이 때, 가장 큰 뷰를 기준으로 resize된다.
equalSpacing: 서브 뷰들은 동일한 spacing을 같도록 크기가 결정된다.
euqalCentering: 서브 뷰들의 center를 기준으로 spacing이 맞춰진다.
마찬가지로, 이 때 줄어들거나 늘어나는 서브 뷰들의 크기는 priority에 의해 결정된다.
스택 뷰의 axis의 수직 방향으로 서브 뷰들의 위치나 크기를 결정한다.
spacing은 말그대로 스택 뷰 내부의 서브뷰들 사이의 간격을 의미한다.
사용자가 테이블 뷰 셀을 터치 시 동적으로 높이가 변하는 테이블 뷰 셀의 구현원리는 다음과 같다.
//
// Cell.swift
// StackView_Practice
//
// Created by LEE on 2023/04/12.
//
import UIKit
class Cell: UITableViewCell{
static let identifier = "Cell"
let stackView: UIStackView = {
let view = UIStackView()
view.axis = .vertical
view.spacing = 0
view.alignment = .leading
view.distribution = .fill
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let titleLabel: UILabel = {
let label = UILabel()
label.textColor = .black
label.textAlignment = .left
label.font = .systemFont(ofSize: 16)
label.numberOfLines = 1
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let desLabel: UILabel = {
let label = UILabel()
label.textColor = .black
label.textAlignment = .left
label.font = .systemFont(ofSize: 16)
label.numberOfLines = 1
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
contentView.addSubview(stackView)
stackView.addArrangedSubview(titleLabel)
stackView.addArrangedSubview(desLabel)
NSLayoutConstraint.activate([
stackView.leftAnchor.constraint(equalTo: contentView.leftAnchor),
stackView.rightAnchor.constraint(equalTo: contentView.rightAnchor),
stackView.topAnchor.constraint(equalTo: contentView.topAnchor),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)])
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
//
// ViewController.swift
// StackView_Practice
//
// Created by LEE on 2023/04/12.
//
import UIKit
class ViewController: UIViewController {
typealias Item = (title: String, des: String, isDesHidden: Bool)
var items: [Item] = (0...100)
.map{
Item(title: String($0), des: "description by \($0)", isDesHidden: true)
}
let tableView: UITableView = {
let view = UITableView()
view.backgroundColor = .white
view.separatorStyle = .none
view.translatesAutoresizingMaskIntoConstraints = false
view.rowHeight = 50
view.bounces = true
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
tableView.register(Cell.self, forCellReuseIdentifier: Cell.identifier)
tableView.delegate = self
tableView.dataSource = self
NSLayoutConstraint.activate([
tableView.leftAnchor.constraint(equalTo: view.leftAnchor),
tableView.rightAnchor.constraint(equalTo: view.rightAnchor),
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Cell.identifier) as! Cell
cell.titleLabel.text = items[indexPath.row].title
cell.desLabel.text = items[indexPath.row].des
cell.desLabel.isHidden = items[indexPath.row].isDesHidden
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
items[indexPath.row].isDesHidden.toggle()
// tableView.reconfigureRows(at: [IndexPath(row: indexPath.row, section: 0)])
tableView.reloadRows(at: [IndexPath(row: indexPath.row, section: 0)], with: .automatic)
}
}
https://ios-development.tistory.com/1222
https://babbab2.tistory.com/154
https://developer.apple.com/documentation/uikit/uistackview