簡體   English   中英

ObjC 協議上的協議擴展

[英]Protocol extension on an ObjC protocol

我有一個 Objective-C 協議,主要用於 Objective-C 對象和一兩個 Swift 對象。

我想在 Swift 中擴展協議並添加 2 個函數。 一個用於注冊通知,另一個用於處理通知。

如果我添加這些

func registerForPresetLoadedNotification() {
    NSNotificationCenter.defaultCenter().addObserver(self as AnyObject,
                                                     selector: #selector(presetLoaded(_:)),
                                                     name: kPresetLoadedNotificationName,
                                                     object: nil)
}

func presetLoaded(notification: NSNotification) {
    
}

我在 #selector 上收到一個錯誤消息:

'#selector' 的參數是指未公開給 Objective-C 的方法

如果我然后將 presetLoaded 標記為@objc我會收到一個錯誤消息:

@objc 只能與類的成員、@objc 協議和類的具體擴展一起使用

我也不能將協議擴展標記為@objc

當我將 Objective-C 協議創建為 Swift 協議時,我得到了同樣的錯誤。

有沒有辦法實現這一點,適用於使用該協議的 Objective-C 和 Swift 類?

實際上,您不能真正將協議擴展的函數標記為@objc (或dynamic ,順便說一下,這是等效的)。 Objective-C 運行時只允許分派類的方法。

在您的特定情況下,如果您真的想通過協議擴展來實現,我可以提出以下解決方案(假設您的原始協議名為ObjcProtocol )。

讓我們為我們的通知處理程序制作一個包裝器:

final class InternalNotificationHandler {
    private let source: ObjcProtocol

    init(source: ObjcProtocol) {
        // We require source object in case we need access some properties etc.
        self.source = source
    }

    @objc func presetLoaded(notification: NSNotification) {
        // Your notification logic here
    }
}

現在我們需要擴展我們的ObjcProtocol來引入所需的邏輯

import Foundation
import ObjectiveC

internal var NotificationAssociatedObjectHandle: UInt8 = 0

extension ObjcProtocol {
    // This stored variable represent a "singleton" concept
    // But since protocol extension can only have stored properties we save it via Objective-C runtime
    private var notificationHandler: InternalNotificationHandler {
        // Try to an get associated instance of our handler
        guard let associatedObj = objc_getAssociatedObject(self, &NotificationAssociatedObjectHandle)
            as? InternalNotificationHandler else {
            // If we do not have any associated create and store it
            let newAssociatedObj = InternalNotificationHandler(source: self)
            objc_setAssociatedObject(self,
                                     &NotificationAssociatedObjectHandle,
                                     newAssociatedObj,
                                     objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            return newAssociatedObj
        }

        return associatedObj
    }

    func registerForPresetLoadedNotification() {
        NSNotificationCenter.defaultCenter().addObserver(self,
                                                         selector: #selector(notificationHandler.presetLoaded(_:)),
                                                         name: kPresetLoadedNotificationName,
                                                         object: self)
    }

    func unregisterForPresetLoadedNotification() {
        // Clear notification observer and associated objects
        NSNotificationCenter.defaultCenter().removeObserver(self,
                                                            name: kPresetLoadedNotificationName,
                                                            object: self)
        objc_removeAssociatedObjects(self)
    }
}

我知道這可能看起來不那么優雅,所以我真的會考慮改變一個核心方法。

一個注意事項:您可能想要限制您的協議擴展

extension ObjcProtocol where Self: SomeProtocolOrClass

我找到了一種方法:) 一起避免@objc :D

//Adjusts UITableView content height when keyboard show/hide
public protocol KeyboardObservable: NSObjectProtocol {
    func registerForKeyboardEvents()
    func unregisterForKeyboardEvents()
}

extension KeyboardObservable where Self: UITableView {

    public func registerForKeyboardEvents() {
        let defaultCenter = NotificationCenter.default

        var tokenShow: NSObjectProtocol!
        tokenShow = defaultCenter.addObserver(forName: .UIKeyboardDidShow, object: nil, queue: nil) { [weak self] (notification) in
            guard self != nil else {
                defaultCenter.removeObserver(tokenShow)
                return
            }
            self!.keyboardWilShow(notification as NSNotification)
        }

        var tokenHide: NSObjectProtocol!
        tokenHide = defaultCenter.addObserver(forName: .UIKeyboardWillHide, object: nil, queue: nil) { [weak self] (notification) in
            guard self != nil else {
                defaultCenter.removeObserver(tokenHide)
                return
            }
            self!.keyboardWilHide(notification as NSNotification)
        }
    }

    private func keyboardDidShow(_ notification: Notification) {
        let rect = ((notification as NSNotification).userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let height = rect.height
        var insets = UIEdgeInsetsMake(0, 0, height, 0)
        insets.top = contentInset.top
        contentInset = insets
        scrollIndicatorInsets = insets
    }

    private func keyboardWillHide(_ notification: Notification) {
        var insets = UIEdgeInsetsMake(0, 0, 0, 0)
        insets.top = contentInset.top
        UIView.animate(withDuration: 0.3) { 
            self.contentInset = insets
            self.scrollIndicatorInsets = insets
        }
    }

    public func unregisterForKeyboardEvents() {
        NotificationCenter.default.removeObserver(self)
    }

}

示例

class CreateStudentTableView: UITableView, KeyboardObservable {

  init(frame: CGRect, style: UITableViewStyle) {
    super.init(frame: frame, style: style)
    registerForKeyboardEvents()
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM