2015-12-01 3 views
4

Я попытался создать универсальный механизм, который мог бы реагировать на клавиатуру, отображающую и скрывающуюся в iOS. Я придумал простой протокол и его расширение, которое не содержит реальную анимацию, но тянет их от объекта, который принимает этот протокол:Swift NSNotificationCenter observer при сбое расширения класса

import UIKit 

protocol KeyboardAnimatable { 
    func keyboardWillShowAnimation() -> (() -> Void)? 
    func keyboardWillHideAnimation() -> (() -> Void)? 
} 

extension KeyboardAnimatable where Self: UIViewController { 

    func setupKeyboardNotifcationListeners() { 
     NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil) 
     NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil) 
    } 

    func removeKeyboardNotificationListeners() { 
     NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil) 
     NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil) 
    } 

    func keyboardWillShow(notification: NSNotification) { 
     let userInfo = notification.userInfo as! Dictionary<String, AnyObject> 
     let animationDuration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSTimeInterval 
     let animationCurve = userInfo[UIKeyboardAnimationCurveUserInfoKey]!.intValue 
     let curveAnimationOption = UIViewAnimationOptions(rawValue: UInt(animationCurve)) 
     let options: UIViewAnimationOptions = [.BeginFromCurrentState, curveAnimationOption] 

     if let animation = keyboardWillShowAnimation() { 
      UIView.animateWithDuration(animationDuration, delay: 0, options: options, animations: animation, completion: nil) 
     } 
    } 

    func keyboardWillHide(notification: NSNotification) { 
     let userInfo = notification.userInfo as! Dictionary<String, AnyObject> 
     let animationDuration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSTimeInterval 
     let animationCurve = userInfo[UIKeyboardAnimationCurveUserInfoKey]!.intValue 
     let curveAnimationOption = UIViewAnimationOptions(rawValue: UInt(animationCurve)) 
     let options: UIViewAnimationOptions = [.BeginFromCurrentState, curveAnimationOption] 

     if let animation = keyboardWillHideAnimation() { 
      UIView.animateWithDuration(animationDuration, delay: 0, options: options, animations: animation, completion: nil) 
     } 
    } 

} 

Я явно использовать where положение в extension KeyboardAnimatable where Self: UIViewController, чтобы сузить ее, так что я могу использовать self при добавлении наблюдателей. Теперь я могу создать контроллер представления, который принимает этот протокол и, например, меняется ограничение, когда клавиатура появляется:

import UIKit 

class ViewController: UIViewController, KeyboardAnimatable { 

    @IBOutlet weak var constraintYCenter: NSLayoutConstraint! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     setupKeyboardNotifcationListeners() 
    } 

    func keyboardWillShowAnimation() -> (() -> Void)? { 
     return { 
      self.constraintYCenter.constant = 100 
      self.view.layoutIfNeeded() 
     } 
    } 

    func keyboardWillHideAnimation() -> (() -> Void)? { 
     return nil 
    } 

// func keyboardWillShow(notification: NSNotification) { 
//  print("will show") 
// } 
//  
// func keyboardWillHide(notification: NSNotification) { 
//  print("will hide") 
// } 

    deinit { 
     removeKeyboardNotificationListeners() 
    } 
} 

Но когда я запускаю этот код в приложение вылетает с ошибкой при слежении:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: '-[KeyboardAnimatableDemo.ViewController keyboardWillShow:]: 
unrecognized selector sent to instance 0x7fb429f192d0' 

Похоже, на мой взгляд контроллер не использует keyboardWillShow: метод, реализованный в расширении протокола , Когда я раскомментирую эти два метода, реализованных непосредственно в ViewController, они работают (журналы печатаются), но тогда целая концепция с общим клавиатурным аниматором бесполезна.

Я думал, что может произойти что-то странное с self в setupKeyboardNotifcationListeners(), но даже если я его изменю, например. func setupKeyboardNotifcationListeners(vc: UIViewController) и использовать vc вместо self при регистрации наблюдателя ничего не меняется.

Одним из возможных решений может быть создание расширения UIViewController только для подмножества контроллеров, которые принимают этот протокол, например. что-то вроде этого extension UIViewcontroller where Self: KeyboardAnimatable, но этот не компилируется, и я не нашел никакого примера такого определения, поэтому он может быть даже невозможен в Swift.

Кто-нибудь пытался добиться чего-то подобного, и ему это удалось?

+0

Decomment строка с '' keyboardWillShow' и keyboardWillHide' декларацией, как вы пытаетесь вызвать эти функции –

+0

Но я хочу использовать методы, реализованные в расширении, в противном случае я не получу ничего, если я должен реализовать эти методы в каждом UIViewController. – Kamil

+0

Правильно, я пропустил объявление в расширении вашего протокола –

ответ

1

Я решил эту проблему, удалив протокол и представив для этих методов «пустую» реализацию. Теперь Extension для клавиатуры распространяется непосредственно на UIViewController, поэтому он должен быть довольно прост в использовании.

код на сути: https://gist.github.com/AdamSliwakowski/6fa1e920254ce584d203