Skip to content

Instantly share code, notes, and snippets.

@rnystrom
Created November 8, 2017 15:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rnystrom/4f66312601952fdcf62a985c1fdd0567 to your computer and use it in GitHub Desktop.
Save rnystrom/4f66312601952fdcf62a985c1fdd0567 to your computer and use it in GitHub Desktop.
//
// KeyboardTransition.swift
// ViewControllerKeyboardAnimated
//
// Created by Ryan Nystrom on 11/7/17.
// Copyright © 2017 Ryan Nystrom. All rights reserved.
//
import UIKit
protocol Keyboardable {
var focusResponder: UIResponder { get }
}
extension UIViewAnimationCurve {
var toOptions: UIViewAnimationOptions {
return UIViewAnimationOptions(rawValue: UInt(rawValue << 16))
}
}
class KeyboardTransition: NSObject, UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning {
private struct TransitionDetails {
let duration: TimeInterval
let curve: UIViewAnimationCurve
}
private weak var controller: (UIViewController & Keyboardable)?
private var transitionDetails: TransitionDetails?
private var transitionContext: UIViewControllerContextTransitioning?
init(controller: UIViewController & Keyboardable) {
self.controller = controller
super.init()
NotificationCenter.default.addObserver(
self,
selector: #selector(onKeyboardWillShow(notification:)),
name: .UIKeyboardWillShow,
object: nil
)
}
// MARK: UIViewControllerTransitioningDelegate
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return nil
}
// MARK: UIViewControllerAnimatedTransitioning
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toVC = transitionContext.viewController(forKey: .to),
let fromVC = transitionContext.viewController(forKey: .from) else {
transitionContext.completeTransition(true)
return
}
self.transitionContext = transitionContext
transitionContext.containerView.addSubview(toVC.view)
var frame = fromVC.view.frame
frame.origin.y = fromVC.view.frame.maxY
toVC.view.frame = frame
controller?.focusResponder.becomeFirstResponder()
didChange()
}
// MARK: Private API
func didChange() {
guard let details = transitionDetails, let context = transitionContext else { return }
guard let toVC = context.viewController(forKey: .to),
let fromVC = context.viewController(forKey: .from) else {
context.completeTransition(true)
return
}
transitionContext?.containerView.addSubview(toVC.view)
var frame = fromVC.view.frame
frame.origin.y = fromVC.view.frame.maxY
toVC.view.frame = frame
UIView.animateKeyframes(withDuration: details.duration, delay: 0, options: [.beginFromCurrentState, UIViewKeyframeAnimationOptions(rawValue: details.curve.toOptions.rawValue)], animations: {
toVC.view.frame = fromVC.view.frame
}) { (_) in
context.completeTransition(true)
}
}
// MARK: Notifications
@objc func onKeyboardWillShow(notification: NSNotification) {
transitionDetails = TransitionDetails(
duration: notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? TimeInterval ?? 0.3,
curve: notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? UIViewAnimationCurve ?? UIViewAnimationCurve.easeInOut
)
NotificationCenter.default.removeObserver(self)
didChange()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment