Как создать несколько разделов в UICollectionView с несколькими заголовками с помощью RxDatasource

Прежде, чем кто-либо сделает его дубликатом, у меня есть все теги RxDatasource на SO и других сайтах. Но у меня никто не работал.

Итак, мой вопрос полностью связан с этим тем, который Я тоже следил за своим случаем. Но также я понятия не имею, что здесь происходит. И это были две недели борьбы. Я также проверил образец кода gitHub, но не смог понять. Я создал приложение с использованием RxSwift и Realm в шаблоне архитектуры MVVM, все работает нормально, но теперь мне нужно создать два раздела в моем представлении, используя UICollectioView, для этого я прочитал о RxdataSource и попытался применить его, но Я вообще не понимал, что он на самом деле делает. Я пробовал создавать другие проекты для обучения, но они тоже не работали. Тем не менее я попытался сделать этот код, но он дал мне ошибку.

То, что я сделал, взято из приведенной выше ссылки, находится в приведенном ниже коде. Я также не знаю, как передать моему источнику данных данные или списки из одного массива после его разделения. ниже весь мой код.

Понятия не имею, что делает этот блок.

//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)
    }

и ViewModels выглядит так. откуда я заселяю.

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]" были эквивалентными. любые советы или помощь будут оценены. заранее спасибо


person Chalenger    schedule 10.10.2019    source источник


Ответы (1)


RxCollectionViewSectionedReloadDataSource<SectionModel> ожидает, что вы привяжете элементы типа SectionModel, потому что вы передали SectionModel как общий параметр. Судя по всему, вы хотели бы использовать StudentModel. Для этого вы можете сделать свой StudentModel совместимым с SectionModelType протоколом, а затем использовать RxCollectionViewSectionedReloadDataSource<StudentModel>:

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])
}
person Mateusz Chechliński    schedule 11.10.2019
comment
так что после долгих и долгих попыток я не получил то, что хотел, таким образом ... поэтому я попытался сделать это как-то иначе. Я обновил код, так что посмотрите, понимаете ли вы это. - person Chalenger; 13.10.2019
comment
Вы передали одну модель SectionViewModel, в то время как ваш источник данных ожидал массив. Просто добавьте скобки, чтобы создать единый массив элементов: .map({[SectionViewModel(...)]}] вместо .map({SectionViewModel(...)}) - person Mateusz Chechliński; 13.10.2019
comment
Я сделал то же самое, но получил еще одну ошибку. Я поделюсь с вами, когда вернусь домой. - person Chalenger; 13.10.2019
comment
Я взглянул на ваш конструктор SectionViewModel и заметил, что у него есть аргументы original:SectionViewModel и items:[StudentModel], а при связывании вы вызываете SectionViewModel.init(header:String, items: [StudentModel]). Может это ошибка компилятора? - person Mateusz Chechliński; 13.10.2019
comment
Добавление массива вокруг SectionViewModel сработало для меня. Раньше я передавал источник данных в массив, который давал мне ошибку. Большое спасибо за помощь :) очень признателен - person Chalenger; 13.10.2019