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
- collectionview
- Refactoring
- 클린 코드
- map
- Clean Code
- tableView
- uiscrollview
- combine
- ios
- UICollectionView
- 애니메이션
- uitableview
- 스위프트
- Protocol
- swift documentation
- MVVM
- clean architecture
- 리펙토링
- 리펙터링
- 리팩토링
- RxCocoa
- SWIFT
- UITextView
- swiftUI
- HIG
- ribs
- Observable
- Human interface guide
- Xcode
- rxswift
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 1. Section, Item 모델링 - 단일 Section, 다중 Item 모델링 방법 (UITableView, UICollectionView) 본문
iOS 실전 (swift)
[iOS - swift] 1. Section, Item 모델링 - 단일 Section, 다중 Item 모델링 방법 (UITableView, UICollectionView)
jake-kim 2022. 5. 26. 22:561. Section, Item 모델링 - 단일 Section, 다중 Item 모델링 방법 (UITableView, UICollectionView)
2. Section, Item 모델링 - 다중 Section, 다중 Item 모델링 방법 (UITableView, UICollectionView)
예제에 사용한 framework
- Cell안의 UI 인터렉션 처리를 위해 일반적으로 많이 사용하는 비동기 처리 방식 예제를 보이기 위해 아래 프레임워크 사용
- RxSwift와 RxCocoa 사용
다중 Item 모델링 아이디어
- ViewController에서 UITableView를 가지고 있고 이 tableView의 셀 처리를 쉽게 하고싶은 경우 모델링이 중요
- tableView에서 사용할 데이터 소스의 형태는 아래와 같은 형태
var items = [MyItem]()
- MyItem은 enum으로 정의하고 associative type으로 각 셀에 표출할 데이터 모델 사용
- associative type에 모델을 배열 타입으로 넣어서 받도록 구현할 수 있지만, cell을 처리하는 쪽에서 불편하므로 배열이 아닌 단일 Model로 정의
enum MyItem {
case normal
case special(text: String)
}
위같은 형태로 사용할때의 장점)
- 장점 1) cellForRowAt 델리게이트에서 셀을 정의할 때 items[indexPath.row]로 놓고 switch문으로 셀 타입을 쉽게 구분이 가능
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
switch self.items[indexPath.item] {
case .normal:
cell.prepare(text: nil)
return cell
case let .special(text):
cell.prepare(text: text)
cell.buttonTapObservable
.throttle(.microseconds(500), scheduler: MainScheduler.asyncInstance)
.bind { print("tap button!") }
.disposed(by: cell.disposeBag)
return cell
}
}
- 장점 2) 셀이 탭되면 호출되는 didSelectRowAt 델리게이트 메소드에서도 쉽게 구분이 가능
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch self.items[indexPath.row] {
case .normal:
print("did tap normal cell")
case .special:
print("did tap normal special")
}
}
뷰와 사용하는 쪽
- 셀 정의
import UIKit
import RxSwift
final class MyCell: UITableViewCell {
private let myImageView: UIImageView = {
let image = UIImageView()
image.image = UIImage(named: "dog")
image.isUserInteractionEnabled = false
image.translatesAutoresizingMaskIntoConstraints = false
return image
}()
private let label: UILabel = {
let label = UILabel()
label.text = "special 셀"
label.font = .systemFont(ofSize: 24)
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let button: UIButton = {
let button = UIButton()
button.setTitleColor(.systemBlue, for: .normal)
button.setTitleColor(.blue, for: .highlighted)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
var buttonTapObservable: Observable<Void> {
self.button.rx.tap.asObservable()
}
var disposeBag = DisposeBag()
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(self.myImageView)
self.contentView.addSubview(self.label)
self.contentView.addSubview(self.button)
NSLayoutConstraint.activate([
self.myImageView.bottomAnchor.constraint(lessThanOrEqualTo: self.contentView.bottomAnchor),
self.myImageView.topAnchor.constraint(greaterThanOrEqualTo: self.contentView.topAnchor),
self.myImageView.leftAnchor.constraint(equalTo: self.contentView.leftAnchor),
self.myImageView.widthAnchor.constraint(equalToConstant: 120),
self.myImageView.heightAnchor.constraint(equalToConstant: 120)
])
NSLayoutConstraint.activate([
self.label.centerYAnchor.constraint(equalTo: self.contentView.centerYAnchor),
self.label.centerXAnchor.constraint(equalTo: self.contentView.centerXAnchor),
])
NSLayoutConstraint.activate([
self.button.rightAnchor.constraint(equalTo: self.contentView.rightAnchor, constant: -16),
self.button.centerYAnchor.constraint(equalTo: self.contentView.centerYAnchor),
])
}
override func prepareForReuse() {
super.prepareForReuse()
self.disposeBag = DisposeBag()
self.prepare(text: nil)
}
func prepare(text: String?) {
self.button.isHidden = text == nil
self.label.isHidden = text == nil
print(self.label.isHidden)
self.button.setTitle(text, for: .normal)
}
}
- 예제 데이터 준비
// ViewController.swift
var items: [MyItem] = [
.normal,
.special(text: "버튼1"),
.normal,
.special(text: "버튼2"),
.special(text: "버튼3"),
.normal,
]
- tableView 준비
// ViewController.swift
private let tableView: UITableView = {
let view = UITableView()
view.allowsSelection = false
view.backgroundColor = .clear
view.separatorStyle = .none
view.bounces = true
view.showsVerticalScrollIndicator = true
view.contentInset = .zero
view.register(MyCell.self, forCellReuseIdentifier: "MyCell")
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
- 테이블 뷰 레이아웃과 델리게이트 할당
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.tableView)
NSLayoutConstraint.activate([
self.tableView.topAnchor.constraint(equalTo: self.view.topAnchor),
self.tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
self.tableView.leftAnchor.constraint(equalTo: self.view.leftAnchor),
self.tableView.rightAnchor.constraint(equalTo: self.view.rightAnchor),
])
self.tableView.dataSource = self
self.tableView.delegate = self
}
- 델리게이트 정의
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
switch self.items[indexPath.item] {
case .normal:
cell.prepare(text: nil)
return cell
case let .special(text):
cell.prepare(text: text)
cell.buttonTapObservable
.throttle(.microseconds(500), scheduler: MainScheduler.asyncInstance)
.bind { print("tap button!") }
.disposed(by: cell.disposeBag)
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch self.items[indexPath.row] {
case .normal:
print("did tap normal cell")
case .special:
print("did tap normal special")
}
}
}
cf) 만약 다중 Section을 사용하면 아래처럼 Item들을 배열로 넣고 처리..?
-> 다음 포스팅 글에서 확인
// 모델 정의
enum MySection {
case profile([MyItem1])
case option([MyItem2])
case title([MyItem3])
enum MyItem1 {
case normal
case special(text: String)
}
enum MyItem2 {
case someItem
case someSpecialItem
}
enum MyItem3 {
case otherItem
case someOtherItem
}
}
// 사용하는 쪽
let sections: [MySection]
'iOS 실전 (swift)' 카테고리의 다른 글
[iOS - swift] 작은 디바이스 대응 방법 (autolayout) (0) | 2022.06.11 |
---|---|
[iOS - swift] 2. Section, Item 모델링 - 다중 Section, 다중 Item 모델링 방법 (UITableView, UICollectionView) (2) | 2022.05.27 |
[iOS - swfit] 18. Scroll view 구현 (storyboard 이용) (0) | 2021.03.14 |
[iOS - swift] status bar, status bar view 속성 (배경, 글씨 색깔) 동적으로 변경하는 방법 (0) | 2021.01.21 |
[iOS - swift] 선 그리기 (UIBezierPath, CAShapeLayer) (0) | 2020.12.17 |
Comments