2016-08-16 3 views
6

Ich habe ein Objective-C-Protokoll, das hauptsächlich von Objective-C-Objekten und einem oder zwei Swift-Objekten verwendet wird.Protokollerweiterung auf einem ObjC-Protokoll

Ich möchte das Protokoll in Swift erweitern und 2 Funktionen hinzufügen. Eine für die Anmeldung und eine weitere für die Benachrichtigung.

Wenn ich hinzufügen, diese

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

func presetLoaded(notification: NSNotification) { 

} 

ich einen Fehler auf dem #selector erhalten, die sagt Argument of '#selector' refers to a method that is not exposed to Objective-C

Wenn ich dann presetLoaded markieren als @objc ich eine Fehlermeldung erhalten, die besagt, @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes

ich auch kann die Protokollerweiterung nicht als @objc markieren

Wenn ich das Objective-C-Protokoll als Swift erstellen Protokoll bekomme ich den gleichen Fehler.

Gibt es einen Weg, dies zu erreichen, der für Objective-C und Swift-Klassen funktioniert, die das Protokoll verwenden?

+0

Wie erweitern Sie das Protokoll und definieren Sie diese Funktionen als _optional_-Funktionen in Objective-C und implementieren sie in Swift? Nur ein Gedanke. – skim

+0

Meine Idee war, die Registerfunktion in der Erweiterung zu implementieren und dann die Benachrichtigungshandlerfunktion in den Klassen objective-c und swift zu überschreiben. Ich muss Parameter in den Ziel-C-Klassen setzen und sie in Swift implementieren funktioniert nicht. – jarryd

+0

Ich bin mir nicht sicher, ob ich dich richtig verstehe. Es klingt wie das, was Sie tun möchten, ist zu erweitern (fügen Sie Funktionen zum vorhandenen Protokoll hinzu) und eine Standardimplementierung in ObjC zur Verfügung stellen und einen Teil dieser Implementierung in Swift überschreiben. Ist das richtig? – skim

Antwort

3

der Tat kann man nicht wirklich markieren eine Funktion einer Protokollerweiterung als @objc (oder dynamische, die von der Art und Weise entspricht). Nur Methoden einer Klasse dürfen von Objective-C-Laufzeit ausgelöst werden.

In Ihrem speziellen Fall, wenn Sie wirklich durch die Protokollerweiterung machen wollen, kann ich die folgende Lösung vorschlagen (vorausgesetzt, Ihr ursprüngliches Protokoll heißt ObjcProtocol).

Lassen Sie uns einen Wrapper für unsere Benachrichtigungs-Handler machen:

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 
    } 
} 

Jetzt müssen wir unsere ObjcProtocol erweitern erforderlich einzuführen Logik

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) 
    } 
} 

Ich weiß, das nicht so elegant aussehen könnte, so dass ich würde wirklich in Betracht ziehen, einen Kernansatz zu ändern.

Eine Anmerkung: Sie tun möchten Ihre Protokollerweiterung

extension ObjcProtocol where Self: SomeProtocolOrClass 
0

ich einen Weg, es @objc alle zusammen vermeiden :) Nur zu tun gefunden beschränken: D

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

extension KeyboardObservable where Self: UITableView { 

    public func registerForKeyboardEvents() { 
     NotificationCenter.default.addObserver(forName: .UIKeyboardDidShow, object: nil, queue: nil) { notification in 
      self.keyboardDidShow(notification) 
     } 
     NotificationCenter.default.addObserver(forName: .UIKeyboardWillHide, object: nil, queue: nil) { notification in 
      self.keyboardWillHide(notification) 
     } 
    } 

    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) 
    } 

} 

Beispiel

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") 
    } 

    deinit { 
    unregisterForKeyboardEvents() 
    } 
} 
Verwandte Themen