2016-03-23 4 views
23

У меня есть расширение протокола, которое оно отлично работало перед быстрым 2.2.
Swift 2.2 #selector в ошибке компилятора расширения протокола

Теперь у меня есть предупреждение, что говорит мне, чтобы использовать новый #selector, но если я добавить его

не метод, объявленный с Objective-C Selector.

Я попытался воспроизвести проблему в этом несколько строк кода, которые можно легко копировать и вставлять также в детскую площадку

protocol Tappable { 
    func addTapGestureRecognizer() 
    func tapGestureDetected(gesture:UITapGestureRecognizer) 
} 

extension Tappable where Self: UIView { 
    func addTapGestureRecognizer() { 
     let gesture = UITapGestureRecognizer(target: self, action:#selector(Tappable.tapGestureDetected(_:))) 
     addGestureRecognizer(gesture) 
    } 
} 

class TapView: UIView, Tappable { 
    func tapGestureDetected(gesture:UITapGestureRecognizer) { 
     print("Tapped") 
    } 
} 

Существует также предложено добавить к этому методу в протоколе @objc, но если я это сделаю, мне также будет предложено добавить его в класс, который его реализует, но как только я добавлю, что класс больше не соответствует протоколу, поскольку, похоже, он не видит реализацию в расширении протокола.
Как я могу реализовать это правильно?

+0

Не могли бы вы опубликовать компилируемый пример? Как объявляется 'panGestureDetected'? – Sulthan

+0

Я собираюсь добавить его завтра, спасибо Sulthan – Andrea

+0

@Andrea Изменить Pannable.panGestureDetected (_ :). введите имя класса, в котором объявлен panGestureDetected, вместо 'Pannable.whatever' просто введите' YourClass.panGestureDetected (_ :) ' –

ответ

20

У меня была аналогичная проблема. вот что я сделал.

  1. Отмечено как протокол @objc.
  2. Помечены любые методы, которые я по умолчанию использовал как необязательные.
  3. Then used Self. в #selector.

    @objc public protocol UpdatableUserInterfaceType { 
        optional func startUpdateUITimer() 
        optional var updateInterval: NSTimeInterval { get } 
        func updateUI(notif: NSTimer) 
    } 
    
    public extension UpdatableUserInterfaceType where Self: ViewController { 
    
        var updateUITimer: NSTimer { 
        return NSTimer.scheduledTimerWithTimeInterval(updateInterval, target: self, selector: #selector(Self.updateUI(_:)), userInfo: nil, repeats: true) 
        } 
    
        func startUpdateUITimer() { 
        print(updateUITimer) 
        } 
    
        var updateInterval: NSTimeInterval { 
        return 60.0 
        } 
    } 
    
+0

Да, это работает! Thanks – Andrea

+0

@Andrea, Cool! Рад, что смог помочь. – someoneAnyone

+0

Я получаю «Использование неразрешенного идентификатора« Я ». Мой протокол не помечен публикой. –

15

Вы можете создать свойство, которое является Selector ... Пример:

protocol Tappable { 
    var selector: Selector { get } 
    func addTapGestureRecognizer() 
} 

extension Tappable where Self: UIView { 
    func addTapGestureRecognizer() { 
     let gesture = UITapGestureRecognizer(target: self, action: selector) 
     addGestureRecognizer(gesture) 
    } 
} 

class TapView: UIView, Tappable { 
    var selector = #selector(TapView.tapGestureDetected(_:)) 

    func tapGestureDetected(gesture:UITapGestureRecognizer) { 
     print("Tapped") 
    } 
} 

Ошибка останавливается, чтобы показать и не более необходимо установить протокол и класс с @objc декоратор.

Это решение не самое элегантное, но выглядит нормально до сих пор.

+0

Да, не так уж и удивительно, но лучше, чем размещение @objc везде, особенно если вы «наследуете» из цепочки других протоколов с их реализациями по умолчанию. Внезапно вы заканчиваете чтобы изменить всю вашу базу кода только из-за этого селектора. –

+0

как вызвать протокол собственной функции как селектор, я делаю не хочу объявлять селектор в расширении класса ... –

6

Этот ответ очень похож на Бруно Hecktheuers, но вместо того, чтобы все, что хочет, чтобы они соответствовали «Tappable» протокол реализации «селектор» переменную, мы решили передать его в качестве параметра функции addTapGestureRecognizer:

protocol Tappable { 
    func addTapGestureRecognizer(selector selector: Selector) 
    func tapGestureDetected(gesture:UITapGestureRecognizer) 
} 

extension Tappable where Self: UIView { 
    func addTapGestureRecognizer(selector selector: Selector) 
     let gesture = UITapGestureRecognizer(target: self, action: selector) 
     addGestureRecognizer(gesture) 
    } 
} 

class TapView: UIView, Tappable {  
    func tapGestureDetected(gesture:UITapGestureRecognizer) { 
     print("Tapped") 
    } 
} 

, а затем просто передать селектор, где он используется:

addTapGestureRecognizer(selector: #selector(self.tapGestureDetected(_:))) 

Таким образом, мы избежать те, реализующие этот протокол, имеющий реализовать переменную селектора и мы также избежать того, чтобы отметить everyon e используя этот протокол с помощью «@objc». Похоже, этот подход менее раздутый.

+0

thanks @Peep nice approach – Andrea

+4

Что делать, если вы хотите предоставить стандартную реализацию 'tapGestureDetected' в самом расширении протокола, а не в конкретном классе, реализующем протокол? – NRitH

3

Вот рабочий пример с использованием Swift 3. Он использует стандартный протокол Swift без необходимости в каких-либо @objc украшениях и частном расширении для определения функции обратного вызова.

protocol PlayButtonPlayable { 

    // be sure to call addPlayButtonRecognizer from viewDidLoad or later in the display cycle 
    func addPlayButtonRecognizer() 
    func handlePlayButton(_ sender: UITapGestureRecognizer) 

} 

fileprivate extension UIViewController { 
    @objc func _handlePlayButton(_ sender: UITapGestureRecognizer) { 
     if let playable = self as? PlayButtonPlayable { 
      playable.handlePlayButton(sender) 
     } 
    } 
} 

fileprivate extension Selector { 
    static let playTapped = 
     #selector(UIViewController._handlePlayButton(_:)) 
} 

extension PlayButtonPlayable where Self: UIViewController { 

    func addPlayButtonRecognizer() { 
     let playButtonRecognizer = UITapGestureRecognizer(target: self, action: .playTapped) 
     playButtonRecognizer.allowedPressTypes = [ NSNumber(value: UIPressType.playPause.rawValue as Int) ] 
     view.addGestureRecognizer(playButtonRecognizer) 
    } 

} 
0

Мне довелось увидеть это в боковой панели, у меня недавно была эта проблема.К сожалению, из-за ограничений времени выполнения Objective-C вы не можете использовать @objc для расширений протокола, я считаю, что этот вопрос был закрыт в начале этого года.

Проблема возникает из-за того, что добавление добавляется после согласования протокола, поэтому нет возможности гарантировать соответствие протокола протоколу. При этом можно использовать, чтобы вызвать метод в качестве селектора из любого подкласса NSObject и соответствует протоколу. Это чаще всего делается с делегацией.

Это означает, что вы можете создать пустой подклассу оболочки, которая соответствует протоколу, и использовать оболочку для вызова ее методов из протокола, определенного в оболочке, любые другие неопределенные методы из протокола могут быть переданы делегату. Существуют и другие аналогичные решения, которые используют частное расширение конкретного класса, такого как UIViewController, и определяют метод, который вызывает метод протокола, но они также привязаны к определенному классу, а не по умолчанию реализации определенного класса, который соответствует протокол.

Подумайте, что вы пытаетесь реализовать стандартную реализацию функции протокола, которая использует другие собственные функции протокола, чтобы определить значение для собственной реализации. гмм!

Протокол:

public protocol CustomViewDelegate { 
    func update() 
    func nonDelegatedMethod() 
} 

Вид:

Используйте делегат, и определить метод обертку для безопасного развернет метод делегата.

class CustomView: UIView { 

    let updateButton: UIButton = { 
     let button = UIButton(frame: CGRect(origin: CGPoint(x: 50, y: 50), size: CGSize(width: 150, height: 50))) 
     button.backgroundColor = UIColor.lightGray 
     button.addTarget(self, action: #selector(doDelegateMethod), for: .touchUpInside) 
     return button 
    }() 

    var delegate:CustomViewDelegate? 

    required init?(coder aDecoder: NSCoder) { 
     fatalError("Pew pew, Aghh!") 
    } 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
     addSubview(updateButton) 
    } 

    @objc func doDelegateMethod() { 
     if delegate != nil { 
      delegate!.update() 
     } else { 
      print("Gottfried: I wanted to be a brain surgeon, but I had a bad habit of dropping things") 
     } 
    } 


    } 

ViewController:

Соответствует в View Controller для делегата отображения вида: и реализовать метод Протокола.

class ViewController: UIViewController, CustomViewDelegate { 

    let customView = CustomView(frame: CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 200, height: 200))) 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     customView.backgroundColor = UIColor.red 
     customView.delegate = self //if delegate is not set, the app will not crash 
     self.view.addSubview(customView) 
    } 

    // Protocol -> UIView Button Action -> View Controller's Method 
    func update() { 
     print("Delegating work from View that Conforms to CustomViewDelegate to View Controller") 
    } 

    //Protocol > View Controller's Required Implementation 
    func nonDelegatedMethod() { 

     //Do something else 

    } 
} 

Обратите внимание, что контроллер представления только должен был соответствовать делегату и не ставил селектор некоторого свойства точки зрения, это разделяет точку зрения (и это протокол) от вида контроллера.

У вас уже есть UIView с именем TapView, который наследуется от UIView и Tappable так что ваша реализация может быть:

Протокол:

protocol TappableViewDelegate { 
    func tapGestureDetected(gesture:UITapGestureRecognizer) 
} 

TappableView:

class TappableView: UIView { 

    var delegate:TappableViewDelegate? 

    required init?(coder aDecoder: NSCoder) { 
     fatalError("Pew pew, Aghh!") 
    } 

    override init(frame: CGRect) { 
     super.init(frame: frame) 

     let gesture = UITapGestureRecognizer(target: self, action: #selector(doDelegateMethod(gesture:))) 
     addGestureRecognizer(gesture) 
    } 

    @objc func doDelegateMethod(gesture:UITapGestureRecognizer) { 
     if delegate != nil { 
      delegate!.tapGestureDetected(gesture: gesture) 
     } else { 
      print("Gottfried: I wanted to be a brain surgeon, but I had a bad habit of dropping things") 
     } 
    } 

} 

ViewController:

class ViewController: UIViewController, TappableViewDelegate { 

    let tapView = TappableView(frame: CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 200, height: 200))) 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     tapView.backgroundColor = UIColor.red 
     tapView.delegate = self 
     self.view.addSubview(tapView) 
    } 

    func tapGestureDetected(gesture: UITapGestureRecognizer) { 
     print("User did tap") 
    } 

} 
Смежные вопросы