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

