skip to Main Content

I have used RxCollectionViewSectionedReloadDataSource to load my data into UICollectionView.

let dataSource = RxCollectionViewSectionedReloadDataSource<SectionModel<String, WorkGroup>>(
            configureCell: { (_, collectionView, indexPath, item) in
                guard let cell = collectionView
                        .dequeueReusableCell(withReuseIdentifier: WorkGroupCell.identifier, for: indexPath) as? WorkGroupCell else {
                            return WorkGroupCell()
                        }
                cell.viewModel = item
                return cell
            }
        )
        
        viewModel.items.bind(to: tileCollectonView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)
 
        tileCollectonView.rx.setDelegate(self).disposed(by: disposeBag)

Using above code i can display the data. But i want to drag and drop(Reorder) the cell.

Please let me know how can i do that reorder using RxSwift.

Help will be highly appreciated.

2

Answers


  1. As is often the case, you need a state machine.

    The logic involved is quite simple:

    struct State<Item> {
        var items: [Item] = []
    }
    
    func state<Item>(initialState: State<Item>, itemMoved: Observable<ItemMovedEvent>) -> Observable<State<Item>> {
        itemMoved
            .scan(into: initialState) { (state, itemMoved) in
                state.items.move(from: itemMoved.sourceIndex.row, to: itemMoved.destinationIndex.row)
            }
            .startWith(initialState)
    }
    
    extension Array
    {
        mutating func move(from oldIndex: Index, to newIndex: Index) {
            if oldIndex == newIndex { return }
            if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
            self.insert(self.remove(at: oldIndex), at: newIndex)
        }
    }
    

    The above uses the ItemMovedEvent provided by RxCocoa for table views. In order to create something like that for a collection view, you will need to wrap the UICollectionViewDragDelegate in a delegate proxy. An article on how to do that can be found here: Convert a Swift Delegate to RxSwift Observables

    Login or Signup to reply.
  2. so I assume that you are using RxDataSources

    after you set datasource variable, you need to set two more properties: canMoveItemAtIndexPath, moveItem

    // in CollectionViewSectionedDataSource class
    public typealias MoveItem = (CollectionViewSectionedDataSource<Section>, _ sourceIndexPath:IndexPath, _ destinationIndexPath:IndexPath) -> Void
    public typealias CanMoveItemAtIndexPath = (CollectionViewSectionedDataSource<Section>, IndexPath) -> Bool
    
    let dataSource = ...
    dataSource.canMoveItemAtIndexPath = { dataSource, indexPath in
        return true
    }
            
    dataSource.moveItem = {dataSource, source, destination in
        ...
    }
    

    and use UICollectionView methods to start/update/end interaction.
    in my case I added long press gesture to handle dragging

        //in viewDidLoad
        let startDragGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleDragging))
        collectionView.addGestureRecognizer(startDragGesture)
        ...
    
        
        @objc func handleDragging(gesture: UILongPressGestureRecognizer){
            switch gesture.state {
            case .began:
                guard let indexPath = collectionView.indexPathForItem(at: gesture.location(in: collectionView)) else { return }
                
                collectionView.beginInteractiveMovementForItem(at: indexPath)
            case .changed:
                collectionView.updateInteractiveMovementTargetPosition(gesture.location(in: collectionView))
            case .ended:
                collectionView.endInteractiveMovement()
            default:
                collectionView.cancelInteractiveMovement()
            }
        }
    

    then you can drag your cell

    in moveItem closure, you should move actual data in your collection.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search