SwiftUI onReceive 不适用于 UIPasteboard 发布者

Posted

技术标签:

【中文标题】SwiftUI onReceive 不适用于 UIPasteboard 发布者【英文标题】:SwiftUI onReceive don't work with UIPasteboard publisher 【发布时间】:2021-05-07 16:12:28 【问题描述】:

我想通过 onReceive 订阅 SwiftUI 中的 UIPasteboard 更改。 pHasStringsPublisher 不会在剪贴板中的某些内容发生更改时立即更新,我不明白为什么。

import SwiftUI

struct ContentView: View 
    let pasteboard = UIPasteboard.general
    
    @State var pString: String = "pString"
    @State var pHasStrings: Bool = false
    @State var pHasStringsPublisher: Bool = false

    var body: some View 
        VStack
            Spacer()
            Text("b: '\(self.pString)'")
                .font(.headline)
            Text("b: '\(self.pHasStrings.description)'")
                .font(.headline)
            Text("p: '\(self.pHasStringsPublisher.description)'")
                .font(.headline)
            Spacer()
            Button(action: 
                self.pString = self.pasteboard.string ?? "nil"
                self.pHasStrings = self.pasteboard.hasStrings
            , label: 
                Text("read pb")
                    .font(.largeTitle)
            )
            Button(action: 
                self.pasteboard.items = []
            , label: 
                Text("clear pb")
                    .font(.largeTitle)
            )
            Button(action: 
                self.pasteboard.string = Date().description
            , label: 
                Text("set pb")
                    .font(.largeTitle)
            )
            
        
        .onReceive(self.pasteboard
                    .publisher(for: \.hasStrings)
                    .print()
                    .receive(on: RunLoop.main)
                    .eraseToAnyPublisher()
                   , perform:
                     hasStrings in
                        print("pasteboard publisher")
                        self.pHasStringsPublisher = hasStrings
                    )
    


【问题讨论】:

您确定 hasStrings 是符合 KVO 的属性吗? publisher(for: KeyPath 方法仅适用于符合 KVO 的属性,因为它在底层使用了 KVO。该属性的文档没有提到它符合 KVO,所以我怀疑它实际上不是。您需要找到另一种方法来观察粘贴板的变化。 感谢您的澄清。我不知道符合 KVO 的属性,但我会记住这一点。 【参考方案1】:

据我所知,UIPasteboard 的所有属性均未记录为支持键值观察 (KVO),因此 publisher(for: \.hasStrings) 可能永远不会发布任何内容。

相反,您可以从默认的NotificationCenter 监听UIPasteboard.changedNotification。但是,如果您希望用户从另一个应用程序中复制字符串,那仍然不够,因为如果在您的应用程序处于后台时更改了其内容,则粘贴板不会发布 changedNotification。所以你需要收听UIApplication.didBecomeActiveNotification

让我们在UIPasteboard 的扩展中将其全部包装起来:

extension UIPasteboard 
    var hasStringsPublisher: AnyPublisher<Bool, Never> 
        return Just(hasStrings)
            .merge(
                with: NotificationCenter.default
                    .publisher(for: UIPasteboard.changedNotification, object: self)
                    .map  _ in self.hasStrings )
            .merge(
                with: NotificationCenter.default
                    .publisher(for: UIApplication.didBecomeActiveNotification, object: nil)
                    .map  _ in self.hasStrings )
            .eraseToAnyPublisher()
    

并像这样使用它:

    var body: some View 
        VStack 
            blah blah blah
        
        .onReceive(UIPasteboard.general.hasStringsPublisher)  hasStrings = $0 
    

【讨论】:

以上是关于SwiftUI onReceive 不适用于 UIPasteboard 发布者的主要内容,如果未能解决你的问题,请参考以下文章

无法添加窗口 - 令牌 null 不适用于广播接收器的 OnReceive 内的应用程序

Swift UI 实时预览画布不适用于 macOS Catalina 和 Xcode 11.0

SwiftUI 视图中的 onReceive 导致无限循环

当 ObservedObject 更改时,在 SwiftUI 视图中未调用 onReceive

如何在 SwiftUI 中将表单滚动到特定的 UI 元素

SwiftUI视图onReceive方法接收“冗余”事件的解决