Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 16. table view의 커스텀 cell (xib, nib이용) / 셀 클릭 중복 바인딩 방지 본문

iOS 실전 (swift)

[iOS - swift] 16. table view의 커스텀 cell (xib, nib이용) / 셀 클릭 중복 바인딩 방지

jake-kim 2020. 8. 23. 18:12

xib, nib란?


Xml Interface Builder: 플랫파일(아무런 구조적 상호관계각 없는 레코드들이 들어 있는 파일) -> nib와 기능적으로 동일

Next Interface Builder: nib는 바이너리 파일

 

즉, xlb를 nib로 바꾼 후 bundle(실행할 때 같이 실행되는 파일)에 올리는 것

바이너리 파일들을 xcode에서 관리하기 힘들기 때문에 xlb로 먼저 저장

custom cell 생성


배치 및 IBOutet생성


#그림1 - 왼쪽에 적절한 레이아웃 배치, 오른쪽에 IBOutlet 생성

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
    }
Comments