Commit 56527435 by Daniel Dahan

initial phase of interactive transitions added

parent b7bcb759
...@@ -95,7 +95,8 @@ fileprivate struct MotionInstance { ...@@ -95,7 +95,8 @@ fileprivate struct MotionInstance {
} }
fileprivate struct MotionInstanceController { fileprivate struct MotionInstanceController {
fileprivate var isEnabled: Bool fileprivate var isMotionEnabled: Bool
fileprivate var isInteractiveMotionEnabled: Bool
fileprivate weak var delegate: MotionDelegate? fileprivate weak var delegate: MotionDelegate?
} }
...@@ -104,7 +105,7 @@ extension UIViewController: MotionDelegate, UIViewControllerTransitioningDelegat ...@@ -104,7 +105,7 @@ extension UIViewController: MotionDelegate, UIViewControllerTransitioningDelegat
fileprivate var motionInstanceController: MotionInstanceController { fileprivate var motionInstanceController: MotionInstanceController {
get { get {
return AssociatedObject(base: self, key: &MotionInstanceControllerKey) { return AssociatedObject(base: self, key: &MotionInstanceControllerKey) {
return MotionInstanceController(isEnabled: false, delegate: nil) return MotionInstanceController(isMotionEnabled: false, isInteractiveMotionEnabled: false, delegate: nil)
} }
} }
set(value) { set(value) {
...@@ -115,18 +116,30 @@ extension UIViewController: MotionDelegate, UIViewControllerTransitioningDelegat ...@@ -115,18 +116,30 @@ extension UIViewController: MotionDelegate, UIViewControllerTransitioningDelegat
/// A boolean that indicates whether motion is enabled. /// A boolean that indicates whether motion is enabled.
open var isMotionEnabled: Bool { open var isMotionEnabled: Bool {
get { get {
return motionInstanceController.isEnabled return motionInstanceController.isMotionEnabled
} }
set(value) { set(value) {
if value { if value {
modalPresentationStyle = .custom prepareMotionDelegation()
transitioningDelegate = self motionInstanceController.isInteractiveMotionEnabled = false
motionDelegate = self
(self as? UINavigationController)?.delegate = self
(self as? UITabBarController)?.delegate = self
} }
motionInstanceController.isEnabled = value motionInstanceController.isMotionEnabled = value
}
}
/// A boolean that indicates whether interactive motion is enabled.
open var isInteractiveMotionEnabled: Bool {
get {
return motionInstanceController.isInteractiveMotionEnabled
}
set(value) {
if value {
prepareMotionDelegation()
motionInstanceController.isMotionEnabled = false
}
motionInstanceController.isInteractiveMotionEnabled = value
} }
} }
...@@ -139,7 +152,9 @@ extension UIViewController: MotionDelegate, UIViewControllerTransitioningDelegat ...@@ -139,7 +152,9 @@ extension UIViewController: MotionDelegate, UIViewControllerTransitioningDelegat
motionInstanceController.delegate = value motionInstanceController.delegate = value
} }
} }
}
extension UIViewController {
/** /**
Determines whether to use a Motion instance for transitions. Determines whether to use a Motion instance for transitions.
- Parameter _ navigationController: A UINavigationController. - Parameter _ navigationController: A UINavigationController.
...@@ -149,7 +164,7 @@ extension UIViewController: MotionDelegate, UIViewControllerTransitioningDelegat ...@@ -149,7 +164,7 @@ extension UIViewController: MotionDelegate, UIViewControllerTransitioningDelegat
- Returns: An optional UIViewControllerAnimatedTransitioning. - Returns: An optional UIViewControllerAnimatedTransitioning.
*/ */
open func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { open func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return fromVC.isMotionEnabled ? Motion(isPresenting: operation == .push, isContainer: true) : nil return fromVC.isMotionEnabled ? Motion(isPresenting: operation == .push) : nil
} }
/** /**
...@@ -160,7 +175,18 @@ extension UIViewController: MotionDelegate, UIViewControllerTransitioningDelegat ...@@ -160,7 +175,18 @@ extension UIViewController: MotionDelegate, UIViewControllerTransitioningDelegat
- Returns: An optional UIViewControllerAnimatedTransitioning. - Returns: An optional UIViewControllerAnimatedTransitioning.
*/ */
open func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { open func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return fromVC.isMotionEnabled ? Motion(isPresenting: true, isContainer: true) : nil return fromVC.isMotionEnabled ? Motion(isPresenting: true) : nil
}
}
extension UIViewController {
/// Prepares the motionDelegate.
fileprivate func prepareMotionDelegation() {
modalPresentationStyle = .custom
transitioningDelegate = self
motionDelegate = self
(self as? UINavigationController)?.delegate = self
(self as? UITabBarController)?.delegate = self
} }
} }
...@@ -173,7 +199,7 @@ extension UIViewController { ...@@ -173,7 +199,7 @@ extension UIViewController {
- Returns: An optional UIViewControllerAnimatedTransitioning. - Returns: An optional UIViewControllerAnimatedTransitioning.
*/ */
open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return isMotionEnabled ? PresentingMotion(isPresenting: true, isContainer: false) : nil return isMotionEnabled ? PresentingMotion(isPresenting: true) : nil
} }
/** /**
...@@ -182,7 +208,7 @@ extension UIViewController { ...@@ -182,7 +208,7 @@ extension UIViewController {
- Returns: An optional UIViewControllerAnimatedTransitioning. - Returns: An optional UIViewControllerAnimatedTransitioning.
*/ */
open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return isMotionEnabled ? DismissingMotion(isPresenting: true, isContainer: false) : nil return isMotionEnabled ? DismissingMotion(isPresenting: true) : nil
} }
/** /**
...@@ -195,6 +221,14 @@ extension UIViewController { ...@@ -195,6 +221,14 @@ extension UIViewController {
open func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { open func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return isMotionEnabled ? MotionPresentationController(presentedViewController: presented, presenting: presenting) : nil return isMotionEnabled ? MotionPresentationController(presentedViewController: presented, presenting: presenting) : nil
} }
open func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return isInteractiveMotionEnabled ? InteractivePresentingMotion() : nil
}
open func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return isInteractiveMotionEnabled ? InteractiveDismissingMotion() : nil
}
} }
extension UIView { extension UIView {
...@@ -348,9 +382,6 @@ open class MotionAnimator: NSObject { ...@@ -348,9 +382,6 @@ open class MotionAnimator: NSObject {
/// A boolean indicating whether Motion is presenting a view controller. /// A boolean indicating whether Motion is presenting a view controller.
open fileprivate(set) var isPresenting: Bool open fileprivate(set) var isPresenting: Bool
/// A boolean indicating whether the view controller is a container.
open fileprivate(set) var isContainer: Bool
/** /**
An Array of UIView pairs with common motionIdentifiers in An Array of UIView pairs with common motionIdentifiers in
the from and to view controllers. the from and to view controllers.
...@@ -374,19 +405,19 @@ open class MotionAnimator: NSObject { ...@@ -374,19 +405,19 @@ open class MotionAnimator: NSObject {
} }
/// The transition context for the current transition. /// The transition context for the current transition.
open var transitionContext: UIViewControllerContextTransitioning! open fileprivate(set) var transitionContext: UIViewControllerContextTransitioning!
/// The transition delay time. /// The transition delay time.
open var delay: TimeInterval = 0 open fileprivate(set) var delay: TimeInterval = 0
/// The transition duration time. /// The transition duration time.
open var duration: TimeInterval = 0.35 open fileprivate(set) var duration: TimeInterval = 0.35
/// The transition container view. /// The transition container view.
open var containerView: UIView! open fileprivate(set) var containerView: UIView!
/// The view that is used to animate the transitions between view controllers. /// The view that is used to animate the transitions between view controllers.
open var transitionView = UIView() open fileprivate(set) var transitionView = UIView()
/// The view that is being transitioned to. /// The view that is being transitioned to.
open var toView: UIView { open var toView: UIView {
...@@ -411,7 +442,6 @@ open class MotionAnimator: NSObject { ...@@ -411,7 +442,6 @@ open class MotionAnimator: NSObject {
/// The default initializer. /// The default initializer.
public override init() { public override init() {
isPresenting = false isPresenting = false
isContainer = false
super.init() super.init()
} }
...@@ -419,12 +449,9 @@ open class MotionAnimator: NSObject { ...@@ -419,12 +449,9 @@ open class MotionAnimator: NSObject {
An initializer to modify the presenting and container state. An initializer to modify the presenting and container state.
- Parameter isPresenting: A boolean value indicating if the - Parameter isPresenting: A boolean value indicating if the
Motion instance is presenting the view controller. Motion instance is presenting the view controller.
- Parameter isContainer: A boolean value indicating if the
Motion instance is a container view controller.
*/ */
public init(isPresenting: Bool, isContainer: Bool) { public init(isPresenting: Bool) {
self.isPresenting = isPresenting self.isPresenting = isPresenting
self.isContainer = isContainer
super.init() super.init()
} }
...@@ -835,5 +862,80 @@ open class DismissingMotion: Motion { ...@@ -835,5 +862,80 @@ open class DismissingMotion: Motion {
toView.setNeedsLayout() toView.setNeedsLayout()
toView.layoutIfNeeded() toView.layoutIfNeeded()
} }
}
open class InteractiveMotion: UIPercentDrivenInteractiveTransition {
/// The transition context for the current transition.
open fileprivate(set) var transitionContext: UIViewControllerContextTransitioning!
/// A reference to the panGesture for transitions.
open fileprivate(set) var panGesture: UIPanGestureRecognizer!
/// The transition container view.
open fileprivate(set) var containerView: UIView!
open override func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
super.startInteractiveTransition(transitionContext)
self.transitionContext = transitionContext
preparePanGesture()
prepareContainerView()
}
//
//
// optional public var completionSpeed: CGFloat { get }
//
// optional public var completionCurve: UIViewAnimationCurve { get }
//
//
// /// In 10.0, if an object conforming to UIViewControllerAnimatedTransitioning is
// /// known to be interruptible, it is possible to start it as if it was not
// /// interactive and then interrupt the transition and interact with it. In this
// /// case, implement this method and return NO. If an interactor does not
// /// implement this method, YES is assumed.
// @available(iOS 10.0, *)
// optional public var wantsInteractiveStart: Bool { get }
}
extension InteractiveMotion {
fileprivate func preparePanGesture() {
panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanUpdate(gestureRecognizer:)))
panGesture.maximumNumberOfTouches = 1
}
/// Prepares the containerView.
fileprivate func prepareContainerView() {
containerView = transitionContext.containerView
containerView.addGestureRecognizer(panGesture)
}
}
extension InteractiveMotion {
@objc
fileprivate func handlePanUpdate(gestureRecognizer: UIGestureRecognizer) {
switch gestureRecognizer.state {
case .began:
panGesture.setTranslation(.zero, in: containerView)
print("began")
case .changed:
let translation = panGesture.translation(in: containerView)
let percentage = fabs(translation.y / containerView.bounds.height)
update(percentage)
print("changed", percentage)
case .ended:
finish()
containerView.removeGestureRecognizer(panGesture)
print("ended")
default:break
}
}
}
open class InteractivePresentingMotion: InteractiveMotion {
}
open class InteractiveDismissingMotion: InteractiveMotion {
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment