在Swift 5协议中使用@objc

3

我在使用 Swift 协议中的 @objc 代码时遇到了问题,想知道是否有解决方法。

目前我的代码如下:

import UIKit

@objc
protocol TrackScreenshot {
    func registerObserver()
    func removeObservers()
}

extension TrackScreenshot where Self: ScreenTracking {
    func registerObserver() {
        NotificationCenter.default.addObserver(self, selector: #selector(trackScreenshot), name: UIApplication.userDidTakeScreenshotNotification, object: nil)
    }
    
    func removeObservers() {
        NotificationCenter.default.removeObserver(self, name: UIApplication.userDidTakeScreenshotNotification, object: nil )
    }
    
    func trackScreenshot() {
        print(screenName.rawValue)
    }
}

我想继承 TrackScreenshot 协议,使得截图可以轻松进行跟踪。 但是,这里有一个问题。 在 #selector 上注册观察者的 registerObserver() 方法要求在 trackScreenshot 方法上添加 @objc,但如果我这样做,Xcode 会在 trackScreenshot() 行上报错并告诉我:@objc 只能用于类的成员、@objc 协议以及类的具体扩展。

有没有一种方法可以解决这个问题呢? 也尝试过:

    NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: nil) { _ in
        print(self.screenName.rawValue)
    }

但是它并没有起作用,观察者无法被移除并留在圈子里,所以在打开新屏幕时会打印出所有先前的屏幕名称。

任何帮助都非常欢迎!提前感谢!


1
只需让您的协议符合 NSObjectProtocol 协议即可解决问题。 - Schottky
默认方法实现,就像您在协议扩展中添加的那些一样,仅在Swift中可用,您将无法从Objective-C代码中使用它们,因此会出现错误/警告。 - Cristik
@objc protocol TrackScreenshot : NSObject { ... } - Ol Sen
2个回答

3
我会使用闭包形式的通知观察,而不是选择器/方法。
protocol TrackScreenshot {
    func registerObserver(handler: (()->Void)?)
    func removeObservers()
}

extension TrackScreenshot where Self: ScreenTracking {
    func registerObserver(handler: (()->Void)?) {
        NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: nil) { (notification) in
            handler?()
        }
    }
    
    func removeObservers() {
        NotificationCenter.default.removeObserver(self, name: UIApplication.userDidTakeScreenshotNotification, object: nil )
    }
}

那么您的使用方式可能是这样的:

self.registerObserver { [weak self] in
    guard let self = self else {
        return
    }
    print("Screen shot")'
}

1
你也可以使用代理来跟踪屏幕截图通知,根据需要随意重构:
public protocol ScreenshotDelegate: AnyObject {
    func screenshotDetected()
}

open class ScreenshotTracker: NSObject {
    
    private weak var delegate: ScreenshotDelegate?
    
    public init(delegate: ScreenshotDelegate) {
        self.delegate = delegate
       
        NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: OperationQueue.main) { notification in
            delegate.screenshotDetected()
            print("Screenshot notification")
        }
    }
}

视图控制器设置:

override func viewDidLoad() {
    super.viewDidLoad()
    let _ = ScreenshotTracker(delegate: self)
}

extension ViewController: ScreenshotDelegate {
    func screenshotDetected() {
        print("screenshot taken!!!")
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接