Я попытался создать универсальный механизм, который мог бы реагировать на клавиатуру, отображающую и скрывающуюся в 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.
Кто-нибудь пытался добиться чего-то подобного, и ему это удалось?
Decomment строка с '' keyboardWillShow' и keyboardWillHide' декларацией, как вы пытаетесь вызвать эти функции –
Но я хочу использовать методы, реализованные в расширении, в противном случае я не получу ничего, если я должен реализовать эти методы в каждом UIViewController. – Kamil
Правильно, я пропустил объявление в расширении вашего протокола –