Created
December 15, 2016 16:31
-
-
Save tarunon/2e0a47456aa77f7d0a5543c77cf3f676 to your computer and use it in GitHub Desktop.
Type-safe ViewControllerTransition wrapper
This file contains hidden or 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
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