J'ai un protocole Objective-C qui est principalement utilisé par des objets Objective-C et un ou deux objets Swift.
Je voudrais étendre le protocole en Swift et ajouter 2 fonctions. L'une pour s'inscrire à une notification et une autre pour gérer la notification.
Si j'ajoute ces
func registerForPresetLoadedNotification() {
NSNotificationCenter.defaultCenter().addObserver(self as AnyObject,
selector: #selector(presetLoaded(_:)),
name: kPresetLoadedNotificationName,
object: nil)
}
func presetLoaded(notification: NSNotification) {
}
Je reçois une erreur sur le #selector qui dit Argument of '#selector' refers to a method that is not exposed to Objective-C
Si je marque ensuite presetLoaded comme @objc
Je reçois une erreur qui dit @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes
Je ne peux pas non plus marquer l'extension de protocole comme @objc
Lorsque je crée le protocole Objective-C en tant que protocole Swift, j'obtiens la même erreur.
Y a-t-il un moyen d'y parvenir qui fonctionnera pour Objective-C et Swift classes qui utilisent le protocole?
En effet, vous ne pouvez pas vraiment marquer une fonction d'une extension de protocole comme @ objc (ou dynamic, ce qui est d'ailleurs équivalent). Seules les méthodes d'une classe peuvent être distribuées par le runtime Objective-C.
Dans votre cas particulier, si vous voulez vraiment passer par l'extension de protocole, je peux proposer la solution suivante (en supposant que votre protocole d'origine est nommé ObjcProtocol).
Faisons un wrapper pour notre gestionnaire de notification:
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
}
}
Maintenant, nous devons étendre notre ObjcProtocol pour introduire la logique requise
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)
}
}
Je sais que cela pourrait ne pas sembler si élégant, alors j'envisagerais vraiment de changer une approche fondamentale.
ne note: Vous voudrez peut-être restreindre votre extension de protocole
extension ObjcProtocol where Self: SomeProtocolOrClass
J'ai trouvé un moyen de le faire :) Évitez simplement @objc tous ensemble: 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)
}
}
Exemple
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")
}
}