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

Swift中如何禁止UITextField编辑但保留用户交互功能

解决方案

针对你提出的「禁止UITextField手动编辑但保留点击交互弹出选择器」需求,只需要在你现有自定义PickerTextField类的基础上补充两处重写逻辑,同时修复原有代码的边界问题即可:

  • 重写shouldChangeCharactersIn方法返回false,拦截所有用户手动输入、替换文本的操作
  • 重写canPerformAction方法,禁用剪切、粘贴、选中、全选等系统编辑菜单,避免用户通过长按触发编辑行为
  • 修复原有updateText方法的数组越界判断:原判断条件会导致选中最后一行数据时触发异常,调整为大于等于数组总数时返回

完整修改后代码

import UIKit

typealias PickerTextFieldDisplayNameHandler = ((Any) -> String)
typealias PickerTextFieldItemSelectionHandler = ((Int, Any) -> Void)

class PickerTextField: UITextField {

    private let pickerView = UIPickerView(frame: .zero)
    private var lastSelectedRow: Int?
    
    public var pickerData: [Any] = []
    public var displayNameHandler: PickerTextFieldDisplayNameHandler?
    public var itemSelectionHandler: PickerTextFieldItemSelectionHandler?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.configureView()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.configureView()
    }
    
    // 隐藏光标(原有逻辑保留)
    override func caretRect(for position: UITextPosition) -> CGRect {
        return .zero
    }
    
    // 新增:禁止所有手动编辑输入
    override func shouldChangeCharacters(in range: NSRange, replacementString string: String) -> Bool {
        return false
    }
    
    // 新增:禁用编辑相关的系统菜单
    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        let disableActions = [
            #selector(UIResponderStandardEditActions.cut(_:)),
            #selector(UIResponderStandardEditActions.copy(_:)),
            #selector(UIResponderStandardEditActions.paste(_:)),
            #selector(UIResponderStandardEditActions.select(_:)),
            #selector(UIResponderStandardEditActions.selectAll(_:)),
            #selector(UIResponderStandardEditActions.delete(_:))
        ]
        if disableActions.contains(action) {
            return false
        }
        return super.canPerformAction(action, withSender: sender)
    }
    
    private func configureView() {
        self.pickerView.delegate = self
        self.pickerView.dataSource = self
        self.inputView = pickerView
        
        let toolbar = UIToolbar()
        toolbar.barStyle = .default
        toolbar.sizeToFit()
        
        let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(doneButtonTapped))
        toolbar.setItems([spaceButton, doneButton], animated: false)
        
        self.inputAccessoryView = toolbar
    }
    
    private func updateText() {
        if self.lastSelectedRow == nil {
            self.lastSelectedRow = 0
        }
        // 修复原边界判断错误,避免数组越界
        if self.lastSelectedRow! >= self.pickerData.count || self.pickerData.isEmpty {
            return
        }
        let data = self.pickerData[self.lastSelectedRow!]
        self.text = self.displayNameHandler?(data)
    }
    
    @objc func doneButtonTapped() {
        self.updateText()
        self.resignFirstResponder()
    }
    
}

extension PickerTextField: UIPickerViewDelegate {
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        let data = self.pickerData[row]
        return self.displayNameHandler?(data)
    }
}

extension PickerTextField: UIPickerViewDataSource {
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return self.pickerData.count
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        self.lastSelectedRow = row
        self.updateText()
        let data = self.pickerData[row]
        self.itemSelectionHandler?(row, data)
    }
}

使用示例

你可以在业务代码中直接调用该控件,示例如下:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let pickerField = PickerTextField(frame: CGRect(x: 50, y: 100, width: 200, height: 40))
        pickerField.borderStyle = .roundedRect
        pickerField.pickerData = ["选项1", "选项2", "选项3", "选项4"]
        // 配置展示文本的回调
        pickerField.displayNameHandler = { item in
            return item as? String ?? ""
        }
        // 配置选中后的回调
        pickerField.itemSelectionHandler = { index, item in
            print("选中了第\(index)项:\(item)")
        }
        view.addSubview(pickerField)
    }
}

修改完成后,点击文本框只会弹出UIPickerView选择器,用户无法进行任何手动编辑、粘贴、选中等操作,文本内容仅会跟随选择器的选中结果更新,完全符合你的需求。

内容的提问来源于stack exchange,提问作者Lakith Jayalath

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 02:36:03