일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- RxCocoa
- HIG
- collectionview
- 리펙터링
- clean architecture
- Observable
- rxswift
- combine
- map
- Xcode
- UITextView
- 리팩토링
- 리펙토링
- uiscrollview
- uitableview
- swiftUI
- 애니메이션
- Refactoring
- swift documentation
- SWIFT
- ribs
- 클린 코드
- Protocol
- 스위프트
- Human interface guide
- MVVM
- tableView
- ios
- Clean Code
- UICollectionView
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 16. table view의 커스텀 cell (xib, nib이용) / 셀 클릭 중복 바인딩 방지 본문
[iOS - swift] 16. table view의 커스텀 cell (xib, nib이용) / 셀 클릭 중복 바인딩 방지
jake-kim 2020. 8. 23. 18:12xib, nib란?
Xml Interface Builder: 플랫파일(아무런 구조적 상호관계각 없는 레코드들이 들어 있는 파일) -> nib와 기능적으로 동일
Next Interface Builder: nib는 바이너리 파일
즉, xlb를 nib로 바꾼 후 bundle(실행할 때 같이 실행되는 파일)에 올리는 것
바이너리 파일들을 xcode에서 관리하기 힘들기 때문에 xlb로 먼저 저장
custom cell 생성
배치 및 IBOutet생성
bind함수 정의
bind함수 정의 이유: 추후에 tableView의 델리게이트 함수에서 cell을 만든 후, dataSource에 관한 값을 cell에 입력할 때 아래와 같이 이용
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.deque(TermsCell.self)
cell.bind(viewModel.dataSource[indexPath.row])
updateCheckBoxImage(cell, indexPath.row)
cell.btnCheck.rx.tap.asDriverOnErrorNever()
.drive(onNext: { [weak self] in
self?.viewModel.btnCheck()
}).disposed(by: cell.disposeBag)
cellComponentTapEvent(cell, indexPath.row)
return cell
}
정의 방법 - cell을 정의하는 .swift파일에 (#그림1 에서) bind함수 정의
func bind(_ data: TermsContents) {
lblTitle.text = data.title.precomposedStringWithCanonicalMapping // NFD -> NFC
if data.isMandatory {
lblOption.text = "(필수)"
} else {
lblOption.text = "(선택)"
}
if data.contents == "" {
btnDesc.isHidden = true
} else {
btnDesc.isHidden = false
}
isAccept = data.isAccept
}
* 중요한 disposeBag선언:
1) disposeBag을 cell에 선언
2) prepareForReuse()함수에 disposeBag을 최기화 (cell이 재사용될 때 바인딩이 초기화 될 수 있게끔 함)
class InfoCell: UITableViewCell {
@IBOutlet weak var btnCheck: UIButton!
@IBOutlet weak var lblTitle: UILabel!
@IBOutlet weak var btnDesc: UIButton!
var isChecked = false
var disposeBag = DisposeBag()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag()
}
func bind(_ data: Memo) {
lblTitle.text = data.title
if isChecked {
btnCheck.setImage(UIImage(named: "system_24_checkedbox"), for: .normal)
} else {
btnCheck.setImage(UIImage(named: "system_24_checkbox"), for: .normal)
}
}
}
* 각 셀에 disposeBag을 넣어줘야 하는 이유: VC에서 데이터를 업데이트된 후 tableView.reloadData()를 호출하면 다음 델리게이트 함수가 호출되는데, 이 함수에서 셀 안에 버튼과 같은 컴포넌트에 바인딩을 시켜주는 작업을 한다면 여러번 바인딩되는 현상이 발생(reloadData를 호출하는 횟수만큼)
-> 셀 안에 버튼을 클릭하면 그만큼 이벤트를 발생하여 여러번 클릭한 것으로 간주됨.
그래서, 위에서 셀이 재사용 될때 불리는 함수인 prepareForReuse()에서 기존 바인딩을 제거할 수 있도록 disposeBag을 cell에서 정의한 것으로 사용할 것 "disposed(by: cell.disposeBag)"
// tableView의 델리게이트 함수인, tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell에서
cell.btnCheck.rx.tap.asDriverOnErrorNever()
.drive(onNext: { [weak self] in
self?.viewModel.btnCheck()
}).disposed(by: cell.disposeBag)
cell 등록
cell등록: xib -> nib
// viewDidLoad
myTalbeView.dataSource = self
myTableView.delegate = self
myTableView.register(UINib(nibName: myCell.className, bundle: nil), forCellReuseIdentifier: myCell.className)
cell객체 획득:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: myCell.className) as! myCell
cell.bind(viewModel.dataSource[indexPath.row])
cell.btnCheck.rx.tap.asDriverOnErrorNever()
.drive(onNext: { [weak self] in
self?.viewModel.btnCheck()
}).disposed(by: cell.disposeBag)
return cell
}
cf) tableView의 cell의 높이를 동적으로 설정하는 방법
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}