Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- HIG
- collectionview
- UITextView
- 리펙토링
- clean architecture
- Xcode
- Refactoring
- Protocol
- tableView
- Clean Code
- Human interface guide
- SWIFT
- RxCocoa
- 리팩토링
- rxswift
- MVVM
- swift documentation
- combine
- swiftUI
- map
- 애니메이션
- uiscrollview
- 클린 코드
- 스위프트
- UICollectionView
- ribs
- ios
- uitableview
- 리펙터링
- Observable
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - Swift] SelfSizingTableView, SelfSizingCollectionView 구현 방법 (Dynamic Size) 본문
iOS 응용 (swift)
[iOS - Swift] SelfSizingTableView, SelfSizingCollectionView 구현 방법 (Dynamic Size)
jake-kim 2022. 11. 26. 22:50
SelfSizing 아이디어
- SelfSizing이란?
- Cell의 크기에 따라 일종의 Container인 TableView와 CollectionView의 크기도 동적으로 커지게 만드는 것
- 구현 아이디어
- layoutSubviews()에서 invalidateIntrinsicContentSize() 호출
- intrinsicContentSize를 재정의하여 콘텐츠의 크기만큼 해당 뷰가 늘어나게끔 구현
- 사용하는쪽에서는 UIStackView로 위에서 정의한 tableView, collectionView를 넣어서 사용
- UIStackView는 내부 뷰의 intrinsicContentSize에 따라 달라지므로, SelfSizing 구현에 적합
- 여기까지 하면 자동으로 height가 변경되는 tableView, scrollView를 구현할 수 있고, UIScrollView안에 넣으면 스크롤까지 가능
LayoutSubviews()와 invalidateIntrinsicContentSize() 개념
- layoutSubviews()
- 뷰가 그려지는 Constraints -> Layout -> Draw 순서 중 Layout 중 하나이며, 해당 뷰의 크기나 레이아웃이 변경되면 하위 뷰(subview)들도 변경되어야 하므로 하위 뷰들에게 업뎃을 알림
- invalidateIntrinsicContentSize()
- intrinsicContentSize를 업데이트하는 메소드
SelfSizing 구현
- SelfSizingTableView, SelfSizingCollectionView 구현
class SelfSizingTableView: UITableView {
override var intrinsicContentSize: CGSize {
contentSize
}
override func layoutSubviews() {
invalidateIntrinsicContentSize()
super.layoutSubviews()
}
}
class SelfSizingCollectionView: UICollectionView {
override var intrinsicContentSize: CGSize {
contentSize
}
override func layoutSubviews() {
invalidateIntrinsicContentSize()
super.layoutSubviews()
}
}
- (원리는 같으니 예제에서는 tableView를 중심으로 작성)
- tableView는 동적으로 intrinsicContentSize가 증가하므로, UIStackView안에 넣어서 intrinsicContentSize만큼 자동으로 height값이 증가하도록 구현
class ViewController: UIViewController {
private var items = (0...2).map { String($0) }
private let stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
private let tableView: SelfSizingTableView = {
let view = SelfSizingTableView()
view.allowsSelection = false
view.backgroundColor = .clear
view.separatorStyle = .none
view.bounces = true
view.showsVerticalScrollIndicator = true
view.contentInset = .zero
view.register(UITableViewCell.self, forCellReuseIdentifier: "UITableViewCell")
view.estimatedRowHeight = 120
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
}
- 데이터를 추가하며 테이블뷰의 height가 증가하는 것을 확인하기 위해 button과 label 준비
- button: 누를 경우 아이템 증가
- label: 테이블 뷰 밑에 있어서, 테이블 뷰 높이가 증가하는것을 확인하는 용도
private let button: UIButton = {
let button = UIButton(type: .system)
button.setTitle("데이터 추가", for: .normal)
button.addTarget(self, action: #selector(tap), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
private let label: UILabel = {
let label = UILabel()
label.text = "테이블뷰 밑에 있는 Label"
label.font = .systemFont(ofSize: 24)
label.numberOfLines = 1
label.textColor = .black
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
@objc private func tap() {
let array = (Int(items.last!)!+1...Int(items.last!)!+2).map { String($0) }
items.append(contentsOf: array)
tableView.reloadData()
}
- 레이아웃
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(stackView)
view.addSubview(button)
view.addSubview(label)
stackView.addArrangedSubview(tableView)
tableView.dataSource = self
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 16),
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -120)
])
}
- extension
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell", for: indexPath)
cell.textLabel?.text = items[indexPath.row]
cell.textLabel?.textColor = .white
cell.backgroundColor = .random()
return cell
}
}
extension CGFloat {
static func random() -> CGFloat {
CGFloat(arc4random()) / CGFloat(UInt32.max)
}
}
extension UIColor {
static func random() -> UIColor {
UIColor(
red: .random(),
green: .random(),
blue: .random(),
alpha: 1.0
)
}
}
- 구현
- 길이가 동적으로 늘어나고 있고 있지만, 화면의 height보다 더 크게 증가한 경우 스크롤이 안되는 이슈가 존재
- UISCrollView를 사용하여 해결
- UIScrollView를 이용하여 스크롤되게끔 구현
- UIScrollView 안에 UIStackView를 넣으면, UIStackView의 내부 intrinsicContentSize에 따라서 자동으로 커지면 스크롤 되게끔 구현
- scrollView 추가
private let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
- tableView 스크롤 비활성화
private let tableView: SelfSizingTableView = {
let view = SelfSizingTableView()
...
view.isScrollEnabled = false // <-
return view
}()
- addSubview
- view에 scrollView를 넣고, scrollView에 stackView 삽입
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView) // <-
scrollView.addSubview(stackView) // <-
view.addSubview(button)
view.addSubview(label)
- 레이아웃
- 핵심 - stackView의 leading, trailing 말고도 widthAnchor도 추가해야 콘텐츠가 표기
- UIScrollView를 사용할때 세로 스크롤을 이용하고 싶으면 scroll의 width를 고정시켜야하고, 가로 스크롤을 이용하고 싶으면 height를 고정
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView) // <-
scrollView.addSubview(stackView) // <-
view.addSubview(button)
view.addSubview(label)
stackView.addArrangedSubview(tableView)
tableView.dataSource = self
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor), // 주의
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 16),
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -120)
])
}
(결과)
* 전체 코드: https://github.com/JK0369/ExSelfSizingTableView
* 참고
https://stackoverflow.com/questions/29779128/how-to-make-a-random-color-with-swift
'iOS 응용 (swift)' 카테고리의 다른 글
Comments