Потяните, чтобы обновить источник данных rx таблицы. - PullRequest
0 голосов
/ 28 ноября 2018

В моем мобильном приложении я хотел бы обновить источник данных tableView с помощью запроса на обновление, но я не знаю, как вставить новые элементы поверх источника данных tableview.

Я вижу, что есть метод insertRows, такой как: self.tableView? .InsertRows (at: [indexPath], with: .top), но как мне добавить newItems здесь в соответствии с моими методами??

У меня есть функция initializedTableView (), которая инициализирует tableView с наблюдаемыми элементами PublishSubject.

func initializeTableView() {

    viewModel
        .items
        .subscribe(onNext: { items in

            self.tableView?.delegate = nil
            self.tableView?.dataSource = nil

            Observable.just(items)
                .bind(to:(self.tableView?.rx.items(cellIdentifier: 
                 itemCell.Identifier, cellType: itemCell.self))!) { 
                 (index, element, cell) in

                    cell.itemModel = element

                }.disposed(by: self.disposeBag)
        })
        .disposed(by: disposeBag)
}

Эта функция вызывается, когда пользователь запрашивает обновление для обновления:

func refreshTableView() {

    // get new items
    viewModel
        .newItems
        .subscribe(onNext: { newItems in

            //new
            let new = newItems.filter({ item in
                // items.new == true
            })

            //old
            var old = newItems.filter({ item -> Bool in
                // items.new == false
            })

            new.forEach({item in
                // how to update tableView.rx.datasource here???

            })

 }).disposed(by: disposeBag)
 }

Ответы [ 3 ]

0 голосов
/ 29 ноября 2018
struct ViewModel {
    let items: Observable<[Item]>

    init(trigger: Observable<Void>, newItems: @escaping () -> Observable<[Item]>) {
        items = trigger
            .flatMapLatest(newItems)
            .scan([], accumulator: { $1 + $0 })
    }
}

Выше не обрабатывает ошибки и не сбрасывает, но scan поместит новые элементы в верхнюю часть списка.

Ситуация не совсем правильная.Обычно вызов API возвращает все элементы. Как он может узнать, какие элементы являются «новыми»?

0 голосов
/ 04 декабря 2018
struct ViewModel {
    let items: BehaviorRelay<[Item]>

    init() {
        self.items = BehaviorRelay(value: [])
    }

    func fetchNewItems() {
        // This assumes you are properly distinguishing which items are new 
        // and `newItems` does not contain existing items
        let newItems: [Item] = /* However you get new items */

        // Get a copy of the current items
        var updatedItems = self.items.value

        // Insert new items at the beginning of currentItems
        updatedItems.insert(contentsOf: newItems, at: 0)

        // For simplicity this answer assumes you are using a single cell and are okay with a reload
        // rather than the insert animations.
        // This will reload your tableView since 'items' is bound to the tableView items
        //
        // Alternatively, you could use RxDataSources and use the `RxTableViewSectionedAnimatedDataSource`
        // This will require a section model that conforms to `AnimatableSectionModelType` and some
        // overall reworking of this example
        items.accept(updatedItems)
    }
}

final class CustomViewController: UIViewController {

    deinit {
        disposeBag = DisposeBag()
    }

    @IBOutlet weak var tableView: UITableView!

    private var disposeBag = DisposeBag()
    private let viewModel = ViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomTableCell.self, forCellReuseIdentifier: "ReuseID")
        tableView.refreshControl = UIRefreshControl()

        viewModel.items
            .bind(to: tableView.rx.items(cellIdentifier: "ReuseID", cellType: CustomTableCell.self)) { row, item, cell in
                // Configure cell with item
                cell.configure(with: item)
        }
        .disposed(by: disposeBag)

        tableView.refreshControl?.rx.controlEvent(.valueChanged)
            .subscribe(onNext: { [weak self] in
                self?.viewModel.fetchNewItems()
            })
            .disposed(by: disposeBag)
    }
}

Альтернативный ответ с использованием BehaviorRelay и привязок.Таким образом, вы обновляете только реле items, и оно автоматически обновляет tableView.Он также предоставляет более "Rx" способ обработки pull to refresh.

Как упоминалось в комментариях к коду, предполагается, что вы определяете, какие элементы являются новыми, а newItems не содержит каких-либо существующих элементов.В любом случае это должно послужить отправной точкой.

0 голосов
/ 28 ноября 2018

Я сделал нечто подобное с моим приложением, так как у меня были проблемы с tableView.insertRows.

Вот код:

func loadMoreComments() {
    // call to backend to get more comments
    getMoreComments { (newComments) in
        // combine the new data and your existing data source array
        self.comments = newComments + self.comments
        self.tableView.reloadData()
        self.tableView.layoutIfNeeded()
        // calculate the total height of the newly added cells
        var addedHeight: CGFloat = 0
        for i in 0...result.count {
            let indexRow = i
            let tempIndexPath = IndexPath(row: Int(indexRow), section: 0)
            addedHeight = addedHeight + self.tableView.rectForRow(at: tempIndexPath).height
        }
        // adjust the content offset by how much height was added to the start so that it looks the same to the user
        self.tableView.contentOffset.y = self.tableView.contentOffset.y + addedHeight
    }
}

Итак, вычисляя высоты новых ячеек, являющихсядобавив в начало и затем добавив эту вычисленную высоту к tableView.contentOffset.y, я смог легко добавить ячейки в верхнюю часть tableView, не переделывая мой tableView.Это может выглядеть как резкое обходное решение, но смещение в tableView.contentOffset не заметно, если вы правильно рассчитаете высоту.

...