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 |
Tags
- Protocol
- 애니메이션
- swift documentation
- ios
- RxCocoa
- Human interface guide
- UICollectionView
- collectionview
- 스위프트
- SWIFT
- Refactoring
- ribs
- HIG
- 리팩토링
- 리펙터링
- combine
- Clean Code
- 클린 코드
- swiftUI
- 리펙토링
- MVVM
- clean architecture
- Xcode
- Observable
- uitableview
- rxswift
- map
- uiscrollview
- tableView
- UITextView
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] UICollectionView 동적인 셀 구현 방법 (Dynamic Cell Size, Dynamic height) 본문
카테고리 없음
[iOS - swift] UICollectionView 동적인 셀 구현 방법 (Dynamic Cell Size, Dynamic height)
jake-kim 2023. 9. 1. 00:13동적인 셀
- UICollectionView나 UITableView를 사용할 때 동적인 셀 처리가 필요
- Cell안에서 UIImageView에 URL을 가지고 image를 가져오는 피드와 같은 UI에 Cell의 height가 동적으로 변하는 경우가 존재
동적인 셀 구현 아이디어
- 보통 이미지 라이브러리는 UIImageView의 extension으로 있고 이것을 아래처럼 사용 가능하도록 처리
- 셀 안에서 setImage가 끝났을 때 동적으로 셀의 크기를 변경해주는 작업이 필요
// (Kingfisher 방법)
imageView.kf.setImage(with: url) { ... }
// (SDWebImage 방법)
imageView.sd_setImage(with: url) { ... }
- 뷰를 오토레이아웃으로 처리한다면 Cell안에서 imageView의 size를 지정해놓고 이미지 로드가 완료된 시점에 update를 수행하면 imageView의 크기 업데이트
- 주의사항 - imageView의 크기만 변경해주면 Cell자체의 크기가 변경되지 않음
- imageView만 업데이트하면 Cell의 크기는 자동으로 업데이트 되지 않으므로 invalidateIntrinsicContentSize()를 호출하여 Cell의 크기에도 적용하면 완료
동적인 셀 구현
* 셀 구현 편의를 위해 SnapKit(오토레이아웃 편리), Kingfisher(이미지 캐싱) 사용
target 'ExDynamicHeight' do
use_frameworks!
pod 'SnapKit'
pod 'Kingfisher'
end
- 셀 정의
// MyCell.swift
import UIKit
import SnapKit
import Kingfisher
final class MyCell: UICollectionViewCell {
}
- 필요한 imageView 선언 및 url 선언
// MyCell.swift
private let imageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFill
return view
}()
var url: URL? {
didSet {
// TODO
}
}
- UI 레이아웃 준비
// MyCell.swift
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError()
}
override func prepareForReuse() {
super.prepareForReuse()
url = nil
imageView.image = nil
}
private func setupUI() {
// TODO
}
- setupUI() 구현
- 가정) width값은 디바이스 크기 너비만큼 고정하고, height가 동적으로 변하도록 처리할 것
- edges를 cell에 맞추고, 크기를 업데이트 시켜주기 위해서 width와 height를 선언한 후 priority로 오토레이아웃 충돌나지 않도록 설정
private func setupUI() {
backgroundColor = .lightGray
contentView.addSubview(imageView)
imageView.snp.makeConstraints {
$0.edges.equalToSuperview()
$0.width.equalTo(UIScreen.main.bounds.width).priority(999)
$0.height.equalTo(50).priority(999)
}
}
- url의 didSet 처리
- 이미지 로드가 완료된 경우, imageView에 업데이트
- 핵심) imageView만 업데이트하면 cell의 크기가 바뀌지 않으므로 invalidateIntrinsicContentSize()를 호출
var url: URL? {
didSet {
guard let url else { return }
imageView.kf.setImage(with: url) { [weak self] result in
guard case .success(let value) = result, let self else { return }
imageView.snp.updateConstraints {
$0.height.equalTo(value.image.size.height).priority(999)
}
invalidateIntrinsicContentSize()
}
}
}
동적인 셀 사용
- 위에서 구현한 셀을 사용하는 ViewController에서는 별도 아무런 처리 없어도 손쉽게 사용이 가능
import UIKit
import SnapKit
class ViewController: UIViewController {
private let collectionView = {
let layout = UICollectionViewFlowLayout()
let view = UICollectionView(frame: .zero, collectionViewLayout: layout)
view.register(MyCell.self, forCellWithReuseIdentifier: "cell")
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
layout.minimumInteritemSpacing = 10
layout.minimumLineSpacing = 10
view.contentInsetAdjustmentBehavior = .always
return view
}()
private static var width: Int {
Int(UIScreen.main.bounds.width)
}
private static var height: Int {
(100...800).randomElement()!
}
var dataSource = (1...10)
.compactMap { _ in URL(string: "https://random.imagecdn.app/\(ViewController.width)/\(ViewController.height)") }
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
view.addSubview(collectionView)
collectionView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
dataSource.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print(dataSource)
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? MyCell
cell?.url = dataSource[indexPath.row]
return cell ?? UICollectionViewCell()
}
}
(완성)
* 전체 코드: https://github.com/JK0369/ExDynamicHeight
* 참고
Comments