Skip to content

Instantly share code, notes, and snippets.

@tarunon
Created December 15, 2016 16:31
Show Gist options
  • Save tarunon/2e0a47456aa77f7d0a5543c77cf3f676 to your computer and use it in GitHub Desktop.
Save tarunon/2e0a47456aa77f7d0a5543c77cf3f676 to your computer and use it in GitHub Desktop.
Type-safe ViewControllerTransition wrapper
import Foundation
enum TransitioningMode {
case present
case dismiss
}
struct TransitioningContext<Presenting: UIViewController, Presented: UIViewController> {
let native: UIViewControllerContextTransitioning
let mode: TransitioningMode
init(_ native: UIViewControllerContextTransitioning, mode: TransitioningMode) {
self.native = native
self.mode = mode
}
var presenting: Presenting {
switch mode {
case .present:
return native.viewController(forKey: .from) as! Presenting
case .dismiss:
return native.viewController(forKey: .to) as! Presenting
}
}
var presented: Presented {
switch mode {
case .present:
return native.viewController(forKey: .to) as! Presented
case .dismiss:
return native.viewController(forKey: .from) as! Presented
}
}
}
protocol Transitioning {
associatedtype Presenting: UIViewController
associatedtype Presented: UIViewController
func present(using context: TransitioningContext<Presenting, Presented>)
func dismiss(using context: TransitioningContext<Presenting, Presented>)
}
protocol AnimatedTransitioning: Transitioning {
var duration: TimeInterval { get }
}
protocol InteractiveTransitioning: Transitioning {
var speed: CGFloat { get }
var curve: UIViewAnimationCurve { get }
}
class AnyAnimatedTransitioning<S: UIViewController, D: UIViewController>: NSObject, UIViewControllerAnimatedTransitioning, AnimatedTransitioning {
typealias Presenting = S
typealias Presented = D
typealias Animation = (TransitioningContext<S, D>) -> ()
let _duration: TimeInterval
let _present: (TransitioningContext<S, D>) -> ()
let _dismiss: (TransitioningContext<S, D>) -> ()
let _mode: TransitioningMode
init(duration: TimeInterval, present: @escaping Animation, dismiss: @escaping Animation, mode: TransitioningMode) {
_duration = duration
_present = present
_dismiss = dismiss
_mode = mode
}
convenience init<T: AnimatedTransitioning>(_ transitionig: T, mode: TransitioningMode) where S == T.Presenting, D == T.Presented {
self.init(
duration: transitionig.duration,
present: transitionig.present,
dismiss: transitionig.dismiss,
mode: mode
)
}
var duration: TimeInterval {
return _duration
}
func present(using context: TransitioningContext<S, D>) {
_present(context)
}
func dismiss(using context: TransitioningContext<S, D>) {
_dismiss(context)
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
switch _mode {
case .present:
present(using: TransitioningContext<S, D>(transitionContext, mode: _mode))
case .dismiss:
dismiss(using: TransitioningContext<S, D>(transitionContext, mode: _mode))
}
}
}
class AnyInteractiveTransitioning<S: UIViewController, D: UIViewController>: NSObject, UIViewControllerInteractiveTransitioning, InteractiveTransitioning {
typealias Presenting = S
typealias Presented = D
typealias Animation = (TransitioningContext<S, D>) -> ()
let _speed: CGFloat
let _curve: UIViewAnimationCurve
let _present: Animation
let _dismiss: Animation
let _mode: TransitioningMode
init(speed: CGFloat, curve: UIViewAnimationCurve, present: @escaping Animation, dismiss: @escaping Animation, mode: TransitioningMode) {
_speed = speed
_curve = curve
_present = present
_dismiss = dismiss
_mode = mode
}
convenience init<T: InteractiveTransitioning>(_ transitionig: T, mode: TransitioningMode) where S == T.Presenting, D == T.Presented {
self.init(
speed: transitionig.speed,
curve: transitionig.curve,
present: transitionig.present,
dismiss: transitionig.dismiss,
mode: mode
)
}
var speed: CGFloat {
return _speed
}
var curve: UIViewAnimationCurve {
return _curve
}
func present(using context: TransitioningContext<S, D>) {
_present(context)
}
func dismiss(using context: TransitioningContext<S, D>) {
_dismiss(context)
}
var completionSpeed: CGFloat {
return speed
}
var completionCurve: UIViewAnimationCurve {
return curve
}
func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
switch _mode {
case .present:
present(using: TransitioningContext<S, D>(transitionContext, mode: _mode))
case .dismiss:
dismiss(using: TransitioningContext<S, D>(transitionContext, mode: _mode))
}
}
}
class AnimatedTransitioningDelegate<T: AnimatedTransitioning>: NSObject, UIViewControllerTransitioningDelegate {
let transitioning: T
init(_ transitioning: T) {
self.transitioning = transitioning
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return AnyAnimatedTransitioning(transitioning, mode: .present)
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return AnyAnimatedTransitioning(transitioning, mode: .dismiss)
}
}
class InteractiveTransitioningDelegate<T: InteractiveTransitioning>: NSObject, UIViewControllerTransitioningDelegate {
let transitioning: T
init(_ transitioning: T) {
self.transitioning = transitioning
}
func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return AnyInteractiveTransitioning(transitioning, mode: .present)
}
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return AnyInteractiveTransitioning(transitioning, mode: .dismiss)
}
}
extension NSObjectProtocol where Self: UIViewController {
func present<D: UIViewController, T: AnimatedTransitioning>(_ viewControllerToPresent: D, animatedTransitioning: T, completion: (() -> Void)?=nil) where Self == T.Presenting, D == T.Presented {
viewControllerToPresent.transitioningDelegate = AnimatedTransitioningDelegate(animatedTransitioning)
present(viewControllerToPresent, animated: true, completion: completion)
}
func present<D: UIViewController, T: InteractiveTransitioning>(_ viewControllerToPresent: D, interactiveTransitioning: T, completion: (() -> Void)?=nil) where Self == T.Presenting, D == T.Presented {
viewControllerToPresent.transitioningDelegate = InteractiveTransitioningDelegate(interactiveTransitioning)
present(viewControllerToPresent, animated: true, completion: completion)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment