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

如何结合RxSwift、RxCocoa以类似tableView.rx.items的方式使用SkeletonView

实现思路:自定义同时兼容RxSwift数据源协议和SkeletonTableViewDataSource协议的数据源类,再扩展UITableView的Rx绑定方法,即可实现类似tableView.rx.items的调用形式。

1. 定义Rx兼容的骨架屏数据源类

import RxSwift
import RxCocoa
import SkeletonView
import RxDataSources

class RxSkeletonTableViewDataSource<Section: SectionModelType>: TableViewSectionedDataSource<Section>, SkeletonTableViewDataSource {
    // 骨架屏配置参数,可根据业务场景自定义
    var skeletonSectionCount: Int = 1
    var skeletonRowCountPerSection: Int = 5
    var skeletonCellReuseID: String = ""
    
    // MARK: SkeletonTableViewDataSource 协议实现
    func numSections(in collectionSkeletonView: UITableView) -> Int {
        skeletonSectionCount
    }
    
    func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int {
        skeletonRowCountPerSection
    }
    
    func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
        skeletonCellReuseID
    }
    
    func collectionSkeletonView(_ skeletonView: UITableView, prepareCellForSkeleton cell: UITableViewCell, at indexPath: IndexPath) {
        cell.showAnimatedGradientSkeleton()
    }
}

2. 扩展UITableView的Rx绑定方法

extension Reactive where Base: UITableView {
    func items<S: Sequence, Cell: UITableViewCell, O: ObservableType>(
        skeletonCellID: String,
        skeletonSectionCount: Int = 1,
        skeletonRowCount: Int = 5,
        cellType: Cell.Type = Cell.self
    ) -> (_ source: O) -> (_ configureCell: @escaping (Int, S.Element, Cell) -> Void) -> Disposable
    where O.Element == S {
        return { source in
            return { configureCell in
                let dataSource = RxSkeletonTableViewDataSource<SectionModel<String, S.Element>>()
                dataSource.skeletonCellReuseID = skeletonCellID
                dataSource.skeletonSectionCount = skeletonSectionCount
                dataSource.skeletonRowCountPerSection = skeletonRowCount
                
                dataSource.configureCell = { _, tableView, indexPath, item in
                    let cell = tableView.dequeueReusableCell(withIdentifier: skeletonCellID, for: indexPath) as! Cell
                    // 数据加载完成后隐藏骨架屏
                    cell.hideSkeleton()
                    // 执行外部传入的cell配置逻辑
                    configureCell(indexPath.row, item, cell)
                    return cell
                }
                return self.items(dataSource: dataSource)(source.map { [SectionModel(model: "", items: Array($0))] })
            }
        }
    }
}

3. 业务使用示例

// 提前注册cell
tableView.register(MyBusinessCell.self, forCellReuseIdentifier: "MyBusinessCellID")
// 页面加载后先展示骨架屏
tableView.showAnimatedSkeleton()

// 你的业务数据序列(比如网络请求返回的列表数据)
let businessDataList: Observable<[MyBusinessItem]> = ...

// 绑定数据,调用方式和原生rx.items完全一致
businessDataList
    .bind(to: tableView.rx.items(
        skeletonCellID: "MyBusinessCellID",
        skeletonRowCount: 6,
        cellType: MyBusinessCell.self
    )) { row, item, cell in
        // 正常写你的业务cell配置逻辑
        cell.titleLabel.text = item.title
        cell.avatarImageView.kf.setImage(with: URL(string: item.avatarUrl))
    }
    .disposed(by: disposeBag)

// 数据返回后隐藏骨架屏
businessDataList
    .subscribe(onNext: { [weak self] _ in
        self?.tableView.hideSkeleton(reloadDataAfter: true)
    })
    .disposed(by: disposeBag)

注意事项

  • 骨架屏用的cell复用ID需要和正常业务cell的复用ID保持一致,保证骨架样式和实际业务样式匹配
  • 多section场景可调整skeletonSectionCount参数,同时修改数据源类的section相关配置即可
  • 如果需要自定义骨架动画效果,直接修改prepareCellForSkeleton方法内的骨架调用参数即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 13:48:04