관리 메뉴

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

[iOS - swift] RxDataSources 스와이프하여 삭제 (swipe to delete) 적용 방법, UITableView 본문

iOS 응용 (swift)

[iOS - swift] RxDataSources 스와이프하여 삭제 (swipe to delete) 적용 방법, UITableView

jake-kim 2022. 6. 23. 22:27

스와이프하여 Delete

사용한 프레임워크

구현 아이디어

  • RxDataSources의 인스턴스가 가지고 있는 canEditRowAtIndexPath라는 프로퍼티를 사용하여 delete 기능 활성화
dataSource.canEditRowAtIndexPath = { _, _ in true }
  • tableView의 rx.itemDeleted를 바인딩하여 처리
self.tableView.rx.itemDeleted
  .bind { ... }

RxDataSource를 사용한 UITableView 구현

  • 예제에 사용할 SectionModel 
    • Section의 데이터는 사용하지 않을 것이지만, section이 존재할때 어떻게 사용되는지를 예를 위해 존재
import RxDataSources

struct SomeType {
  typealias Model = SectionModel<Section, Item>
  
  enum Section: Equatable {
    case date(date: Date)
  }
  enum Item: Equatable {
    case record(title: String?)
  }
}
  • 예제에 사용할 ViewController
import UIKit
import RxSwift
import RxCocoa
import RxDataSources

class ViewController: UIViewController {

}
  • tableView 선언
  private let tableView: UITableView = {
    let view = UITableView()
    view.allowsSelection = true
    view.backgroundColor = .clear
    view.separatorStyle = .none
    view.bounces = true
    view.showsVerticalScrollIndicator = true
    view.contentInset = .zero
    view.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
  }()
  • 예제로 사용할 데이터 정의
  var items = BehaviorSubject<[SomeType.Model]>(
    value: [
      SomeType.Model(
        model: .date(date: Date()),
        items: (0...100)
          .map(String.init)
          .map { SomeType.Model.Item.record(title: $0) }
      )
    ]
  )
  • RxDataSource 인스턴스 정의
// in viewDidLoad

let dataSource = RxTableViewSectionedReloadDataSource<SomeType.Model> { dataSource, tableView, indexPath, item in
  switch item {
  case let .record(title):
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    cell.textLabel?.text = title
    return cell
  }
}
  • 데이터와 tableView의 dataSource를 바인딩
self.items
  .distinctUntilChanged()
  .bind(to: self.tableView.rx.items(dataSource: dataSource))
  .disposed(by: self.disposeBag)

스와이프하여 삭제 구현

  • 위에서 만든 dataSource 인스턴스를 사용하여 edit 기능 활성화
    • 여기까지하고 스와이프하여 delete하려고 해도 동작 안하므로 주의 (rx.itemDeleted 바인딩까지 해야 활성화)
dataSource.canEditRowAtIndexPath = { _, _ in true }
  • rx.itemDeleted 바인딩
    • RxDataSources에서 제공하는 SectionModel은 바로 섹션 -> 아이템들에 접근이 가능
    • 특정 아이템을 제거 후 onNext로 업데이트
self.tableView.rx.itemDeleted
  .observe(on: MainScheduler.asyncInstance)
  .withUnretained(self)
  .bind { ss, indexPath in
    guard var section = try? ss.items.value() else { return }
    var updateSection = section[indexPath.section]
    
    // Update item
    updateSection.items.remove(at: indexPath.item)
    
    // Update section
    section[indexPath.section] = updateSection
    
    // Emit
    ss.items.onNext(section)
  }
  .disposed(by: self.disposeBag)

* 전체 코드: https://github.com/JK0369/ExRxDataSources

Comments