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

