Swift iOS如何将plist数据展示到联动UIPickerView及关联Label
双联动UIPickerView加载plist数据实现方案
核心实现逻辑说明
不需要创建两个独立的UIPickerView实例,直接使用同一个UIPickerView设置2个滚动列(component)即可实现联动:
- 第1列:展示所有
Occupancy Code字段值 - 第2列:动态展示当前第1列选中编码对应的所有
Description of Occupancy值 - 选中第1列任意项时,自动刷新第2列数据,默认选中第2列首条内容
- 选择完成后自动匹配关联数据,同步更新两个UILabel的展示内容
具体实现步骤
1. 补全全局属性与控件关联
先在控制器中补充缺失的属性、关联两个展示用Label,替换原有无效的测试数据源:
import UIKit class ViewController: UIViewController,UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate { @IBOutlet weak var occupancyTextBox: UITextField! // 关联两个展示用Label输出口 @IBOutlet weak var resultLabel1: UILabel! @IBOutlet weak var resultLabel2: UILabel! //MARK: Variables var toolBar = UIToolbar() var pickerViewOccupancy = UIPickerView() // plist解析后的全量数据 var arrData = [[String : String]]() // 第一列数据源:所有去重后的Occupancy Code var occupancyCodeList = [String]() // 分组数据:key为Occupancy Code,value为该编码下对应的所有条目数组 var codeGroupedData = [String: [[String: String]]]() // 记录当前选中的索引 var selectedCodeIndex = 0 var selectedDescIndex = 0 override func viewDidLoad() { super.viewDidLoad() // 解析plist数据 loadPlistData() occupancyTextBox.delegate = self createPickerView() setupToolBar() // 默认选中第一组第一条数据,初始化Label展示 updateRelatedDisplay() } }
2. 修正plist数据解析与分组逻辑
原有解析逻辑只存储了两个字段,需要把plist全量字段读取后按Occupancy Code分组,供联动使用:
extension ViewController { func loadPlistData() { guard let url = Bundle.main.url(forResource: "firedata", withExtension: "plist"), let data = try? Data(contentsOf: url), let plist = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [[String : Any]] else { return } // 清空旧数据 arrData.removeAll() occupancyCodeList.removeAll() codeGroupedData.removeAll() for item in plist { // 读取plist中所有需要的字段,字段名和实际plist保持一致即可 var dict = [String: String]() dict["Occupancy Code"] = item["Occupancy Code"] as? String ?? "" dict["Description of Occupancy"] = item["Description of Occupancy"] as? String ?? "" // 补充第三项关联字段、绑定到编码的关联字段,替换为你自己plist里的实际字段名 dict["ThirdRelatedValue"] = item["ThirdRelatedValue"] as? String ?? "" dict["CodeBindValue"] = item["CodeBindValue"] as? String ?? "" arrData.append(dict) // 按Occupancy Code分组 let code = dict["Occupancy Code"]! if codeGroupedData[code] == nil { codeGroupedData[code] = [[String: String]]() occupancyCodeList.append(code) } codeGroupedData[code]?.append(dict) } } }
3. 配置PickerView与工具栏
原有工具栏没有添加操作按钮,这里补全完成按钮逻辑,同时绑定PickerView作为输入框的输入视图:
extension ViewController { func createPickerView() { pickerViewOccupancy.delegate = self pickerViewOccupancy.dataSource = self occupancyTextBox.inputView = pickerViewOccupancy } func setupToolBar() { toolBar.sizeToFit() let doneBtn = UIBarButtonItem(title: "完成", style: .done, target: self, action: #selector(donePickerSelect)) let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) toolBar.items = [flexSpace, doneBtn] toolBar.isUserInteractionEnabled = true occupancyTextBox.inputAccessoryView = toolBar } @objc func donePickerSelect() { // 收起选择器 view.endEditing(true) // 更新输入框文本 let currentCode = occupancyCodeList[selectedCodeIndex] occupancyTextBox.text = "Occupancy: \(currentCode)" // 更新关联Label展示 updateRelatedDisplay() } // 抽离Label更新逻辑,选中项变化时统一调用 func updateRelatedDisplay() { guard occupancyCodeList.count > selectedCodeIndex, let descList = codeGroupedData[occupancyCodeList[selectedCodeIndex]], descList.count > selectedDescIndex else { return } let currentItem = descList[selectedDescIndex] // 第一个Label展示两个选中值对应的第三项关联数据 resultLabel1.text = currentItem["ThirdRelatedValue"] // 第二个Label展示第一个选中项绑定的关联数据 resultLabel2.text = currentItem["CodeBindValue"] } }
4. 实现PickerView代理与数据源的联动逻辑
替换原有单轮的代理实现,完成双列联动逻辑:
//MARK: PickerView Methods extension ViewController { // 返回2列,对应编码、描述两个选择项 func numberOfComponents(in pickerView: UIPickerView) -> Int { return 2 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { if component == 0 { // 第一列返回编码总数 return occupancyCodeList.count } else { // 第二列返回当前选中编码对应的描述总数 let currentCode = occupancyCodeList[selectedCodeIndex] return codeGroupedData[currentCode]?.count ?? 0 } } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { if component == 0 { return occupancyCodeList[row] } else { let currentCode = occupancyCodeList[selectedCodeIndex] return codeGroupedData[currentCode]?[row]["Description of Occupancy"] } } func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { if component == 0 { // 选中第一列编码时,重置描述选中索引为0,刷新第二列 selectedCodeIndex = row selectedDescIndex = 0 pickerView.reloadComponent(1) pickerView.selectRow(0, inComponent: 1, animated: true) } else { // 选中第二列描述时,记录索引 selectedDescIndex = row } // 实时更新展示内容 updateRelatedDisplay() } }
注意事项
- 代码中
ThirdRelatedValue、CodeBindValue两个字段名需要和你自己plist中实际存储的字段名保持一致,直接替换即可 - 如果你的plist中同一个Occupancy Code只对应一条Description,直接在分组后取首项即可,第二列可以设置为只展示1行不可滚动,基础逻辑不需要改动
- 如果需要自定义PickerView每行的样式(比如调整字体、颜色),可以替换
titleForRow代理为viewForRow代理实现自定义视图
内容的提问来源于stack exchange,提问作者Uvesh Imam
相关产品推荐
相关产品推荐

