【问题标题】:Binding to a datasource using RxDatasources使用 RxDatasources 绑定到数据源
【发布时间】:2022-01-01 20:47:21
【问题描述】:

我有一个从服务器获取的模型列表,基本上我得到了这些模型的数组:

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 返回什么才能成功绑定到之前创建的dataSource

【问题讨论】:

    标签: swift tableview rx-swift rxdatasources


    【解决方案1】:

    您必须更换已更改的联系人;所有已更改的联系人,但您不能这样做,因为您没有跟踪所有联系人,仅跟踪最近的一个。所以你不能在地图中做到这一点。您需要改用scan

    我对你没有发布的代码做了很多假设,所以下面是一个可编译的例子。如果您的类型不同,则必须进行一些更改:

    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: $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 = 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"
    }
    

    【讨论】:

    • 是的,你是对的。我只需要将scan 的返回语句中items 中设置的内容从items: updates.original 更改为items: changedModels,就可以了。
    猜你喜欢
    • 2010-12-25
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多