You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将UITableViewRowAction展示逻辑从代理移至MVVM架构的ViewModel

用RxSwift + RxDataSource + MVVM实现左滑操作的解耦

这问题我之前做类似邮件类项目时也折腾过,要把左滑操作的逻辑从VC的代理里搬到ViewModel,核心思路就是把操作的配置和事件流转都交给ViewModel管理,VC只负责UI层的绑定和事件传递。下面给你一步步拆解具体实现:

1. 定义带左滑操作配置的Cell ViewModel

首先,给每个列表项的ViewModel添加左滑操作的元数据,让ViewModel能根据业务逻辑决定每个item显示哪些左滑按钮:

import RxSwift

struct MailItemViewModel {
    let id: UUID // 唯一标识,用来区分不同item
    let title: String
    let content: String
    var swipeActions: [SwipeAction] = []
    
    // 左滑操作的配置模型,包含UI信息和操作类型
    struct SwipeAction {
        let title: String
        let backgroundColor: UIColor
        let actionType: ActionType
    }
    
    // 定义操作类型,让ViewModel能区分不同操作
    enum ActionType {
        case delete
        case more
    }
}

2. 在ViewModel中管理数据和操作逻辑

ViewModel要负责:

  • 提供列表数据的Observable
  • 接收VC传递的操作事件,处理业务逻辑(比如删除item、触发更多操作)
  • 根据业务规则配置每个item的左滑操作
class MailViewModel {
    // 对外暴露的列表数据Observable
    let items: Observable<[MailItemViewModel]>
    // 用来接收VC传递的操作事件
    private let actionSubject = PublishSubject<(itemId: UUID, action: MailItemViewModel.ActionType)>()
    // 内部保存当前列表数据,方便更新
    private let itemsSubject = BehaviorSubject<[MailItemViewModel]>(value: [])
    private let disposeBag = DisposeBag()
    
    init() {
        // 初始化模拟数据,实际项目中替换为接口请求或本地数据
        let initialItems = [
            MailItemViewModel(
                id: UUID(),
                title: "团队周会通知",
                content: "本周三下午2点召开团队周会,请准时参加",
                swipeActions: [
                    MailItemViewModel.SwipeAction(title: "删除", backgroundColor: .systemRed, actionType: .delete),
                    MailItemViewModel.SwipeAction(title: "更多", backgroundColor: .systemGray, actionType: .more)
                ]
            ),
            MailItemViewModel(
                id: UUID(),
                title: "项目进度更新",
                content: "项目第一阶段已完成,进入第二阶段开发",
                swipeActions: [
                    MailItemViewModel.SwipeAction(title: "删除", backgroundColor: .systemRed, actionType: .delete)
                ]
            )
        ]
        itemsSubject.onNext(initialItems)
        items = itemsSubject.asObservable()
        
        // 订阅操作事件,处理业务逻辑
        setupActionHandling()
    }
    
    private func setupActionHandling() {
        actionSubject
            .subscribe(onNext: { [weak self] itemId, action in
                guard let self = self else { return }
                switch action {
                case .delete:
                    // 处理删除逻辑:从当前数据中移除对应item
                    var currentItems = try! self.itemsSubject.value()
                    currentItems.removeAll { $0.id == itemId }
                    self.itemsSubject.onNext(currentItems)
                    // 这里还可以添加网络请求,比如通知服务器删除该邮件
                case .more:
                    // 处理更多操作,比如弹出菜单、跳转详情等
                    print("触发更多操作,itemId: \(itemId)")
                }
            })
            .disposed(by: disposeBag)
    }
    
    // 对外暴露发送操作事件的方法,VC可以调用这个方法传递事件
    func sendAction(for itemId: UUID, action: MailItemViewModel.ActionType) {
        actionSubject.onNext((itemId, action))
    }
}

3. 在VC中绑定数据和左滑操作

VC的职责就是把ViewModel的数据绑定到TableView,同时把左滑按钮的点击事件传递给ViewModel。这里需要扩展RxTableViewDelegateProxy来支持editActionsForRowAt代理方法的Rx化配置:

扩展RxTableViewDelegateProxy

import RxSwift
import RxCocoa

extension RxTableViewDelegateProxy {
    func setEditActionsForRowAt(_ handler: @escaping (UITableView, IndexPath) -> [UITableViewRowAction]?) {
        self.methodInvoker.selector = #selector(UITableViewDelegate.tableView(_:editActionsForRowAt:))
        self.methodInvoker.method = { [weak self] tableView, indexPath in
            guard let self = self else { return nil }
            return handler(tableView, indexPath)
        }
    }
}

VC中的绑定逻辑

import RxSwift
import RxCocoa
import RxDataSources

class MailViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    private let viewModel = MailViewModel()
    private let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupTableView()
        bindViewModel()
    }
    
    private func setupTableView() {
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "MailCell")
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedRowHeight = 60
    }
    
    private func bindViewModel() {
        // 配置RxDataSource
        let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, MailItemViewModel>>(
            configureCell: { _, tableView, indexPath, item in
                let cell = tableView.dequeueReusableCell(withIdentifier: "MailCell", for: indexPath)
                cell.textLabel?.text = item.title
                cell.detailTextLabel?.text = item.content
                return cell
            }
        )
        
        // 绑定列表数据到TableView
        viewModel.items
            .map { [SectionModel(model: "", items: $0)] }
            .bind(to: tableView.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)
        
        // 配置左滑操作
        RxTableViewDelegateProxy.proxy(for: tableView).setEditActionsForRowAt { [weak self] tableView, indexPath in
            guard let self = self,
                  let item = dataSource[indexPath] else { return nil }
            
            // 根据ViewModel配置的swipeActions生成UITableViewRowAction
            return item.swipeActions.map { swipeConfig in
                UITableViewRowAction(style: .default, title: swipeConfig.title) { _, _ in
                    // 把点击事件传递给ViewModel
                    self.viewModel.sendAction(for: item.id, action: swipeConfig.actionType)
                }
            }
        }
    }
}

核心思路总结

  • 职责分离:ViewModel负责决定每个item有哪些左滑操作,以及处理操作对应的业务逻辑;VC只负责UI渲染和事件传递。
  • Rx化代理:通过扩展RxTableViewDelegateProxy,把原生的代理方法转化为可配置的闭包,避免VC中充斥代理代码。
  • 事件流转:用PublishSubject把操作事件从VC传递到ViewModel,实现真正的MVVM数据驱动。

这样一来,左滑操作的逻辑就完全从VC的代理方法中迁移到ViewModel里了,后续修改操作逻辑或者新增操作类型,只需要修改ViewModel即可,不用动VC的代码。

内容的提问来源于stack exchange,提问作者Jefferson Setiawan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:11:35