列表tableview collectionview如何使用Rx

2021-12-09  本文已影响0人  剁椒鸡蛋zy

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
}

上一篇下一篇

猜你喜欢

热点阅读