SwiftUI onReceive 不适用于 UIPasteboard 发布者

3xy*_*n0s 1 uipasteboard swift swiftui combine

我想使用 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
                    })
    }

}
Run Code Online (Sandbox Code Playgroud)

rob*_*off 5

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

相反,您可以UIPasteboard.changedNotification从默认的NotificationCenter. 但是,如果您希望用户从另一个应用程序复制字符串,这仍然不够,因为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()
    }
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

    var body: some View {
        VStack {
            blah blah blah
        }
        .onReceive(UIPasteboard.general.hasStringsPublisher) { hasStrings = $0 }
    }
Run Code Online (Sandbox Code Playgroud)