如何使用iOS MVVM处理子视图中的用户交互

iqjalb3h  于 2023-08-08  发布在  iOS
关注(0)|答案(1)|浏览(95)

为了用表视图控制器实现MVVM,通常每个单元格都有一个父视图模型和一堆子视图模型。假设每个单元格都有一个“喜欢”按钮,现在用户点击其中一个“喜欢”按钮。
在搜索堆栈溢出之后,我看到了三种可能的处理流的方法:
1.点击的动作被发送到子视图模型,子视图模型在内部处理类似的动作。
1.点击的动作被发送到子视图模型,子视图模型将意图传递给父视图模型,父视图模型处理类似动作。
1.点击的动作被发送到表视图控制器(使用单元格委托或闭包),然后表视图控制器将点击的动作传递给父视图模型。
我个人更喜欢第二种和第三种方法。让我困惑的是,在第三种方法中,子视图模型只负责输出(表示数据),而不负责输入(处理交互)。相反,父视图模型负责处理子视图的交互。因为通常一个视图模型会为它的视图处理这两个问题。
哪种方法更好?有没有更好的方法来实现同样的目标?
任何建议都将是有益的。

bqucvtff

bqucvtff1#

在这种情况下,我会问自己的第一个问题是,视图控制器中的任何状态是否必须根据单元格中的输入进行更改。如果是这样,那么选项2是最好的选择(假设是RxSwift)。如果你使用委托/闭包而不是Observables,那么选项3可以方便地减少所需的委托数量。
从您的描述中,当用户点击按钮时,视图控制器的状态似乎不需要更新,因此选项1听起来是最好的。
下面是我如何使用我的CLE architecture来实现它。您可以将connect函数视为视图模型。

extension ViewController {
    // The view controller doesn't have much to do. Just fetch the array of
    // likables and show them on the table view.
    func connect(api: API) {
        api.response(.fetchLikables)
            .bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: LikableCell.self)) { _, item, cell in
                cell.connect(api: api, item: item)
            }
            .disposed(by: disposeBag)

        api.error
            .map { $0.localizedDescription }
            .bind(onNext: presentScene(animated: true) { message in
                UIAlertController(title: "Error", message: message, preferredStyle: .alert).scene {
                    $0.connectOK()
                }
            })
            .disposed(by: disposeBag)
    }
}

extension LikableCell {
    // The cell has the interesting behavior. If the thing is liked, then the
    // `likeButton` is selected. When the button is tapped, update the state
    // and send the network request. If the request fails, then reset the state.
    func connect(api: API, item: LikableThing) {
        enum Input {
            case tap
            case updateSucceeded
            case updateFailed
        }
        cycle(
            input: likeButton.rx.tap.map(to: Input.tap),
            initialState: (current: item.isLiked, reset: item.isLiked),
            reduce: { state, input in
                switch input {
                case .tap:
                    state.current.toggle() // toggle the like state right away.
                case .updateSucceeded:
                    state.reset = state.current // if server success, update the reset
                case .updateFailed:
                    state.current = state.reset // if server fail, update the current state.
                }
            },
            reaction: { args in
                args
                    .filter { $0.1 == .tap } // only make network request when user taps
                    .flatMapLatest { state, _ in
                        return api.successResponse(.setLikable(id: item.id, isLiked: !state.current))
                            .map { $0 ? Input.updateSucceeded : Input.updateFailed }
                    }
            }
        )
        .map { $0.current }
        .bind(to: likeButton.rx.isSelected)
        .disposed(by: disposeBag)
    }
}

struct LikableThing: Decodable, Identifiable {
    let id: Identifier<Int, LikableThing>
    let isLiked: Bool
}

extension Endpoint where Response == [LikableThing] {
    static let fetchLikables: Endpoint = Endpoint(
        request: apply(URLRequest(url: baseURL)) { request in
            // configure request
        },
        decoder: JSONDecoder()
    )
}

extension Endpoint where Response == Void {
    static func setLikable(id: LikableThing.ID, isLiked: Bool) -> Endpoint {
        let request = URLRequest(url: baseURL)
        // configure request
        return Endpoint(request: request)
    }
}

字符串

相关问题