Dynamic Height Cell ๋ง๋ค๊ธฐ - CollectionView + CompositionalLayout
์ต๊ทผ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ Dynamic ํ๊ฒ Cell์ ๋์ด๋ฅผ ๋ฐ๊พธ๊ณ ์ถ์ ๊ฒฝ์ฐ๊ฐ ์๊ฒผ๋๋ฐ,
ํด๊ฒฐํ๊ธฐ๊น์ง ์ฝ์ง์ ๋ง์ด ํ ๊ฒ์ ๋นํด ํด๊ฒฐ๋ฒ์ด ๊ฐ๋จํด์ ๊ธฐ๋ก์ผ๋ก ๋จ๊ฒจ๋ณด๋ ค ํฉ๋๋ค.
CollectionView ๋ง๋ค๊ธฐ
์ผ๋จ ์ด๋ฒ๊ธ์์๋ Compositional Layout์ ์ฌ์ฉํด์ CollectionView๋ฅผ ๋ง๋ค๊ฒ ์ต๋๋ค.
Layout ๋ง๋ค๊ธฐ
func createLayout() -> UICollectionViewCompositionalLayout {
let item = NSCollectionLayoutItem(
layoutSize: .init(
widthDimension: .fractionalWidth(1),
heightDimension: .estimated(1)
)
)
let group = NSCollectionLayoutGroup.vertical(
layoutSize: .init(
widthDimension: .fractionalWidth(1),
heightDimension: .estimated(1)
), subitems: [item]
)
let section = NSCollectionLayoutSection(group: group)
section.interGroupSpacing = 5
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
์ ํ ๋ฌธ์๋ฅผ ๋ณด๋ฉด estimated๋
Use an estimated value if the size of your content might change at runtime, such as when data is loaded or in response to a change in system font size. You provide an initial estimated size and the system computes the actual value later.
์์ฝํด์ ๋ฒ์ญํ๋ฉด, estimated๋ฅผ ์ฌ์ฉํด๋ผ ๋ง์ฝ ๋์ content๊ฐ ๋ฐํ์ ์ค์ ๋ฐ๋๋ค๋ฉด ๋ฐ์ดํฐ๊ฐ ๋ก๋๋๊ฑฐ๋ ํฐํธ ์ฌ์ด์ฆ๊ฐ ๋ฐ๋์ด์ ๋์ด๋ฅผ ๋ฐ๊ฟ์ค์ผ ํ๋ ๊ฒฝ์ฐ์ฒ๋ผ.
๋ผ๊ณ ํฉ๋๋ค.
๊ทธ๋์ ๋์ด๋ฅผ Dynamicํ๊ฒ ๋ฐ๊ฟ์ฃผ๊ธฐ ์ํด group๊ณผ item์ ๋์ด๋ฅผ estimated๋ก ์ค์ ํด ์ฃผ์์ต๋๋ค.
Cell ๋ง๋ค๊ธฐ
๊ทธ๋ฆฌ๊ณ ์ด๋ฐ ์ ์ ๋ง๋ค์ด์ฃผ๊ฒ ์ต๋๋ค.
import UIKit
import SnapKit
final class DynamicCell: UICollectionViewCell {
static let id = String(describing: DynamicCell.self)
private lazy var myLabel: UILabel = {
let label = UILabel()
contentView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.font = UIFont.systemFont(ofSize: 18, weight: .bold)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.backgroundColor = .secondarySystemBackground
layout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(with text: String) {
myLabel.text = text
}
}
extension DynamicCell {
func layout() {
myLabel.snp.makeConstraints {
$0.top.equalTo(contentView.snp.top).offset(8)
$0.leading.equalTo(contentView.snp.leading).offset(12)
$0.bottom.equalTo(contentView.snp.bottom).offset(-8)
$0.trailing.equalTo(contentView.snp.trailing)
}
}
}
UILabel์ด ์ฝ๊ฐ์ Offset์ ๊ฐ์ง๊ณ ์๋ ๋จ์ํ ์ ์ ๋๋ค.
์์ ๋ ์ด์์๊ณผ ์ ์ ์ด์ฉํด์ ๊ฐ๋จํ ๋ง๋ค๋ฉด ์๋์ ๊ฐ์ด ์ ๋์ค๊ฒ ๋ฉ๋๋ค.
๋ฌธ์ ๊ฐ ์์ด ๋ณด์ด๋ค์, ๊ธ์ ํฌ๊ธฐ์ ๋ง์ถฐ ์ ๋์ต๋๋ค.
์ฌ๊ธฐ์ ์ ๋ค์ ์ข ๋ ๋ง์ด ๋ง๋ค์ด ๋ด ์๋ค.
์ค ์๋์ค๋ ๊ฑฐ ๊ฐ๋ค์,
์คํฌ๋กค์ ํ ๋ฒ ํด๋ณผ๊น์?
์ ์๋ฌ๊ฐ ๋์ค๋ค์.
์ ๋ฆฌํด ๋ณด์๋ฉด, ์ง๊ธ View์ top ์ด๋ bottom์ ์ ์์ 8์ฉ inset์ ์คฌ๋๋ฐ, ์ ์ ๋์ด๊ฐ 1์ด๋ผ์ ์ด๊ฒ๋ค์ ๋์์ ๋ง์กฑํ ์ ์๋ค.
๋ผ๋ ๊ฒ ๊ฐ๋ค์.
์ง๊ธ ์๋ ๋ณด์ด์ง ์๋ ์ ๋ค์ ๊ธฐ๋ณธ์ ์ผ๋ก estimated(1)์์ ์ฃผ์ด์ง๋ฐ๋ก 1๋ก ์ผ๋จ ์ค์ ์ด ๋์ด์์ด์ ๋๋ ์ค๋ฅ ๊ฐ์ต๋๋ค.
๊ทธ๋์ estimated๋ฅผ ๋ ํฌ๊ฒ ์ฃผ๋ฉด ์ด ์๋ฌ๊ฐ ์ฌ๋ผ์ง๊ธด ํฉ๋๋ค.
ํ์ง๋ง ์ ์ ์ต๋ ํฌ๊ธฐ๋ฅผ ์์ง ๋ชปํ๋ค๋ฉด ์ด๋จ๊น์ ์ข์ ํด๊ฒฐ๋ฐฉ๋ฒ์ด ์๋ ๊ฑฐ ๊ฐ์ต๋๋ค.
์ ๋ ์ฌ๊ธฐ์ ์ด ์๋ฌ๋ฅผ ์์ ๊ธฐ ์ํด ์ฐธ ๋ง์ ์ฝ์ง์ ํ์ต๋๋ค.
ํด๊ฒฐ๋ฒ๋ถํฐ ๋งํ๋ฉด, ์ ์ ๋ง๋ค ๋ Constraint์ priority๋ฅผ ์ ์ค์ ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
func layout() {
myLabel.snp.makeConstraints {
$0.top.equalTo(contentView.snp.top).offset(8)
$0.leading.equalTo(contentView.snp.leading).offset(12)
$0.bottom.equalTo(contentView.snp.bottom).offset(-8)
$0.trailing.equalTo(contentView.snp.trailing)
}
}
์ฌ๊ธฐ์
์๋์ ๊ฐ์ด ๋ฐ๊ฟ์ค๋๋ค.
func layout() {
myLabel.snp.makeConstraints {
$0.top.equalTo(contentView.snp.top).offset(8).priority(.high)
$0.leading.equalTo(contentView.snp.leading).offset(12)
$0.bottom.equalTo(contentView.snp.bottom).offset(-8)
$0.trailing.equalTo(contentView.snp.trailing).priority(.high)
}
}
top์ด๋ bottom์ pritority๋ฅผ ๋์ฌ์คฌ์ต๋๋ค.
๋ค์ ์คํํด ๋ณผ๊น์.
์๋ฌ๋ ์ ๋จ๊ณ ์ ๋์ด๋ ์๋ง๊ฒ ์ ๋์ค๋ค์.
์ ๋ ์ฒ์์ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด cell์ height constraint๋ฅผ update ํด์ฃผ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋๋ฐ์,
"์ ์ ์ ํฌ๊ธฐ๊ฐ ๋๋ฌด ์์? ๊ทธ๋ฌ๋ฉด ๋ด๊ฐ ์๋ง๊ฒ ๋ฐ๊ฟ์ค๊ฒ"๋ผ๋ ์๊ฐ์ด์์ฃ ..
๊ทธ๋ฌ๋ฉด
Changing the translatesAutoresizingMaskIntoConstraints property of a UICollectionViewCell that is managed by a UICollectionView is not supported, and will result in incorrect self-sizing
์ด๋ ๊ฒ ๋ค๊ฐ ๋ฐ๊ฟ ์ ์๊ณ ์ด์ํ๊ฒ ๋์ฌ ๊ฑฐ์ผ, ๋ผ๋ฉด์ ์๋ฌ๊ฐ ๋๊ฒ ๋ฉ๋๋ค.
๋๊ฐ ๋ชปํ๋๊น ๋ด๊ฐ ํด์ฃผ๊ฒ ๋ค๋๋ฐ ์ ๋ง์ ์ด๋ ๊ฒ๋ ์๊ฐํ์์ฃ . ๊ฒฐ๊ตญ ์ ๊ฐ ์๋ชปํ ๊ฑฐ์์ง๋ง.
๊ทธ๋์ ์ผ๋จ์ tableView๋ก ๊ต์ฒดํ๊ณ automaticDimension์ ์ฌ์ฉํ์ต๋๋ค.
ํน๋ณํ ๋ ์ด์์์ด ํ์ํ์ง ์๋ค๋ฉด ์ด๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ธ๊ฒ ๊ฐ์ต๋๋ค.