Как вставить текст в текстовое поле из другого приложения в пользовательском интерфейсе Swift

#ios #swiftui #textfield #copy-paste #one-time-password

Вопрос:

Я создал поле OTP для 6 цифр, и я хотел бы включить функцию вставки, например, я хочу скопировать текст из imessage в текстовое поле моего приложения. Я думал, что это происходит автоматически в текстовом поле, но когда я дважды нажимаю на текстовое поле, диалоговое окно с просьбой вставить текст не появляется. Мой буфер обмена не был пуст, так как я попробовал его в другом текстовом поле, которое не является SwiftUI, и оно работает.

Мое текстовое поле было создано для множества обходных путей из-за ограничений Xcode, который я использую. Я могу использовать только Xcode 12.3.

пс

Если кто-нибудь также может мне помочь, почему кнопка «Готово» отсутствует на моей десятичной клавиатуре.

Вот мой код:

 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.count <= numberOfFields,
                  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])
    }
    
    func hideKeyboard() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

@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 {
        Background {
            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("", text: $viewModel.otpField) { isEditing in
                        viewModel.isEditing = isEditing
                    }
                    .font(Font.system(size: 50, design: .default))
                    .offset(y: 10)
                    .frame(width: viewModel.isEditing ? 0 : textFieldOriginalWidth, height: textBoxHeight)
                    .textContentType(.oneTimeCode)
                    .foregroundColor(.clear)
                    .background(Color.clear)
                    .keyboardType(.decimalPad)
                } //: ZSTACK
            } //: VSTACK
        } //: Background
        .onTapGesture {
            viewModel.hideKeyboard()
        }

    }
    
    struct Background<Content: View>: View {
        private var content: Content

        init(@ViewBuilder content: @escaping () -> Content) {
            self.content = content()
        }

        var body: some View {
            Color.clear
            .contentShape(Rectangle())
            .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
            .overlay(content)
        }
    }
    
    @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)
    }
}

 

Ответ №1:

Функция вставки есть, но из-за рамки моего текстового поля. это было за пределами досягаемости и не видно на экране. Я отрегулировал рамку, и теперь появляется вставка. Я также изменил цвет акцента, чтобы скрыть курсор.

   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("", text: $viewModel.otpField) { isEditing in
                    viewModel.isEditing = isEditing
                }
                .font(Font.system(size: 50, design: .default))
                .offset(y: 10)
                .frame(width: textFieldOriginalWidth, height: textBoxHeight) // What I changed
                .textContentType(.oneTimeCode)
                .foregroundColor(.clear)
                .background(Color.clear)
                .keyboardType(.decimalPad)
                .accentColor(.clear)
            } //: ZSTACK
        } //: VSTACK