Xcode12.3环境下SwiftUI如何禁用TextField文字高亮且保留粘贴功能
兼容低版本的OTP输入框禁用文本高亮方案
要实现禁用双击/长按文本高亮、保留粘贴能力、适配Xcode12.3和iOS13+的要求,可通过UIKit封装自定义TextField实现,完整修改后代码如下:
import SwiftUI @available(iOS 13.0, *) class OTPViewModel: ObservableObject { var numberOfFields: Int init(numberOfFields: Int = 6) { self.numberOfFields = numberOfFields } @Published var otpField = "" { didSet { guard otpField.last?.isNumber ?? true else { otpField = oldValue return } if otpField.count == numberOfFields { hideKeyboard() } } } @Published var isEditing = false func otp(digit: Int) -> String { guard otpField.count >= digit else { return "" } return String(Array(otpField)[digit - 1]) } private func hideKeyboard() { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) } } // 自定义无选中高亮UITextField @available(iOS 13.0, *) class NoSelectionUITextField: UITextField { override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { // 仅保留粘贴功能,禁用所有会触发选中的操作 if action == #selector(UIResponderStandardEditActions.paste(_:)) { return super.canPerformAction(action, withSender: sender) } return false } override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { // 禁用长按、双击手势,避免触发选中 if gestureRecognizer is UILongPressGestureRecognizer || (gestureRecognizer is UITapGestureRecognizer && (gestureRecognizer as? UITapGestureRecognizer)?.numberOfTapsRequired == 2) { return false } return super.gestureRecognizerShouldBegin(gestureRecognizer) } } // 封装为SwiftUI可用组件 @available(iOS 13.0, *) struct NoSelectionTextField: UIViewRepresentable { @Binding var text: String let onEditingChanged: (Bool) -> Void func makeUIView(context: Context) -> NoSelectionUITextField { let textField = NoSelectionUITextField() textField.delegate = context.coordinator textField.text = text textField.textContentType = .oneTimeCode textField.keyboardType = .decimalPad textField.tintColor = .clear textField.textColor = .clear return textField } func updateUIView(_ uiView: NoSelectionUITextField, context: Context) { uiView.text = text } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UITextFieldDelegate { var parent: NoSelectionTextField init(_ parent: NoSelectionTextField) { self.parent = parent } func textFieldDidChangeSelection(_ textField: UITextField) { parent.text = textField.text ?? "" } func textFieldDidBeginEditing(_ textField: UITextField) { parent.onEditingChanged(true) } func textFieldDidEndEditing(_ textField: UITextField) { parent.onEditingChanged(false) } } } @available(iOS 13.0, *) struct OTPView: View { @ObservedObject var viewModel = OTPViewModel() private let textBoxWidth: CGFloat = 41 private let textBoxHeight = UIScreen.main.bounds.width / 8 private let spaceBetweenLines: CGFloat = 16 private let paddingOfBox: CGFloat = 1 private var textFieldOriginalWidth: CGFloat { (textBoxWidth + CGFloat(18)) * CGFloat(viewModel.numberOfFields) } var body: some View { VStack { ZStack { HStack (spacing: spaceBetweenLines) { ForEach(1 ... viewModel.numberOfFields, id: \.self) { digit in otpText( text: viewModel.otp(digit: digit), isEditing: viewModel.isEditing, beforeCursor: digit - 1 < viewModel.otpField.count, afterCursor: viewModel.otpField.count < digit - 1 ) } } //: HSTACK // 替换为自定义的无选中TextField NoSelectionTextField(text: $viewModel.otpField, onEditingChanged: { isEditing in viewModel.isEditing = isEditing }) .font(Font.system(size: 90, design: .default)) .offset(x: 12, y: 10) .frame(width: textFieldOriginalWidth, height: textBoxHeight) .background(Color.clear) } //: ZSTACK } //: VSTACK } @available(iOS 13.0, *) private func otpText( text: String, isEditing: Bool, beforeCursor: Bool, afterCursor: Bool ) -> some View { return Text(text) .font(Font.custom("GTWalsheim-Regular", size: 34)) .frame(width: textBoxWidth, height: textBoxHeight) .background(VStack{ Spacer() .frame(height: 65) ZStack { Capsule() .frame(width: textBoxWidth, height: 2) .foregroundColor(Color(hex: "#BCBEC0")) Capsule() .frame(width: textBoxWidth, height: 2) .foregroundColor(Color(hex: "#367878")) .offset(x: (beforeCursor ? textBoxWidth : 0) + (afterCursor ? -textBoxWidth : 0)) .animation(.easeInOut, value: [beforeCursor, afterCursor]) .opacity(isEditing ? 1 : 0) } //: ZSTACK .clipped() }) .padding(paddingOfBox) .accentColor(.clear) } }
修改后效果:
- 完全禁用双击、长按触发的文本高亮效果
- 保留粘贴、系统验证码自动填充能力
- 适配iOS13及以上版本,可在Xcode12.3下正常编译运行
内容的提问来源于stack exchange,提问作者Dreiohc
相关产品推荐
相关产品推荐

