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

Swift中UIPickerView滚动后消失,唤起时崩溃报数组越界

解决UIPickerView滚动消失+数组越界崩溃问题

看起来你遇到的问题主要来自两个方面:picker因tableView刷新失去焦点消失,以及组件切换时未处理选中行导致数组越界。我帮你梳理下问题点并给出修复后的代码:

问题根源分析

  1. 错误调用TableView数据源方法:你在didSelectRow里直接用self.tableView(tableView, cellForRowAt: index3)获取cell,这是创建新cell的数据源方法,不是获取界面上已存在的cell,会导致界面状态异常,甚至让textField失去焦点,picker直接消失。
  2. 未处理组件切换时的选中行:当你切换第一个组件后,第二个组件的选中行没有重置,若新大洲对应的货币数组长度比之前的短,就会出现row超出数组范围的崩溃。
  3. 可能重复填充数据源:reloadArea()每次调用都会往dolls里加数据,若被多次调用会导致数据源重复,引发未知问题。

修复后的完整代码

class Doll {
    var continent: String
    var dolls: [String]
    
    init(continent: String, dolls: [String]) {
        self.continent = continent
        self.dolls = dolls
    }
}

class addStoreTableViewController: UITableViewController, UIPickerViewDelegate, UIPickerViewDataSource {
    var picker = UIPickerView()
    var dolls = [Doll]() // 必须初始化数据源数组
    var myDoll: String = "" // 假设你有这个属性
    
    func reloadArea() {
        dolls.removeAll() // 先清空数组,避免重复添加数据
        dolls.append(Doll(continent: "Asia", dolls: ["¥(CNY)","$(HKD)","P(MOP)","NT$(TWD)","¥(JPY)","₩(KRW)","฿(THB)","RM(MYR)","$(SGD)"]))
        dolls.append(Doll(continent: "Euro", dolls: ["£(GBP)","€(EUR)","Fr(CHF)","₤(TRY)","kr(SEK)"]))
        dolls.append(Doll(continent: "Amereica", dolls: ["$(USD)","$(CAD)"]))
        dolls.append(Doll(continent: "Austr", dolls: ["$(AUD)"]))
    }
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 2
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if component == 0 {
            return dolls.count
        } else {
            let selectedIndex = pickerView.selectedRow(inComponent: 0)
            // 安全判断:避免选中索引超出数据源范围
            guard selectedIndex < dolls.count else { return 0 }
            return dolls[selectedIndex].dolls.count
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        if component == 0 {
            guard row < dolls.count else { return nil }
            return dolls[row].continent
        } else {
            let selectedIndex = pickerView.selectedRow(inComponent: 0)
            // 双重安全判断:防止数组越界
            guard selectedIndex < dolls.count, row < dolls[selectedIndex].dolls.count else { return nil }
            return dolls[selectedIndex].dolls[row]
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        if component == 0 {
            // 切换大洲时,重置第二个组件的选中行为0,避免越界
            pickerView.selectRow(0, inComponent: 1, animated: true)
            pickerView.reloadComponent(1)
        }
        
        let selectContinent = pickerView.selectedRow(inComponent: 0)
        let selectDoll = pickerView.selectedRow(inComponent: 1)
        
        // 安全校验,防止数组越界
        guard selectContinent < dolls.count, selectDoll < dolls[selectContinent].dolls.count else { return }
        
        let myDoll = dolls[selectContinent].dolls[selectDoll]
        self.myDoll = myDoll
        
        let index3 = IndexPath(row: 3, section: 0)
        // 正确获取界面上的cell,而非创建新cell
        if let cell3 = tableView.cellForRow(at: index3) as? addStoreTableViewCell {
            cell3.textIn.text = myDoll
            // 若必须reload行,添加以下代码保持picker不消失
            // tableView.reloadRows(at: [index3], with: .fade)
            // cell3.textIn.becomeFirstResponder()
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.reloadArea()
        picker.delegate = self
        picker.dataSource = self
        picker.selectRow(0, inComponent: 0, animated: true)
        picker.selectRow(0, inComponent: 1, animated: true)
        picker.reloadAllComponents()
    }
    
    // 别忘了实现tableView的数据源方法,比如cellForRowAt
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "addStoreCell", for: indexPath) as! addStoreTableViewCell
        if indexPath.row == 3 {
            // 将picker设置为textField的输入视图
            cell.textIn.inputView = picker
        }
        return cell
    }
}

关键修复点说明

  • 数据源防重复:reloadArea()里先清空数组再添加数据,避免多次调用导致数据源重复。
  • 安全判断:所有数组访问前都加索引校验,彻底杜绝越界崩溃。
  • 组件选中行重置:切换第一个组件时,强制把第二个组件的选中行设为0,保证新数据源的行索引有效。
  • 正确获取cell:用tableView.cellForRow(at:)获取界面上的cell,避免创建新实例导致的状态异常。
  • Picker保持显示:如果必须reload table row,调用textIn.becomeFirstResponder()让输入框重新获取焦点,picker就不会消失。

内容的提问来源于stack exchange,提问作者Tech HiMi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:08:42