0

I am having some issues with the RxDataSources cell reload animations for RxSwift. I have a simple table setup like so:

import UIKit
import RxDataSources
import RxCocoa
import RxSwift
import Fakery

class ViewController1: UIViewController {


    @IBOutlet weak var tableView: UITableView!
    let bag = DisposeBag()



    override func viewDidLoad() {
        super.viewDidLoad()
        setupTableView()
    }

    private func setupTableView() {
        tableView.register(UINib(nibName: "TestTableViewCell", bundle: nil), forCellReuseIdentifier: "cell")

        let dataSource = RxTableViewSectionedAnimatedDataSource<SectionOfTestData>(
            animationConfiguration: AnimationConfiguration(insertAnimation: .none, reloadAnimation: .none, deleteAnimation: .none),
            configureCell: { dataSource, tableView, indexPath, element in
                let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TestTableViewCell
                cell.testData = element
                return cell
            })

        someData
            .bind(to: tableView.rx.items(dataSource: dataSource))
            .disposed(by: bag)
    }

    let someData = BehaviorRelay<[SectionOfTestData]>(value: [SectionOfTestData(items: [
        TestData(color: .red, name: "Henry"),
        TestData(color: .blue, name: "Josh")
    ])])

    @IBAction func didTapUpdateButton(_ sender: Any) {
        let colors: [UIColor] = [.blue, .purple, .orange, .red, .brown]


        let items = someData.value.first!.items

        // Add random data when button is tapped
        someData.accept([SectionOfTestData(items: items + [TestData(color: colors.randomElement()!, name: Faker().name.firstName())])])
    }

}

The models:

struct TestData {
    let color: UIColor
    let name: String
}

extension TestData: IdentifiableType, Equatable {
    typealias Identity = Int

    var identity: Identity {
           return Int.random(in: 0..<20000)
    }
}

struct SectionOfTestData {
    var items: [Item]

    var identity: Int {
        return 0
    }
}

extension SectionOfTestData: AnimatableSectionModelType {
    typealias Identity = Int
    typealias Item = TestData

    // Implement default init
    init(original: SectionOfTestData, items: [Item]) {
        self = original
        self.items = items
    }
}

class TestTableViewCell: UITableViewCell {

    @IBOutlet weak var colorView: UIView!
    @IBOutlet weak var nameLabel: UILabel!

    var testData: TestData! {
        didSet {
            colorView.backgroundColor = testData.color
            nameLabel.text = testData.name
        }
    }

}

When the button is tapped the BehaviorRelay is updated and the table seems to refresh however the "animations" are always the same. In the supplied code I have actually set all animation types to .none yet it is still performing an animation. If I try to change the animation type to another type such as .bottom again the animation is the same. What am I doing wrong here?

enter image description here

Is this a reload animation or insert animation? I have no idea if the table reloads or inserts when the data is updated, I can't find any information in the documents. Any pointers on this would be greatly appreciated!

Kex
  • 8,023
  • 9
  • 56
  • 129

2 Answers2

2

Your problem is:

var identity: Identity {
    return Int.random(in: 0..<20000)
}

RxDataSources uses the identity value in order to compute the changeset. You have implemented it in a way that essentially returns a new value each time (unless you get a collision) so from the framework point of view, you are always removing all the items and adding new items. You can check this by implementing

decideViewTransition: { _, _, changeset in
    print(changeset)
    return .animated
}
Fabio Felici
  • 2,841
  • 15
  • 21
  • Can’t believe I didn’t notice that! Of course the id is different I’m computing a new one when calling the random function each time. Thanks! Btw do you know if reloadData is called or insert row is called when the data changes? – Kex Jan 13 '20 at 14:07
  • 1
    If `decideViewTransition` returns `.animated` it calls `performBatchUpdates` passing a closure that calls `insertRows`, `deleteRows` and so on depending on the changeset. If `decideViewTransition` returns `.reload` it will call `reloadData`. – Fabio Felici Jan 13 '20 at 14:57
  • Thanks! Helps a lot. – Kex Jan 13 '20 at 16:13
  • However it seems that for UICollection view it always uses the fade animation for all styles. I posted another question if you have time to look https://stackoverflow.com/questions/59727464/rxdatasources-collection-view-cell-always-uses-fade-for-insert-cell-animation-c – Kex Jan 14 '20 at 04:19
1

To complete the accepted answer :

make a uuid and pass it to identity:

let id = UUID().uuidString
var identity: String {
   return id
}

then the animations work perfectly.

Mehrdad
  • 1,050
  • 1
  • 16
  • 27