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

SwiftUI TextField捕获粘贴文本异常:PasteboardObserver未触发且内容为空

问题描述

我创建了PasteboardObserver类,用于捕获TextField的粘贴文本并修改isCodePasted布尔值,但该类内的所有打印语句均未触发,无法检测粘贴操作;不过在CUINDPasteValidationTextField视图中,onReceive(pasteboardObserver.$pastedCode)的打印语句能执行,但获取到的pastedCode始终为空。请问如何正确捕获粘贴文本并修改isCodePasted的状态?

相关代码

PasteboardObserver 类

import SwiftUI
import UIKit
import Combine

class PasteboardObserver: ObservableObject {
    @Published var pastedCode: String = ""
    @Published var isCodePasted: Bool = false

    private var cancellable: AnyCancellable?

    init() {
        self.cancellable = NotificationCenter.default.publisher(for: UIPasteboard.changedNotification)
            .sink { _ in
                DispatchQueue.main.async {
                    print("Pasteboard changed")
                    if let string = UIPasteboard.general.string {
                        print("Pasted string: \(string)")
                        self.pastedCode = string
                        self.isCodePasted = true
                    } else {
                        print("No string found in pasteboard")
                    }
                }
            }
    }
}

CUINDPasteValidationTextField 视图

struct CUINDPasteValidationTextField: View {
    var body: some View {
        VStack(alignment: .leading) {
            HStack {
                CUINDTextField(text: $text,
                               focused: $focused,
                               placeholder: placeholder,
                               fontColor: style.fontColor
                )
                .onReceive(pasteboardObserver.$pastedCode) { pastedCode in
                    print("foo pasted")
                    print(pastedCode)
                }
            }
        }
    }
}

问题分析与解决方案

核心问题

UIPasteboard.changedNotification仅在剪贴板内容被主动修改时触发(比如用户复制新内容),而用户在TextField内执行粘贴操作时,系统只是将剪贴板内容插入输入框,并不会修改剪贴板本身,因此Observer内的通知监听完全失效。

另外pastedCode为空但onReceive触发,大概率是因为PasteboardObserver实例被重复创建或生命周期管理错误,导致初始化时默认空字符串触发了订阅,但实际未捕获到有效内容。

正确实现方案(推荐)

直接在自定义TextField的底层UITextField中重写粘贴方法,精准捕获输入框的粘贴操作:

1. 修改自定义TextField的UIViewRepresentable实现

假设你的CUINDTextField是基于UIViewRepresentable封装的UITextField,添加粘贴回调:

struct CUINDTextField: UIViewRepresentable {
    @Binding var text: String
    @Binding var focused: Bool
    let placeholder: String
    let fontColor: Color
    // 添加粘贴回调,用于传递粘贴内容
    var onPaste: ((String) -> Void)?

    func makeUIView(context: Context) -> UITextField {
        let textField = UITextField()
        textField.placeholder = placeholder
        textField.textColor = UIColor(fontColor)
        textField.delegate = context.coordinator
        return textField
    }

    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.text = text
        uiView.isFirstResponder = focused
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, UITextFieldDelegate {
        let parent: CUINDTextField

        init(_ parent: CUINDTextField) {
            self.parent = parent
        }

        // 允许粘贴操作
        override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
            return action == #selector(paste(_:)) || super.canPerformAction(action, withSender: sender)
        }

        // 捕获粘贴动作并处理
        override func paste(_ sender: Any?) {
            guard let pasteboardText = UIPasteboard.general.string else {
                super.paste(sender)
                return
            }
            
            // 将粘贴内容插入输入框
            if let textField = parent.uiView {
                if let selectedRange = textField.selectedTextRange {
                    textField.replace(selectedRange, withText: pasteboardText)
                    parent.text = textField.text ?? ""
                } else {
                    parent.text += pasteboardText
                }
            }
            
            // 通知父视图粘贴事件
            parent.onPaste?(pasteboardText)
            super.paste(sender)
        }
    }
}

2. 在视图中使用并更新状态

修正PasteboardObserver的生命周期管理,通过回调更新状态:

struct CUINDPasteValidationTextField: View {
    @State var text: String = ""
    @State var focused: Bool = false
    let placeholder: String
    let style: SomeStyle // 替换为你的实际样式类型
    // 用@StateObject保证Observer实例生命周期稳定
    @StateObject private var pasteboardObserver = PasteboardObserver()

    var body: some View {
        VStack(alignment: .leading) {
            HStack {
                CUINDTextField(text: $text,
                               focused: $focused,
                               placeholder: placeholder,
                               fontColor: style.fontColor,
                               onPaste: { pastedString in
                    // 直接更新Observer状态
                    pasteboardObserver.pastedCode = pastedString
                    pasteboardObserver.isCodePasted = true
                    print("捕获到粘贴内容: \(pastedString)")
                })
                .onReceive(pasteboardObserver.$pastedCode) { pastedCode in
                    print("foo pasted")
                    print(pastedCode)
                }
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Mert Köksal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 08:43:12