#swift #tableview #rx-swift #rxdatasources
Вопрос:
У меня есть список моделей, которые я получаю с сервера, и в основном я получаю массив из них:
struct ContactModel: Codable, Equatable { static func == (lhs: ContactModel, rhs: ContactModel) -gt; Bool { return lhs.name == rhs.name amp;amp; lhs.avatar == rhs.avatar amp;amp; lhs.job == rhs.job amp;amp; lhs.follow == rhs.follow } let id: Int let name: String let avatar:String? let job: JobModel? let follow:Bool enum CodingKeys: String, CodingKey { case id, name, avatar, job, follow }
}
Поэтому я хочу показать список контактов в своем tableview
.
Теперь у меня также есть эта структура, которая является оболочкой для этой модели:
struct ContactCellModel : Equatable, IdentifiableType { static func == (lhs: ContactCellModel, rhs: ContactCellModel) -gt; Bool { return lhs.model == rhs.model } var identity: Int { return model.id } var model: ContactModel var cellIdentifier = ContactTableViewCell.identifier }
То, что я пытаюсь сделать, — это создать источник данных с помощью RxDatasources
и привязаться к нему, вот так(ContactsViewController.swift):
let dataSource = RxTableViewSectionedAnimatedDataSourcelt;ContactsSectionModelgt;( configureCell: { dataSource, tableView, indexPath, item in if let cell = tableView.dequeueReusableCell(withIdentifier: item.cellIdentifier, for: indexPath) as? BaseTableViewCell{ cell.setup(data: item.model) return cell } return UITableViewCell() })
но я не уверен, что мне следует делать сразу после этого. Я попробовал что-то вроде этого:
Observable.combineLatest(contactsViewModel.output.contacts, self.contactViewModel.changedStatusForContact) .map{ (allContacts, changedContact) -gt; ContactsSectionModel in //what should I return here? }.bind(to: dataSource)
Я использую combineLatest
, потому что у меня есть еще один наблюдаемый ( self.contactViewModel.changedStatusForContact
), который уведомляет, когда определенный контакт был изменен (это происходит, когда вы нажимаете определенную кнопку в ячейке контакта).
Итак, что я должен вернуть из .map выше, чтобы успешно привязаться к ранее созданному dataSource
?
Ответ №1:
Вы должны заменить изменившиеся контакты; все контакты, которые были изменены, но вы не можете этого сделать, потому что вы отслеживаете не все из них, а только самый последний. Так что вы не можете сделать это на карте. Вам нужно использовать scan
вместо этого.
Я сделал много предположений о коде, который вы не публиковали, поэтому ниже приведен компилируемый пример. Если ваши типы отличаются, то вам придется внести некоторые изменения:
func example(contacts: Observablelt;[ContactModel]gt;, changedStatusForContact: Observablelt;ContactModelgt;, tableView: UITableView, disposeBag: DisposeBag) { let dataSource = RxTableViewSectionedAnimatedDataSourcelt;ContactsSectionModelgt;( configureCell: { dataSource, tableView, indexPath, item in if let cell = tableView.dequeueReusableCell(withIdentifier: item.cellIdentifier, for: indexPath) as? BaseTableViewCell { cell.setup(data: item.model) return cell } return UITableViewCell() // this is quite dangerious. Better would be to crash IMO. }) let contactsSectionModels = Observable.combineLatest(contacts, changedStatusForContact) { (original: $0, changed: $1) } .scan([ContactsSectionModel]()) { state, updates in // `state` is the last array handed to the table view. // `updates` contains the values from the combineLatest above. var contactModels = state.flatMap { $0.items.map { $0.model } } // get all the contactModels out of the state. if contactModels.isEmpty { contactModels = updates.original } // if there aren't any, then update with the values coming from `contacts` else { guard let index = contactModels .firstIndex(where: { $0.id == updates.changed.id }) else { return state } contactModels[index] = updates.changed } // otherwise find the index of the contact that changed. // and update the array with the changed contact. return [ContactsSectionModel( model: "", items: updates.original .map { ContactCellModel(model: $0) } )] // rebuild the section model and return it. } contactsSectionModels .bind(to: tableView.rx.items(dataSource: dataSource)) .disposed(by: disposeBag) } typealias ContactsSectionModel = AnimatableSectionModellt;String, ContactCellModelgt; struct ContactCellModel: Equatable, IdentifiableType { var identity: Int { model.id } let model: ContactModel let cellIdentifier = ContactTableViewCell.identifier } struct ContactModel: Equatable { let id: Int let name: String let avatar: String? let job: JobModel? let follow: Bool } struct JobModel: Equatable { } class BaseTableViewCell: UITableViewCell { func setup(data: ContactModel) { } } class ContactTableViewCell: BaseTableViewCell { static let identifier = "Cell" }
Комментарии:
1. Да, ты был прав. Мне нужно было только изменить то , что указано
items
в операторе возврата ascan
, fromitems: updates.original
, toitems: changedModels
, и это сработало.