gpt4 book ai didi

ios - 如何使用 MVVM 和 RxSwift 编辑/删除 UICollectionView 单元格

转载 作者:搜寻专家 更新时间:2023-11-01 06:00:26 24 4
gpt4 key购买 nike

我试图了解如何使用对象列表和 UICollectionView 实现 MVVM。我不明白如何实现用户迭代 -> 模型流程。

我已经设置了一个 test application ,Model 只是一个带有 Int 的类,而 View 是一个 UICollectionViewCell,它显示具有相应 Int 值的文本,并具有加号、减号和删除按钮,分别用于增加、减少和删除元素。每个条目看起来像:我想知道使用 MVVM 和 RxSwift 更新/删除单元格的最佳方式。

我有一个随机生成的 Int 值列表

let items: [Model]

只有 Int 值的模型

class Model {
var number: Int

init(_ n: Int = 0) {
self.number = n
}
}

ViewModel 类只包含模型并有一个 Observable

class ViewModel {

var value: Observable<Model>

init(_ model: Model) {
self.value = Observable.just(model)
}
}

和细胞

class Cell : UICollectionViewCell {
class var identifier: String { return "\(self)" }

var bag = DisposeBag()

let label: UILabel
let plus: UIButton
let minus: UIButton
let delete: UIButton
....
var viewModel: ViewModel? = nil {
didSet {
....
viewModel.value
.map({ "number is \($0.number)" })
.asDriver(onErrorJustReturn: "")
.drive(self.label.rx.text)
.disposed(by: self.bag)
....
}
}
}

我不清楚如何做的是如何将按钮连接到相应的操作,然后更新模型和 View 。

Cell 的 ViewModel 是否对此负责?它应该是接收点击事件、更新模型然后更新 View 的那个吗?

在删除的情况下,单元格的删除按钮需要从数据列表中删除当前模型。如果不将所有内容混合在一起,如何做到这一点?

最佳答案

这是 GitHub 中具有以下更新的项目:https://github.com/dtartaglia/RxCollectionViewTester

我们做的第一件事是概述我们所有的输入和输出。输出应该是 View 模型结构的成员,输入应该是输入结构的成员。

在这种情况下,我们有来自单元格的三个输入:

struct CellInput {
let plus: Observable<Void>
let minus: Observable<Void>
let delete: Observable<Void>
}

单元格本身(标签)的一个输出和单元格父级(大概是 View Controller 的 View 模型)的两个输出。

struct CellViewModel {
let label: Observable<String>
let value: Observable<Int>
let delete: Observable<Void>
}

我们还需要设置单元格以接受工厂函数,以便它可以创建 View 模型实例。细胞还需要能够 self 重置:

class Cell : UICollectionViewCell {

var bag = DisposeBag()

var label: UILabel!
var plus: UIButton!
var minus: UIButton!
var delete: UIButton!

// code to configure UIProperties omitted.

override func prepareForReuse() {
super.prepareForReuse()
bag = DisposeBag() // this resets the cell's bindings
}

func configure(with factory: @escaping (CellInput) -> CellViewModel) {
// create the input object
let input = CellInput(
plus: plus.rx.tap.asObservable(),
minus: minus.rx.tap.asObservable(),
delete: delete.rx.tap.asObservable()
)
// create the view model from the factory
let viewModel = factory(input)
// bind the view model's label property to the label
viewModel.label
.bind(to: label.rx.text)
.disposed(by: bag)
}
}

现在我们需要构建 View 模型的初始化方法。这是所有实际工作发生的地方。

extension CellViewModel {
init(_ input: CellInput, initialValue: Int) {
let add = input.plus.map { 1 } // plus adds one to the value
let subtract = input.minus.map { -1 } // minus subtracts one

value = Observable.merge(add, subtract)
.scan(initialValue, accumulator: +) // the logic is here

label = value
.startWith(initialValue)
.map { "number is \($0)" } // create the string from the value
delete = input.delete // delete is just a passthrough in this case
}
}

您会注意到 View 模型的 init 方法需要的不仅仅是工厂函数所提供的。额外的信息将由 View Controller 在创建工厂时提供。

View Controller 将在其 viewDidLoad 中包含此内容:

viewModel.counters
.bind(to: collectionView.rx.items(cellIdentifier: "Cell", cellType: Cell.self)) { index, element, cell in
cell.configure(with: { input in
let vm = CellViewModel(input, initialValue: element.value)
// Remember the value property tracks the current value of the counter
vm.value
.map { (id: element.id, value: $0) } // tell the main view model which counter's value this is
.bind(to: values)
.disposed(by: cell.bag)

vm.delete
.map { element.id } // tell the main view model which counter should be deleted
.bind(to: deletes)
.disposed(by: cell.bag)
return vm // hand the cell view model to the cell
})
}
.disposed(by: bag)

对于上面的例子,我假设:

  • counters类型为 Observable<[(id: UUID, value: Int)]>并且来自 View Controller 的 View 模型。
  • values类型为 PublishSubject<(id: UUID, value: Int)>并输入到 View Controller 的 View 模型中。
  • deletes类型为 PublishSubject<UUID>并输入到 View Controller 的 View 模型中。

View Controller 的 View 模型的构造遵循与单元格相同的模式:

输入:

struct Input {
let value: Observable<(id: UUID, value: Int)>
let add: Observable<Void>
let delete: Observable<UUID>
}

输出:

struct ViewModel {
let counters: Observable<[(id: UUID, value: Int)]>
}

逻辑:

extension ViewModel {
private enum Action {
case add
case value(id: UUID, value: Int)
case delete(id: UUID)
}

init(_ input: Input, initialValues: [(id: UUID, value: Int)]) {
let addAction = input.add.map { Action.add }
let valueAction = input.value.map(Action.value)
let deleteAction = input.delete.map(Action.delete)
counters = Observable.merge(addAction, valueAction, deleteAction)
.scan(into: initialValues) { model, new in
switch new {
case .add:
model.append((id: UUID(), value: 0))
case .value(let id, let value):
if let index = model.index(where: { $0.id == id }) {
model[index].value = value
}
case .delete(let id):
if let index = model.index(where: { $0.id == id }) {
model.remove(at: index)
}
}
}
}
}

关于ios - 如何使用 MVVM 和 RxSwift 编辑/删除 UICollectionView 单元格,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54046919/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com