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

如何在RxSwift的UIPickerView选中行设置自定义视图?

解决RxSwift结合UIPickerView时选中行自定义视图不生效的问题

我完全懂你现在的困扰——不用RxSwift的时候,你能轻松在viewForRow里判断当前行是否选中,然后切换自定义视图的样式,但换成Rx的自定义适配器后,这套逻辑就失灵了。核心问题在于Rx的适配器默认不会监听选中行的变化,也不会在选中状态改变时主动刷新对应行的视图,下面给你具体的解决思路和修改后的代码:

核心问题拆解

  • 非Rx实现里,每次viewForRow被调用时都会检查当前行是否为选中行,但Rx适配器只有在数据源更新时才会刷新PickerView,选中行变化不会触发viewForRow重新执行。
  • 你的Rx适配器没有订阅PickerView的选中事件,所以无法感知选中状态变化,自然没法更新对应行的样式。

一步步解决

1. 在适配器里添加选中行跟踪变量

先给PickerViewViewAdapter加个变量,用来记录当前选中的行号,这样在创建视图时就能判断是否要应用选中样式:

final class PickerViewViewAdapter: NSObject, UIPickerViewDataSource, UIPickerViewDelegate, RxPickerViewDataSourceType, SectionedViewDataSourceType { 
    typealias Element = [MyMODEL] 
    private var items: [MyMODEL] = [] 
    // 新增:记录当前选中的行
    private var selectedRow: Int = 0
    
    // ... 保留原有方法
}

2. 修改viewForRow,根据选中状态设置样式

优化视图复用逻辑,同时根据selectedRow判断是否要应用选中样式,别忘了添加取消选中的恢复逻辑:

func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { 
    // 优先复用视图,避免频繁创建
    let customView: MyCustomView
    if let reusedView = view as? MyCustomView {
        customView = reusedView
        customView.update(with: items[row]) // 给自定义视图加个更新模型的方法
    } else {
        customView = MyCustomView(frame: CGRect.zero, model: items[row])
    }
    
    // 根据选中状态切换样式
    if row == selectedRow {
        customView.selected()
    } else {
        customView.deselected() // 必须加这个方法,恢复未选中时的默认样式
    }
    return customView 
}

3. 订阅选中事件,刷新对应行

在适配器里添加一个方法,专门处理选中事件的订阅,更新选中行并刷新对应的视图:

func bindSelectedEvent(to pickerView: UIPickerView, disposeBag: DisposeBag) {
    pickerView.rx.itemSelected
        .subscribe(onNext: { [weak self] (component, row) in
            guard let self = self else { return }
            // 先刷新之前选中的行,重置它的样式
            pickerView.reloadRows(at: [IndexPath(row: self.selectedRow, section: 0)], forComponent: component)
            // 更新当前选中行的记录
            self.selectedRow = row
            // 再刷新新选中的行,应用选中样式
            pickerView.reloadRows(at: [IndexPath(row: row, section: 0)], forComponent: component)
        })
        .disposed(by: disposeBag)
}

4. 调整外部使用代码,绑定选中事件

在你初始化适配器的地方,调用刚才的绑定方法,把选中事件和适配器关联起来:

let input = MYVIEWMODEL.Input(param1: key, param2: key2, param3: key3) 
let output = myViewModel.transform(input: input) 

let adapter = PickerViewViewAdapter()
output.limitsAvailable 
    .drive(limitPickerView.rx.items(adapter: adapter)) 
    .disposed(by: disposeBag)

// 绑定选中事件,让适配器能感知选中状态变化
adapter.bindSelectedEvent(to: limitPickerView, disposeBag: disposeBag)

额外提醒

  • 如果你的PickerView初始化时默认选中某一行(比如第0行),记得在适配器初始化时把selectedRow设为对应行号,数据源加载完成后手动刷新该行,确保初始样式正确。
  • 自定义视图的selected()和deselected()方法要确保能正确切换样式,比如文字颜色、背景色、边框等属性的变化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:42:27