如何从 Swift UI 中的不同应用程序将文本粘贴到文本字段

How to paste text on textfield from different app in Swift UI

我为 6 位数字创建了一个 OTP 字段,我想启用粘贴功能,例如我想将文本从 imessage 复制到我的应用程序的文本字段。我认为这在文本字段中是自动的,但是当我在文本字段中双击时,不会出现要求粘贴文本的对话框。我的剪贴板不是空的,因为我在其他不是 SwiftUI 的文本字段中尝试过它并且它有效。

由于我使用的 Xcode 的限制,我的文本字段被创建为许多解决方法视图。我只能用Xcode 12.3.

PS

如果有人也能帮助我,为什么我的小数键盘上缺少 完成 按钮。

这是我的代码:

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)
    }
}

有粘贴功能,但由于我的文本字段的框架。它超出了范围,无法在屏幕上看到。我调整了框架,现在粘贴出现了。我还修改了强调色以隐藏光标。

  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