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
相关产品推荐
相关产品推荐

