Created
November 8, 2017 15:30
-
-
Save rnystrom/4f66312601952fdcf62a985c1fdd0567 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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