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

为每个TextField配置独立数据源的UIPickerView(Swift/Firebase)

修复PickerView与Firebase提交问题的完整方案

我帮你排查了代码里的几个关键问题,现在给你修复后的完整实现,应该能解决Picker不工作和Firebase提交的问题:

关键问题分析

  1. 代理方法签名错误:你写的titleForRow和didSelectRow方法不符合Swift的UIPickerView代理方法规范,系统根本没法调用这些方法,这是Picker不工作的核心原因。
  2. Picker刷新逻辑无效:你的updatePicker方法新建了一个PickerView实例,完全没用到当前绑定给TextField的那个,自然起不到刷新作用。
  3. 数据源判断不够可靠:依赖isFirstResponder判断当前要显示的数据源,有时候会因为响应状态延迟导致判断错误。
  4. Firebase提交代码冗余:嵌套了太多层if let,可读性差还容易因为某个字段为空直接终止提交,也不好排查问题。

修复后的完整代码

import UIKit
import Firebase
import FirebaseDatabase

class CreatePostViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate {

    @IBOutlet weak var gameTextField: UITextField!
    @IBOutlet weak var activityTextField: UITextField!
    @IBOutlet weak var consoleTextField: UITextField!
    @IBOutlet weak var skillTextField: UITextField!
    @IBOutlet weak var communicationTextField: UITextField!
    @IBOutlet weak var lfglfmTextField: UITextField!
    @IBOutlet weak var rulesTextView: UITextView!
    @IBOutlet weak var descriptionTextView: UITextView!

    // 数据源数组
    let games = ["Assassin's Creed Origins", "Battlefield 1", "Call of Duty: Advanced Warfare", "Call of Duty: Black Ops III", "Call of Duty: Ghosts", "Call of Duty: Infinite Warfare", "Call of Duty: Modern Warfare Remastered", "Call of Duty: WWII", "Destiny", "Destiny 2", "Fifa 16", "Fifa 17", "Fifa 18", "Rocket League"]
    let consoles = ["Xbox One", "Xbox 360", "Playstation 4", "Playstation 3"]
    let skills = ["Achiever", "Explorer", "Killer", "Socializer"]
    let communicationOptions = ["Mic", "No Mic"]
    let lfglfmOptions = ["LFG", "LFM"]
    
    // 共用的PickerView实例
    let pickerView = UIPickerView()
    // 记录当前正在编辑的TextField,用于判断数据源
    private var currentEditingTextField: UITextField?

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 移除TextView的边距
        rulesTextView.textContainerInset = .zero
        rulesTextView.contentInset = UIEdgeInsets(top: 0, left: -5, bottom: 0, right: 0)
        
        // 点击屏幕收起键盘
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(UIView.endEditing(_:))))
        
        // 配置PickerView
        pickerView.delegate = self
        pickerView.dataSource = self
        
        // 给需要Picker的TextField绑定输入视图,并设置代理
        let pickerTextFields: [UITextField] = [gameTextField, consoleTextField, skillTextField, communicationTextField, lfglfmTextField]
        pickerTextFields.forEach { textField in
            textField.inputView = pickerView
            textField.delegate = self
        }
    }

    // MARK: - UITextFieldDelegate
    func textFieldDidBeginEditing(_ textField: UITextField) {
        currentEditingTextField = textField
        // 切换TextField时立即刷新Picker
        pickerView.reloadAllComponents()
    }

    // MARK: - UIPickerViewDataSource & Delegate
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        guard let textField = currentEditingTextField else { return 0 }
        
        switch textField {
        case gameTextField:
            return games.count
        case consoleTextField:
            return consoles.count
        case skillTextField:
            return skills.count
        case communicationTextField:
            return communicationOptions.count
        case lfglfmTextField:
            return lfglfmOptions.count
        default:
            return 0
        }
    }

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        guard let textField = currentEditingTextField else { return nil }
        
        switch textField {
        case gameTextField:
            return games[row]
        case consoleTextField:
            return consoles[row]
        case skillTextField:
            return skills[row]
        case communicationTextField:
            return communicationOptions[row]
        case lfglfmTextField:
            return lfglfmOptions[row]
        default:
            return nil
        }
    }

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        guard let textField = currentEditingTextField else { return }
        
        switch textField {
        case gameTextField:
            textField.text = games[row]
        case consoleTextField:
            textField.text = consoles[row]
        case skillTextField:
            textField.text = skills[row]
        case communicationTextField:
            textField.text = communicationOptions[row]
        case lfglfmTextField:
            textField.text = lfglfmOptions[row]
        default:
            break
        }
        // 选择后收起Picker
        textField.resignFirstResponder()
    }

    // MARK: - 提交Post到Firebase
    @IBAction func createPostTapped(_ sender: UIButton) {
        guard let uid = Auth.auth().currentUser?.uid else {
            let alert = UIAlertController(title: "Error", message: "You must be logged in to create a post.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default))
            present(alert, animated: true)
            return
        }
        
        // 异步获取用户名并提交
        Database.database().reference().child("usernames").child(uid).observeSingleEvent(of: .value) { [weak self] snapshot in
            guard let self = self else { return }
            guard let userDict = snapshot.value as? [String: Any], let username = userDict.values.first as? String else {
                let alert = UIAlertController(title: "Error", message: "Could not retrieve your username.", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "OK", style: .default))
                self.present(alert, animated: true)
                return
            }
            
            // 验证所有必填字段
            guard let game = self.gameTextField.text, !game.isEmpty,
                  let activity = self.activityTextField.text, !activity.isEmpty,
                  let console = self.consoleTextField.text, !console.isEmpty,
                  let skill = self.skillTextField.text, !skill.isEmpty,
                  let communication = self.communicationTextField.text, !communication.isEmpty,
                  let lfglfm = self.lfglfmTextField.text, !lfglfm.isEmpty,
                  let description = self.descriptionTextView.text, !description.isEmpty else {
                let alert = UIAlertController(title: "Error", message: "Please fill in all required fields.", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "OK", style: .default))
                self.present(alert, animated: true)
                return
            }
            
            // 构造Post数据
            let postObject: [String: Any] = [
                "uid" : uid,
                "username" : username,
                "game" : game,
                "activity" : activity,
                "console" : console,
                "skill" : skill,
                "communication" : communication,
                "lfglfm" : lfglfm,
                "description" : description
            ]
            
            // 提交到Firebase
            Database.database().reference().child("posts").childByAutoId().setValue(postObject) { error, _ in
                if let error = error {
                    let alert = UIAlertController(title: "Failed", message: "Post could not be created: \(error.localizedDescription)", preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "OK", style: .default))
                    self.present(alert, animated: true)
                } else {
                    let alert = UIAlertController(title: "Success!", message: "Your post was added successfully.", preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
                        guard let vc = self.storyboard?.instantiateViewController(withIdentifier: "LoggedInVC") else { return }
                        self.present(vc, animated: true)
                    }))
                    self.present(alert, animated: true)
                }
            }
        }
    }
}

关键修改点说明

  1. 修复代理方法签名:所有PickerView的代理方法都加上了正确的参数标签(比如pickerView(_ pickerView:...)),确保系统能正确调用。
  2. 用currentEditingTextField跟踪当前编辑框:替代原来的isFirstResponder判断,数据源切换更可靠,切换TextField时自动刷新Picker。
  3. 优化Firebase提交逻辑:
    • 改成异步获取用户名,避免同步读取卡主线程的问题。
    • 用单层guard let验证所有必填字段,可读性更强,还能统一处理空字段的错误提示。
    • 添加了完整的错误处理分支,不管是登录状态、用户名获取还是提交失败,都能给出明确提示。
  4. 优化Picker交互体验:选择选项后自动收起Picker,不用用户额外点击空白区域。

内容的提问来源于stack exchange,提问作者Daniel Jones

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:49:14