Search code examples
swiftuitableviewrx-swiftrx-cocoa

RxSwift + canMoveRowAt


I'm using RxSwift (RxCocoa) to populate my tableView cells

viewModel.cellsDriver.drive(tableView.rx.items) { ... }

This way I have no access to tableView's dataSource's method

func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool

But I need to specify which rows are movable for

tableView.rx.itemMoved

How can I restrict which rows should not be able to drag around? I would prefer avoid using RxDataSources


Solution

  • In order to do this, you need to do one of two things. Either pull in the RxDataSource Cocoapod and use one of the TableViewSectionedDataSource subclasses or make your own data source.

    It's not hard to make a data source. Here's an example: https://github.com/dtartaglia/RxMultiCounter/blob/master/RxMultiCounter/RxExtensions/RxSimpleAnimatableDataSource.swift I used DifferenceKit in mine, but if you want to be super simple, you can just call tableView.reloadData() in your func tableView(_:observedEvent) method.

    Here's an example:

    //
    //  RxTableViewMovableRowDataSource.swift
    //
    //  Created by Daniel Tartaglia on 12/19/18.
    //  Copyright © 2018 Daniel Tartaglia. MIT License.
    //
    
    import UIKit
    import RxSwift
    import RxCocoa
    
    
    class RxTableViewMovableRowDataSource<E, Cell: UITableViewCell>: NSObject, RxTableViewDataSourceType, UITableViewDataSource {
    
        init(identifier: String, configure: @escaping (Int, E, Cell) -> Void, canMoveRowAt: ((Int, E) -> Bool)? = nil) {
            self.identifier = identifier
            self.configure = configure
            self.canMoveRowAt = canMoveRowAt
        }
    
        func tableView(_ tableView: UITableView, observedEvent: Event<[E]>) {
            values = observedEvent.element ?? []
            tableView.reloadData()
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return values.count
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as! Cell
            let row = indexPath.row
            configure(row, values[row], cell)
            return cell
        }
    
        func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
            let row = indexPath.row
            return canMoveRowAt?(row, values[row]) ?? true
        }
    
        let identifier: String
        let configure: (Int, E, Cell) -> Void
        let canMoveRowAt: ((Int, E) -> Bool)?
        var values: Element = []
    }