美文网首页
列表tableview collectionview如何使用Rx

列表tableview collectionview如何使用Rx

作者: 剁椒鸡蛋zy | 来源:发表于2021-12-09 18:52 被阅读0次

    1. RxSwift

    Rx里面有列表的扩展,支持基本的列表展示
    ● 有下面两种方式

     let items = Observable.just([
                 "First Item",
                 "Second Item",
                 "Third Item"
             ])
    
             items
             .bind(to: tableView.rx.items) { (tableView, row, element) in
                 let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
                 cell.textLabel?.text = "\(element) @ row \(row)"
                 return cell
             }
             .disposed(by: disposeBag)
    
    
     
     items
      .bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: UITableViewCell.self)) { (row, element, cell) in
                    cell.textLabel?.text = "\(element) @ row \(row)"
       }.disposed(by: disposeBag)
    
    
    // 要实现 添加 删除 ,修改
    //  直接修改Observable
    
    

    看上面的方式,只支持简单的列表, 有多个section的情况就不行了

    2. 复杂的列表 使用RxDataSource

    上面说了Rx只能用来展示简单的列表,复杂的列表 以及列表的添加删除动画过程如何实现呢,就是RxDataSource 提供了一些高级的方法实现。

    RxTableViewSectionedReloadDataSource 和RxTableViewSectionedAnimatedDataSource

    比如要实现下图的列表,

    image

    2.1 RxTableViewSectionedReloadDataSource

    要实现上面的列表,要先定义sectionmodel和cellmodel,并且sectionmodel 要实现 SectionModelType 协议

    struct SectionModel {
        var header: String
    
        var numbers: [CellModel]
    
        var updated: Date
    
        init(header: String, numbers: [Item], updated: Date) {
            self.header = header
            self.numbers = numbers
            self.updated = updated
        }
    }
    
    struct CellModel {
        let number: Int
        let date: Date
    }
    
    extension NumberSection: SectionModelType {
        init(original: NumberSection, items: [IntItem]) {
            self = original
            self.numbers = items
        }
        typealias Item = IntItem
        var items: [IntItem] {
            return numbers
        }
    }
    

    二, 要实现RxTableViewSectionedReloadDataSource, 泛型使用刚才定义的sectionmode

    let dataSource = RxTableViewSectionedReloadDataSource<SectionModel>(
                configureCell: { ds, tv, _, i in
                    let cell = tv.dequeueReusableCell(withIdentifier: "Cell") ?? UITableViewCell(style: .default, reuseIdentifier: "Cell")
                    cell.textLabel?.text = "\(i)"
                    return cell
                },
                titleForHeaderInSection: { ds, index in
                    return ds.sectionModels[index].header
                }
            )
    

    三, 创建列表数据 Observal ,把datasour和tableview 绑定起来

    Observal.just([数据源])
    .bind(to: tableView.rx.items(dataSource: dataSource))
    .disposed(by: disposeBag)`
    

    通过上面步骤 一个包含的多个section的列表就展示出来了。

    2.2 RxTableViewSectionedAnimatedDataSource

    如果要实现添加 移动 删除 动态效果,使用这个DataSource,

    1. 首先 sectionmodel 要实现AnimatableSectionModelType协议,
      cellModel 要遵循 IdentifiableType + Equatable 协议
    extension SectionModel
        : AnimatableSectionModelType, Equatable {
            
            typealias Identity = String
    
        var identity: String {
            return header
        }  
        }
    
    extension CellModel
        : IdentifiableType
        , Equatable {
        typealias Identity = Int
    
        var identity: Int {
            return number
        }
    }
    
    
    func == (lhs: CellModel, rhs: CellModel) -> Bool {
        return lhs.number == rhs.number && lhs.date == rhs.date
    }
    
    
    func == (lhs: SectionModel, rhs: SectionModel) -> Bool {
        return lhs.header == rhs.header && lhs.items == rhs.items && lhs.updated == rhs.updated
    }
    
    

    实现这些协议 可以更好的比较不同sectionmodel 和cellmodel 的变化,来实现列表的刷新。

    3. 一个demo 实现了列表的添加删除移动

    image
    
    // redux like editing example
    class EditingExampleViewController: UIViewController {
        
        @IBOutlet private weak var addButton: UIBarButtonItem!
        
        @IBOutlet private weak var tableView: UITableView!
        let disposeBag = DisposeBag()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let dataSource = EditingExampleViewController.dataSource()
    
            let sections: [NumberSection] = [NumberSection(header: "Section 1", numbers: [], updated: Date()),
                                             NumberSection(header: "Section 2", numbers: [], updated: Date()),
                                             NumberSection(header: "Section 3", numbers: [], updated: Date())]
    
            let initialState = SectionedTableViewState(sections: sections)
            let add3ItemsAddStart = Observable.of((), (), ())
            let addCommand = Observable.of(addButton.rx.tap.asObservable(), add3ItemsAddStart)
                .merge()
                .map(TableViewEditingCommand.addRandomItem)
    
            let deleteCommand = tableView.rx.itemDeleted.asObservable()
                .map(TableViewEditingCommand.DeleteItem)
    
            let movedCommand = tableView.rx.itemMoved
                .map(TableViewEditingCommand.MoveItem)
    
            Observable.of(addCommand, deleteCommand, movedCommand)
                .merge()
                .scan(initialState) { (state: SectionedTableViewState, command: TableViewEditingCommand) -> SectionedTableViewState in
                    return state.execute(command: command)
                }
                .startWith(initialState)
                .map {
                    $0.sections
                }
                .share(replay: 1)
                .bind(to: tableView.rx.items(dataSource: dataSource))
                .disposed(by: disposeBag)
        }
        
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            tableView.setEditing(true, animated: true)
        }
    }
    
    extension EditingExampleViewController {
        static func dataSource() -> RxTableViewSectionedAnimatedDataSource<NumberSection> {
            return RxTableViewSectionedAnimatedDataSource(
                animationConfiguration: AnimationConfiguration(insertAnimation: .top,
                                                                       reloadAnimation: .fade,
                                                                       deleteAnimation: .left),
                configureCell: { _, table, idxPath, item in
                    let cell = table.dequeueReusableCell(withIdentifier: "Cell", for: idxPath)
                    cell.textLabel?.text = "\(item)"
                    return cell
                },
                titleForHeaderInSection: { ds, section -> String? in
                    return ds[section].header
                },
                canEditRowAtIndexPath: { _, _ in
                    return true
                },
                canMoveRowAtIndexPath: { _, _ in
                    return true
                }
            )
        }
    }
    
    enum TableViewEditingCommand {
        case AppendItem(item: IntItem, section: Int)
        case MoveItem(sourceIndex: IndexPath, destinationIndex: IndexPath)
        case DeleteItem(IndexPath)
    }
    
    // This is the part
    
    struct SectionedTableViewState {
        fileprivate var sections: [NumberSection]
        
        init(sections: [NumberSection]) {
            self.sections = sections
        }
        
        func execute(command: TableViewEditingCommand) -> SectionedTableViewState {
            switch command {
            case .AppendItem(let appendEvent):
                var sections = self.sections
                let items = sections[appendEvent.section].items + appendEvent.item
                sections[appendEvent.section] = NumberSection(original: sections[appendEvent.section], items: items)
                return SectionedTableViewState(sections: sections)
            case .DeleteItem(let indexPath):
                var sections = self.sections
                var items = sections[indexPath.section].items
                items.remove(at: indexPath.row)
                sections[indexPath.section] = NumberSection(original: sections[indexPath.section], items: items)
                return SectionedTableViewState(sections: sections)
            case .MoveItem(let moveEvent):
                var sections = self.sections
                var sourceItems = sections[moveEvent.sourceIndex.section].items
                var destinationItems = sections[moveEvent.destinationIndex.section].items
                
                if moveEvent.sourceIndex.section == moveEvent.destinationIndex.section {
                    destinationItems.insert(destinationItems.remove(at: moveEvent.sourceIndex.row),
                                            at: moveEvent.destinationIndex.row)
                    let destinationSection = NumberSection(original: sections[moveEvent.destinationIndex.section], items: destinationItems)
                    sections[moveEvent.sourceIndex.section] = destinationSection
                    
                    return SectionedTableViewState(sections: sections)
                } else {
                    let item = sourceItems.remove(at: moveEvent.sourceIndex.row)
                    destinationItems.insert(item, at: moveEvent.destinationIndex.row)
                    let sourceSection = NumberSection(original: sections[moveEvent.sourceIndex.section], items: sourceItems)
                    let destinationSection = NumberSection(original: sections[moveEvent.destinationIndex.section], items: destinationItems)
                    sections[moveEvent.sourceIndex.section] = sourceSection
                    sections[moveEvent.destinationIndex.section] = destinationSection
                    
                    return SectionedTableViewState(sections: sections)
                }
            }
        }
    }
    
    extension TableViewEditingCommand {
        static var nextNumber = 0
        static func addRandomItem() -> TableViewEditingCommand {
            let randSection = Int.random(in: 0...2)
            let number = nextNumber
            defer { nextNumber = nextNumber + 1 }
            let item = IntItem(number: number, date: Date())
            return TableViewEditingCommand.AppendItem(item: item, section: randSection)
        }
    }
    
    func + <T>(lhs: [T], rhs: T) -> [T] {
        var copy = lhs
        copy.append(rhs)
        return copy
    }
    
    

    相关文章

      网友评论

          本文标题:列表tableview collectionview如何使用Rx

          本文链接:https://www.haomeiwen.com/subject/bxaffrtx.html