如何使用 RxDatasource 在 UICollectionView 中创建具有多个标题的多个部分

Posted

技术标签:

【中文标题】如何使用 RxDatasource 在 UICollectionView 中创建具有多个标题的多个部分【英文标题】:How to create multiple Sections in UICollectionView with multiple Headers using RxDatasource 【发布时间】:2019-10-10 19:05:53 【问题描述】:

在任何人复制之前,我在 SO 和其他网站上都有所有 RxDatasource 标签的场景。但是没有人为我工作。

所以我的问题与this 完全相关,我也关注了我的案例。但我也不知道这里发生了什么。它已经挣扎了两个星期。我也检查了 gitHub 代码示例,但无法理解。 我在 MVVM 架构模式上使用RxSwiftRealm 创建了一个应用程序,一切正常,但现在我需要使用UICollectioView 在我的视图中创建两个部分,因为我读到了@ 987654325@ 并尝试应用它,但我根本没有得到它实际上在做什么。我尝试创建其他学习项目,但那些也没有奏效。我仍然尝试编写此代码,但它给了我错误。

我所做的是来自上面提供的链接在下面的代码中。我也不知道如何在拆分一个数组后为我的数据源提供数据或列表。下面是我的整个代码。

我不知道这个块在做什么。

//Changed
struct SectionViewModel 
    var header: String!
    var items: [StudentModel]


extension SectionViewModel: SectionModelType 
    typealias Item  = StudentModel
    init(original: SectionViewModel, items: [StudentModel]) 
        self = original
        self.items = items
    

那么我的 CollectionView 类就像

class StudentCV: UIViewController, UICollectionViewDelegateFlowLayout 

    //MARK: - Outlets

    @IBOutlet weak var studentsView: UICollectionView!

    let studentCells = BehaviorRelay<[StudentModel]>(value: [])
    var notificationToken: NotificationToken? = nil

    private let disposeBag = DisposeBag()

    override func viewDidLoad() 
        super.viewDidLoad()

        let flowLayout = UICollectionViewFlowLayout()
        let size = CGSize(width: 105, height: 135)
        flowLayout.itemSize = size
        studentsView.setCollectionViewLayout(flowLayout, animated: true)
        studentsView.rx.setDelegate(self).disposed(by: disposeBag)

        setupBinding()
    

    func studentLeft(value: Int, id: Int) 
        SignalRService.sharedClass.chatHub.invoke(method: "StudentLeft", withArgs: [id, value]) (result, error) in
            if let e = error 
                print("Error: \(e)")
             else 
                print("Done!")
                let vale = Database.singleton.updatePickupStatus(studentId: id, pickupValue: value)
                TestDebug.debugInfo(fileName: "", message: "STUDENT LEFTTT:: \(vale)")
                if let r = result 
                    print("Result: \(r)")
                
            
        
    

    deinit 
        notificationToken?.invalidate()
    
    func setupBinding() 
        studentsView.register(UINib(nibName: "StudentCVCell", bundle: nil), forCellWithReuseIdentifier: "studentCV")

        //Cell creation Changed here..............................

           dataSource.configureCell =  (ds, cv, ip, item) in
            let cell = cv.dequeueReusableCell(withReuseIdentifier: "studentCV", for: ip) as! StudentCVCell
            cell.viewModel = item
            return cell
        


            studentCells
                .asObservable()
                .debug("STudent View: ")
                .map( SectionViewModel(header: "Pickups Arrived", items: $0 ) )
                .bind(to: studentsView.rx.items(dataSource: dataSource)) // now here it is giving me this error (Instance method 'items(dataSource:)' requires the types '[SectionViewModel]' and 'SectionViewModel' be equivalent)
                .disposed(by: disposeBag)


        // item selection with model details.
        Observable
        .zip(
            studentsView
            .rx
            .itemSelected,
            studentsView
            .rx
            .modelSelected(StudentModel.self))
            .bind  [weak self] indexPath, model in

                let cell = self?.studentsView.cellForItem(at: indexPath) as? StudentCVCell
                if (model.pickupStatus == 2) 
                    // updating view accordingly
                

        .disposed(by: disposeBag)
    

ViewModel 看起来像这样。从我居住的地方。

class StudentCollectionViewViewModel 


    //MARK: Outlets
    let disposeBag = DisposeBag()
    var notificationToken : NotificationToken? = nil
    let studentCells = BehaviorRelay<[StudentModel]>(value: [])

    var studentCell : Observable<[StudentModel]> 
        return studentCells.asObservable()
    


    deinit 
        notificationToken?.invalidate()
    

    func getStudentsData(id: Int) 

        let studentsData = Database.singleton.fetchStudents(byCLassId: id)
        self.notificationToken = studentsData.observe[weak self] change in
            TestDebug.debugInfo(fileName: "", message: "Switch:::: change")
            switch change 
            case .initial(let initial):
                TestDebug.debugInfo(fileName: "", message: "INIT: \(initial)")
                self!.studentCells.accept(Array(studentsData))
            case .update(_, let deletions, let insertions, let modifications):
                TestDebug.debugInfo(fileName: "", message: "MODIF::: \(modifications)")
                self!.studentCells.accept(Array(studentsData))
            case .error(let error):
                print(error)
            
        


    


我正在从数据库中填充数据,但我需要创建两个列表,我也不知道必须发送两个数据列表来填充的位置。另外,当我尝试在我的代码中使用它来查看事情是如何工作的但它给出了我的以下错误。 实例方法“items(dataSource:)”要求类型“[SectionModel]”和“[StudentModel]”是等价的。 任何建议或帮助将不胜感激。提前致谢

【问题讨论】:

【参考方案1】:

RxCollectionViewSectionedReloadDataSource&lt;SectionModel&gt; 预计您将绑定 SectionModel 类型的项目,因为您将 SectionModel 作为通用参数传递。显然,您想使用StudentModel。要实现这一点,您可以使您的StudentModel 符合SectionModelType 协议,然后使用RxCollectionViewSectionedReloadDataSource&lt;StudentModel&gt;

extension StudentModel: SectionModelType 
    // implement


let dataSource = RxCollectionViewSectionedReloadDataSource<StudentModel>(configureCell:  (datasource, collectionView, indexPath, element) in
    // configure a cell     
)
studentCells.bind(to: studentsView.rx.items(dataSource: dataSource))
    .disposed(by: disposeBag) // don't forget to setup disposal

但我认为StudentModel 描述的是单个单元格,而不是整个部分。如果是这种情况,最好将StudentModel 映射到SectionModel,如下所示:

let dataSource = RxCollectionViewSectionedReloadDataSource<SectionModel>(configureCell:  (datasource, collectionView, indexPath, element) in
    // configure a cell      
)
studentCells
    .map  [SectionModel(model: "", items: $0)] 
    .bind(to: studentsView.rx.items(dataSource: dataSource))
    .disposed(by: disposeBag)

显然,我将您所有的 studentCells 映射到一个部分,这可能不是您的情况。在更复杂的场景中,您可以考虑实现符合SectionModelType 的自定义类型。

此外,您可以传递比空字符串更有价值的内容作为 model,但这同样取决于您的需要。

注意! 在上面的示例中,SectionModel 代表 RxDataSources.SectionModel,而不是:

enum SectionModel 
    case SectionOne(items: [SectionItem])
    case SectionTwo(items: [SectionItem])

【讨论】:

所以经过长时间的尝试,我没有通过这种方式得到我想要的......所以我试图以其他方式做。我已经更新了代码,看看你是否理解。 您传递了一个 SectionViewModel,而您的数据源需要一个数组。只需添加括号即可创建单个项目数组:.map([SectionViewModel(...)]] 而不是 .map(SectionViewModel(...)) 我也做了同样的事情,但又给了我一个错误。我回家后会和你分享。 我查看了您的SectionViewModel 构造函数,发现它有参数original:SectionViewModelitems:[StudentModel],而在绑定中您调用SectionViewModel.init(header:String, items: [StudentModel])。可能是编译器报错? 通过在 SectionViewModel 周围添加数组对我有用。在我将数据源转换为给我错误的数组之前。非常感谢您的帮助:) 非常感谢

以上是关于如何使用 RxDatasource 在 UICollectionView 中创建具有多个标题的多个部分的主要内容,如果未能解决你的问题,请参考以下文章

如何解决气旋复杂性? - RxDataSource RxSwift - SwiftLint

如何正确地将部分中的项目与 RxDataSource swift 结合起来?

如何在swift中将背景图像设置为colorWithPatternImage

使用集合视图交换卡片

如何在 UI 中添加渐变角度?

如何在swift中仅使用角半径为3条边添加阴影?