2016-08-16 5 views
6

У меня есть Objective-C протокол, который используется в основном объектами-C и одним или двумя объектами Swift.Расширение протокола по протоколу ObjC

Я хотел бы расширить протокол в Swift и добавить 2 функции. Один для регистрации для уведомления, а другой - для обработки уведомления.

Если добавить эти

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

func presetLoaded(notification: NSNotification) { 

} 

Я получаю сообщение об ошибке на #selector, который говорит Argument of '#selector' refers to a method that is not exposed to Objective-C

Если я затем отметьте presetLoaded, как @objc я получаю ошибку, которая говорит @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes

Я также не может отмечать расширение протокола как @objc

Когда я создаю протокол Objective-C как Swift протокол. Я получаю ту же ошибку.

Есть ли способ достичь этого, который будет работать для классов Objective-C и Swift, которые используют протокол?

+0

Как насчет расширения протокола и определения этих функций как функций _optional_ в Objective-C и их реализации в Swift? Просто мысль. – skim

+0

Моя идея состояла в том, чтобы реализовать функцию регистров в расширении, а затем переопределить функцию обработчика уведомлений в классах «цель-с» и «быстрый». Мне нужно установить параметры в объектных классах c, реализуя их в Swift wont work. – jarryd

+0

Я не уверен, что правильно вас понимаю. Похоже, что вы хотите расширить (добавить функции в существующий протокол) и обеспечить реализацию по умолчанию в ObjC и переопределить часть этой реализации в Swift. Это верно? – skim

ответ

3

В самом деле, вы не можете пометить функцию расширения для протокола как @objc (или динамический, что эквивалентно, кстати). Разрешается отправлять только методы класса с помощью среды выполнения 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 
0

Я нашел способ сделать это :) Просто избежать @objc все вместе: 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) 
    } 

} 

Пример

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() 
    } 
} 
Смежные вопросы