오늘은 iOS 개발에서 자주 마주치는 Content Hugging Priority에 대해 이야기해보려고 합니다.
이 개념은 뷰의 크기 조정에 있어 꽤 중요한 역할을 하지만, 처음 접할 때는 조금 헷갈릴 수 있어요. 그래서 Content Hugging Priority가 뭔지, 그리고 언제 어떻게 사용해야 하는지 쉽게 설명해드리겠습니다.
Content Hugging Priority란?
Content Hugging Priority라는 건 말 그대로 뷰가 자신의 콘텐츠 크기를 "껴안고(hugging)" 유지하려는 우선순위(priority)를 의미해요. 뷰의 콘텐츠가 많거나 적더라도, 이 우선순위에 따라 그 뷰가 얼마나 자신의 크기를 고수할지를 결정하게 됩니다.
예를 들어, UILabel을 생각해볼게요. 이 라벨에 긴 텍스트가 들어가면 라벨의 너비가 자연스럽게 늘어나겠죠? 그런데 이 라벨이 다른 뷰와 함께 Auto Layout을 통해 배치되어 있다면, 그 라벨이 얼마나 크기를 유지하려고 할지가 중요해집니다. 이때 Content Hugging Priority를 설정하게 되는 거죠.
축(axis)란?
iOS에서 Content Hugging Priority를 설정할 때는 축(axis)이라는 개념을 이해해야 해요. 축은 두 가지가 있어요:
- 가로 방향 (horizontal) 축: 뷰가 가로로 자신의 크기를 유지하려고 하는 우선순위입니다.
- 세로 방향 (vertical) 축: 뷰가 세로로 자신의 크기를 유지하려고 하는 우선순위입니다.
언제 사용해야 할까?
이 우선순위는 기본적으로 모든 뷰에 설정되어 있지만, 가끔은 이를 커스터마이즈해야 할 때가 있어요. 예를 들어, 두 개의 라벨이 나란히 있을 때, 한쪽 라벨의 텍스트 길이가 변할 때 다른 쪽 라벨이 얼마나 영향을 받을지 결정하고 싶을 때입니다.
let label = UILabel()
label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
이 코드에서 라벨의 Content Hugging Priority를 가로 방향으로 높게 설정했어요. 이 말은, 이 라벨이 가로로는 크기를 줄이거나 늘리기 싫어한다는 뜻이죠. 즉, 다른 뷰가 공간을 더 차지하려고 할 때, 이 라벨은 자신의 공간을 지키려고 노력할 거예요.
여기서 제가 이 부분에 대해서 어디에 적용하면 좋을지에 대해서 예시를 하나 들고 왔어요
이렇게 축에 맞지않게 정렬되어있는걸
이모지와 아래 텍스트를 맞추어 주는 역할이라고
생각하시면 됩니다
import UIKit
import SnapKit
class StackViewController: UIViewController {
private let buttonStackView = UIStackView()
private let scheduleButton = UIButton()
private let projectButton = UIButton()
private let iepButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
setupButtonStackView()
setupConstraints()
// Do any additional setup after loading the view.
}
private func setupButtonStackView() {
view.addSubview(buttonStackView) // 먼저 view에 추가합니다.
buttonStackView.axis = .horizontal
buttonStackView.distribution = .fillEqually
buttonStackView.spacing = 10
let buttonData = [
(":날짜:", "일정"),
(":연필2:", "프로젝트"),
(":직소:", "개별화\n교육계획")
]
[scheduleButton, projectButton, iepButton].enumerated().forEach { index, button in
let (emoji, title) = buttonData[index]
// button.backgroundColor = ColorScheme.cardBackground
button.layer.cornerRadius = 12
button.layer.shadowColor = UIColor.black.cgColor
button.layer.shadowOpacity = 0.1
button.layer.shadowOffset = CGSize(width: 0, height: 2)
button.layer.shadowRadius = 4
// button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
let stackView = UIStackView()
stackView.axis = .vertical
stackView.alignment = .center
stackView.spacing = 4
let emojiLabel = UILabel()
emojiLabel.text = emoji
emojiLabel.font = UIFont.systemFont(ofSize: 50)
emojiLabel.textAlignment = .center
let titleLabel = UILabel()
titleLabel.text = title
titleLabel.font = UIFont.systemFont(ofSize: 20, weight: .medium)
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 0
stackView.addArrangedSubview(emojiLabel)
stackView.addArrangedSubview(titleLabel)
button.addSubview(stackView)
stackView.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(4)
}
buttonStackView.addArrangedSubview(button)
}
}
private func setupConstraints() {
buttonStackView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide).offset(16)
make.leading.trailing.equalToSuperview().inset(16)
make.bottom.lessThanOrEqualTo(view.safeAreaLayoutGuide).offset(-16)
}
}
}
이렇게 코드를 넣는다면 위치가 맞지 않기 때문에 위치를 맞추기 위해서
스택뷰 정렬과
stackView.alignment = .center
emojiLabel.textAlignment = .center
titleLabel.textAlignment = .center
버튼 여백을 동일하게 주고 이모지의 높이와 길이를 동일하게 주도록 합니다
button.contentEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
수정을 햊ㅆ지만 버튼 사이즈가 달라서 전혀 예뻐보이지 않는 버튼들이 완성되었습니다
이런 이유는 컨텐츠 크기나 내용에 따라서 버튼 크기가 달라지기 때문입니다
그래서 각 버튼의 크기를 컨텐츠에 맞춰서 크기를 조절합니다
[buttonStackView.arrangedSubviews].forEach { view in
view.snp.makeConstraints { make in
make.width.equalTo(buttonStackView.arrangedSubviews[0].snp.width)
make.height.equalTo(buttonStackView.arrangedSubviews[0].snp.height)
}
}
스택뷰 사이즈를 0번쨰 너비와 높이에 다 맞추고
buttonStackView.axis = .horizontal
buttonStackView.distribution = .fillEqually
buttonStackView.spacing = 10
fillEqually로 맞추게되면 동일한 버튼 크기가 완성됩니다
맞추기 위해 이러한 일련의 과정을 통해서 해결할 수 있습니다
import UIKit
import SnapKit
class StackViewController: UIViewController {
private let buttonStackView = UIStackView()
private let scheduleButton = UIButton()
private let projectButton = UIButton()
private let iepButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
setupButtonStackView()
setupConstraints()
}
private func setupButtonStackView() {
view.addSubview(buttonStackView)
buttonStackView.axis = .horizontal
buttonStackView.distribution = .fillEqually
buttonStackView.spacing = 10
buttonStackView.alignment = .center
let buttonData = [
(":날짜:", "일정"),
(":연필2:", "프로젝트"),
(":직소:", "개별화교육")
]
[scheduleButton, projectButton, iepButton].enumerated().forEach { index, button in
let (emoji, title) = buttonData[index]
button.backgroundColor = .white
button.layer.cornerRadius = 12
button.layer.shadowColor = UIColor.black.cgColor
button.layer.shadowOpacity = 0.1
button.layer.shadowOffset = CGSize(width: 0, height: 2)
button.layer.shadowRadius = 4
button.contentEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
// button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
let stackView = UIStackView()
stackView.axis = .vertical
stackView.alignment = .fill
stackView.spacing = 4
let emojiLabel = UILabel()
emojiLabel.text = emoji
emojiLabel.font = UIFont.systemFont(ofSize: 50)
emojiLabel.textAlignment = .center
emojiLabel.setContentHuggingPriority(.defaultHigh, for: .vertical)
let titleLabel = UILabel()
titleLabel.text = title
titleLabel.font = UIFont.systemFont(ofSize: 20, weight: .medium)
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 0
titleLabel.setContentHuggingPriority(.defaultLow, for: .vertical)
stackView.addArrangedSubview(emojiLabel)
stackView.addArrangedSubview(titleLabel)
button.addSubview(stackView)
stackView.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(4)
}
buttonStackView.addArrangedSubview(button)
}
}
private func setupConstraints() {
buttonStackView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide).offset(16)
make.leading.trailing.equalToSuperview().inset(16)
make.bottom.lessThanOrEqualTo(view.safeAreaLayoutGuide).offset(-16)
}
buttonStackView.arrangedSubviews.forEach { view in
view.snp.makeConstraints { make in
make.width.equalTo(buttonStackView.arrangedSubviews[0].snp.width)
make.height.equalTo(buttonStackView.arrangedSubviews[0].snp.height)
}
}
}
}
우선 이 문제를 해결하기 위해서는 스택뷰가 어떤식으로 동작하는지 이해를 해야하고
그리고 그 안에 들어가는 오브젝트들을 어떻게 배치할건지에 대해서
심도있게 고민을 해야 풀 수 있었던 문제였던것 같습니다
UIStackView.Distribution.equalCentering
정리
Content Hugging Priority는 뷰의 크기 조정에서 중요한 역할을 합니다. 이 개념을 잘 활용하면 Auto Layout을 사용할 때 더 원하는 대로 레이아웃을 조정할 수 있어요. 가로와 세로 축을 이해하고 우선순위를 설정함으로써, 뷰가 크기를 어떻게 유지할지 세밀하게 제어할 수 있습니다.
https://spin.atomicobject.com/uistackview-distribution/
Exploring UIStackView Distribution Types
Exploring the five UIStackView distribution types: Fill, Fill Equally, Fill Proportionally, Equal Spacing, and Equal Centering.
spin.atomicobject.com
https://developer.apple.com/documentation/uikit/uistackview/distribution
UIStackView.Distribution | Apple Developer Documentation
The layout that defines the size and position of the arranged views along the stack view’s axis.
developer.apple.com
https://developer.apple.com/documentation/uikit/uiview/1622556-contenthuggingpriority
contentHuggingPriority(for:) | Apple Developer Documentation
Returns the priority with which a view resists being made larger than its intrinsic size.
developer.apple.com
'UIKit' 카테고리의 다른 글
iOS/Swift/UIKit | Storyboard 및 UIView - UILabel 글자 크기 자동 변경 Autoshrink (0) | 2024.12.06 |
---|---|
UIKit | SwiftUI Preview 적용하기 (iOS 15.ver) (0) | 2024.08.13 |
iOS | GCD에 관하여 - 2 (4) | 2024.07.16 |
iOS | GCD에 관하여 - 1 (0) | 2024.07.15 |
UIKit | ViewController 생명 주기 (0) | 2024.07.12 |