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

RxSwift实现UITableView单行删除并保留动画效果的方法

解决RxSwift绑定UITableView时单行删除保留动画的问题

默认的tableView.rx.items绑定会在数据源数组更新时直接刷新整个表格——因为它无法识别数组前后的元素差异,自然触发不了单行删除的动画。要实现带动画的单行删除,核心是要做差分更新,最简便可靠的方式是使用RxDataSources库。


步骤1:引入RxDataSources

确保项目已集成RxDataSources(可通过CocoaPods、Swift Package Manager等方式安装)。

步骤2:让数据模型适配差分对比

RxDataSources需要通过唯一标识对比元素差异,所以你的Item模型需要实现IdentifiableType和Equatable协议:

struct Item: IdentifiableType, Equatable {
    // 实现IdentifiableType,提供元素的唯一标识
    var identity: String {
        return self.id // 假设Item有唯一id字段,替换成你的实际唯一属性
    }
    
    // 你的Item原有属性
    let id: String
    // ...其他业务属性
}

// 定义Section模型(即使是单Section列表也需要)
typealias ItemSection = SectionModel<String, Item>

步骤3:修改ViewModel的数据源结构

把原来的单数组BehaviorRelay改成Section数组类型,并更新删除逻辑:

import RxSwift
import RxDataSources

class YourViewModel {
    // 改为Section数组的BehaviorRelay,初始化时默认一个空Section
    let items: BehaviorRelay<[ItemSection]> = BehaviorRelay(value: [SectionModel(model: "", items: [])])

    func removeItem(_ item: Item) {
        var currentSections = items.value
        // 取第一个Section(单Section场景)
        var currentItems = currentSections[0].items
        
        if let removeIndex = currentItems.firstIndex(of: item) {
            currentItems.remove(at: removeIndex)
            // 更新Section的元素列表
            currentSections[0] = SectionModel(model: currentSections[0].model, items: currentItems)
            // 发送更新后的Section数组
            items.accept(currentSections)
        }
    }
}

步骤4:修改ViewController的绑定逻辑

使用RxTableViewSectionedAnimatedDataSource绑定,它会自动计算数组差异并触发对应动画:

import RxSwift
import RxDataSources

class YourViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    let disposeBag = DisposeBag()
    let viewModel = YourViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 1. 创建带动画的数据源
        let dataSource = RxTableViewSectionedAnimatedDataSource<ItemSection>(
            configureCell: { [weak self] _, table, indexPath, item in
                // 复用并配置cell,和你原来的逻辑一致
                let cell = table.dequeueReusableCell(withIdentifier: "YourCellID", for: indexPath) as! YourCell
                cell.setup(with: item)
                return cell
            }
        )
        
        // 2. 自定义删除动画(可选,默认是automatic)
        dataSource.animationConfiguration = AnimationConfiguration(
            deleteAnimation: .right // 和你原来的删除动画类型一致
        )
        
        // 3. 绑定数据源到TableView
        viewModel.items
            .bind(to: tableView.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)
    }
}

可选:不依赖第三方库的手动实现

如果不想引入RxDataSources,也可以手动传递删除索引并执行动画,但这种方式需要自己处理线程安全和索引一致性,容易出错:

// ViewModel修改:新增Subject传递删除的IndexPath
class YourViewModel {
    let items: BehaviorRelay<[Item]> = BehaviorRelay(value: [])
    let deletedIndexPath = PublishSubject<IndexPath>()

    func removeItem(_ item: Item) {
        var loadedItems = items.value
        if let removeIndex = loadedItems.firstIndex(of: item) {
            loadedItems.remove(at: removeIndex)
            items.accept(loadedItems)
            deletedIndexPath.onNext(IndexPath(row: removeIndex, section: 0))
        }
    }
}

// ViewController中订阅删除事件执行动画
viewModel.deletedIndexPath
    .subscribe(onNext: { [weak self] indexPath in
        self?.tableView.deleteRows(at: [indexPath], with: .right)
    })
    .disposed(by: disposeBag)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 14:52:49