使用 RxDatasources 绑定到数据源
Binding to a datasource using RxDatasources
我有一个从服务器获取的模型列表,基本上我得到了这些模型的数组:
struct ContactModel: Codable, Equatable {
static func == (lhs: ContactModel, rhs: ContactModel) -> Bool {
return lhs.name == rhs.name &&
lhs.avatar == rhs.avatar &&
lhs.job == rhs.job &&
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) -> Bool {
return lhs.model == rhs.model
}
var identity: Int {
return model.id
}
var model: ContactModel
var cellIdentifier = ContactTableViewCell.identifier
}
我想做的是使用 RxDatasources
创建数据源并绑定到它,就像这样(ContactsViewController.swift):
let dataSource = RxTableViewSectionedAnimatedDataSource<ContactsSectionModel>(
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) -> ContactsSectionModel in
//what should I return here?
}.bind(to: dataSource)
我使用 combineLatest
,因为我还有一个可观察对象 (self.contactViewModel.changedStatusForContact
),它会在某些联系人已更改时发出通知(当您点击联系人单元格上的某个按钮时会发生这种情况)。
那么我应该从上面的 .map return 做什么才能成功绑定到先前创建的 dataSource
?
您必须更换已更改的联系人;所有已更改的联系人,但您不能这样做,因为您没有跟踪所有联系人,而只跟踪最近的联系人。所以你不能在地图上做到这一点。您需要改用 scan
。
我对你没有的代码做了很多假设post,所以下面是一个可编译的例子。如果您的类型不同,那么您将不得不进行一些更改:
func example(contacts: Observable<[ContactModel]>, changedStatusForContact: Observable<ContactModel>, tableView: UITableView, disposeBag: DisposeBag) {
let dataSource = RxTableViewSectionedAnimatedDataSource<ContactsSectionModel>(
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: [=10=], changed: )
}
.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 { [=10=].items.map { [=10=].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: { [=10=].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: [=10=]) }
)]
// rebuild the section model and return it.
}
contactsSectionModels
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
}
typealias ContactsSectionModel = AnimatableSectionModel<String, ContactCellModel>
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"
}
我有一个从服务器获取的模型列表,基本上我得到了这些模型的数组:
struct ContactModel: Codable, Equatable {
static func == (lhs: ContactModel, rhs: ContactModel) -> Bool {
return lhs.name == rhs.name &&
lhs.avatar == rhs.avatar &&
lhs.job == rhs.job &&
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) -> Bool {
return lhs.model == rhs.model
}
var identity: Int {
return model.id
}
var model: ContactModel
var cellIdentifier = ContactTableViewCell.identifier
}
我想做的是使用 RxDatasources
创建数据源并绑定到它,就像这样(ContactsViewController.swift):
let dataSource = RxTableViewSectionedAnimatedDataSource<ContactsSectionModel>(
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) -> ContactsSectionModel in
//what should I return here?
}.bind(to: dataSource)
我使用 combineLatest
,因为我还有一个可观察对象 (self.contactViewModel.changedStatusForContact
),它会在某些联系人已更改时发出通知(当您点击联系人单元格上的某个按钮时会发生这种情况)。
那么我应该从上面的 .map return 做什么才能成功绑定到先前创建的 dataSource
?
您必须更换已更改的联系人;所有已更改的联系人,但您不能这样做,因为您没有跟踪所有联系人,而只跟踪最近的联系人。所以你不能在地图上做到这一点。您需要改用 scan
。
我对你没有的代码做了很多假设post,所以下面是一个可编译的例子。如果您的类型不同,那么您将不得不进行一些更改:
func example(contacts: Observable<[ContactModel]>, changedStatusForContact: Observable<ContactModel>, tableView: UITableView, disposeBag: DisposeBag) {
let dataSource = RxTableViewSectionedAnimatedDataSource<ContactsSectionModel>(
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: [=10=], changed: )
}
.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 { [=10=].items.map { [=10=].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: { [=10=].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: [=10=]) }
)]
// rebuild the section model and return it.
}
contactsSectionModels
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
}
typealias ContactsSectionModel = AnimatableSectionModel<String, ContactCellModel>
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"
}