我试图创建一个通用机制,可以对iOS中的键盘显示和隐藏作出反应.我提出了简单的协议及其扩展,它不包含真实的动画,但是从采用这个协议的对象中提取它们:
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)
}
}
}
我在扩展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中实现的这两个方法时,它们可以工作(打印日志),但是使用普通键盘动画制作工具的整个概念是无用的.
我认为在setupKeyboardNotifcationListeners()中可能会发生一些奇怪的事情,但即使我将其更改为例如func setupKeyboardNotifcationListeners(vc:UIViewController)并且在注册观察者时没有任何更改时使用vc而不是self.
一种可能的解决方案也可以是仅为采用该协议的控制器的子集创建UIViewController扩展.类似于这个扩展UIViewcontroller的地方Self:KeyboardAnimatable但是这个没有编译,我没有找到任何这样的定义的例子,所以它甚至可能在Swift中不可能.
有没有人试图实现类似的东西,他成功了?
最佳答案 我通过删除协议并为这些方法引入“空”实现来解决这个问题.现在,键盘的扩展直接扩展了UIViewController,所以它应该很容易使用.
代码依旧:https://gist.github.com/AdamSliwakowski/6fa1e920254ce584d203