Commit c7200fdf by Daniel Dahan

development: removed interactive MotionTransitions for initial release

parent ac027518
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
import UIKit import UIKit
open class BottomNavigationController: UITabBarController, UITabBarControllerDelegate { open class BottomNavigationController: UITabBarController {
/** /**
An initializer that initializes the object with a NSCoder object. An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance. - Parameter aDecoder: A NSCoder instance.
...@@ -110,15 +110,10 @@ open class BottomNavigationController: UITabBarController, UITabBarControllerDel ...@@ -110,15 +110,10 @@ open class BottomNavigationController: UITabBarController, UITabBarControllerDel
view.clipsToBounds = true view.clipsToBounds = true
view.contentScaleFactor = Screen.scale view.contentScaleFactor = Screen.scale
view.backgroundColor = .white view.backgroundColor = .white
delegate = self // delegate = self
prepareTabBar() prepareTabBar()
} }
/// Handles transitions when tabBarItems are pressed.
open func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return nil
}
/// Prepares the tabBar. /// Prepares the tabBar.
private func prepareTabBar() { private func prepareTabBar() {
tabBar.heightPreset = .normal tabBar.heightPreset = .normal
......
...@@ -58,7 +58,7 @@ extension UIViewController { ...@@ -58,7 +58,7 @@ extension UIViewController {
} }
} }
open class CollectionViewController: MotionTransitionViewController { open class CollectionViewController: UIViewController {
/// A reference to a Reminder. /// A reference to a Reminder.
open let collectionView = CollectionView() open let collectionView = CollectionView()
...@@ -81,8 +81,7 @@ open class CollectionViewController: MotionTransitionViewController { ...@@ -81,8 +81,7 @@ open class CollectionViewController: MotionTransitionViewController {
The super.prepareView method should always be called immediately The super.prepareView method should always be called immediately
when subclassing. when subclassing.
*/ */
open override func prepare() { open func prepare() {
super.prepare()
view.clipsToBounds = true view.clipsToBounds = true
view.backgroundColor = .white view.backgroundColor = .white
view.contentScaleFactor = Screen.scale view.contentScaleFactor = Screen.scale
......
...@@ -280,7 +280,7 @@ extension CALayer { ...@@ -280,7 +280,7 @@ extension CALayer {
a.append(Motion.translateY(to: to)) a.append(Motion.translateY(to: to))
case let .translateZ(to): case let .translateZ(to):
a.append(Motion.translateZ(to: to)) a.append(Motion.translateZ(to: to))
case let .x(_), .y(_), .point(_, _): case .x(_), .y(_), .point(_, _):
let position = Motion.position(to: CGPoint(x: px, y: py)) let position = Motion.position(to: CGPoint(x: px, y: py))
a.append(position) a.append(position)
case let .position(x, y): case let .position(x, y):
...@@ -295,7 +295,7 @@ extension CALayer { ...@@ -295,7 +295,7 @@ extension CALayer {
let zPosition = Motion.zPosition(index: index) let zPosition = Motion.zPosition(index: index)
zPosition.fromValue = s.value(forKey: MotionAnimationKeyPath.zPosition.rawValue) ?? NSNumber(integerLiteral: 0) zPosition.fromValue = s.value(forKey: MotionAnimationKeyPath.zPosition.rawValue) ?? NSNumber(integerLiteral: 0)
a.append(zPosition) a.append(zPosition)
case let .width(_), .height(_), .size(_, _): case .width(_), .height(_), .size(_, _):
a.append(Motion.size(CGSize(width: w, height: h))) a.append(Motion.size(CGSize(width: w, height: h)))
default:break default:break
} }
......
...@@ -39,15 +39,15 @@ fileprivate struct MotionTransitionInstance { ...@@ -39,15 +39,15 @@ fileprivate struct MotionTransitionInstance {
} }
fileprivate struct MotionTransitionInstanceController { fileprivate struct MotionTransitionInstanceController {
fileprivate var delegate: MotionTransition fileprivate var isEnabled: Bool
} }
extension UIViewController { extension UIViewController: UIViewControllerTransitioningDelegate {
/// MotionTransitionInstanceController Reference. /// MotionTransitionInstanceController Reference.
fileprivate var motionTransition: MotionTransitionInstanceController { fileprivate var motionTransition: MotionTransitionInstanceController {
get { get {
return AssociatedObject(base: self, key: &MotionTransitionInstanceControllerKey) { return AssociatedObject(base: self, key: &MotionTransitionInstanceControllerKey) {
return MotionTransitionInstanceController(delegate: MotionTransition()) return MotionTransitionInstanceController(isEnabled: false)
} }
} }
set(value) { set(value) {
...@@ -55,40 +55,47 @@ extension UIViewController { ...@@ -55,40 +55,47 @@ extension UIViewController {
} }
} }
open var transitionDelegate: MotionTransition { open var isMotionTransitionEnabled: Bool {
return motionTransition.delegate get {
return motionTransition.isEnabled
}
set(value) {
if value {
modalPresentationStyle = .custom
transitioningDelegate = self
} }
}
open class MotionTransitionViewController: UIViewController { motionTransition.isEnabled = value
public init() {
super.init(nibName: nil, bundle: nil)
prepare()
} }
}
}
public required init?(coder aDecoder: NSCoder) { extension UIViewController {
super.init(coder: aDecoder) open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
prepare() return isMotionTransitionEnabled ? MotionTransition(isPresenting: true) : nil
} }
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) return isMotionTransitionEnabled ? MotionTransition() : nil
prepare()
} }
/** open func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
Prepares the view instance when intialized. When subclassing, return isMotionTransitionEnabled ? MotionTransitionPresentationController(presentedViewController: presented, presenting: presenting) : nil
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open func prepare() {
modalPresentationStyle = .custom
transitioningDelegate = transitionDelegate
} }
} }
//extension UIViewController {
// open func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
// return isMotionTransitionEnabled ? MotionTransition(isPresenting: operation == .push) : nil
// }
//}
//
//extension UIViewController {
// open func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
// return isMotionTransitionEnabled ? MotionTransition() : nil
// }
//}
extension UIView { extension UIView {
/// The global position of a view. /// The global position of a view.
open var motionPosition: CGPoint { open var motionPosition: CGPoint {
...@@ -125,7 +132,7 @@ extension UIView { ...@@ -125,7 +132,7 @@ extension UIView {
} }
} }
open func snapshot(afterUpdates: Bool) -> UIView { open func motionTransitionSnapshot(afterUpdates: Bool) -> UIView {
isHidden = false isHidden = false
(self as? Pulseable)?.pulse.pulseLayer?.isHidden = true (self as? Pulseable)?.pulse.pulseLayer?.isHidden = true
...@@ -212,9 +219,9 @@ open class MotionTransitionPresentationController: UIPresentationController { ...@@ -212,9 +219,9 @@ open class MotionTransitionPresentationController: UIPresentationController {
open class MotionTransition: NSObject { open class MotionTransition: NSObject {
open var isPresenting: Bool open var isPresenting: Bool
open var screenSnapshot: UIView! open var transitionSnapshot: UIView!
open let backgroundView = UIView() open let transitionBackgroundView = UIView()
open var toViewController: UIViewController! open var toViewController: UIViewController!
...@@ -233,14 +240,17 @@ open class MotionTransition: NSObject { ...@@ -233,14 +240,17 @@ open class MotionTransition: NSObject {
super.init() super.init()
} }
public init(isPresenting: Bool) {
self.isPresenting = isPresenting
super.init()
}
open var toView: UIView { open var toView: UIView {
return toViewController.view return toViewController.view
} }
open var toSubviews: [UIView] { open var toSubviews: [UIView] {
var views: [UIView] = [] return subviews(of: toView)
subviews(of: toView, views: &views)
return views
} }
open var fromView: UIView { open var fromView: UIView {
...@@ -248,8 +258,12 @@ open class MotionTransition: NSObject { ...@@ -248,8 +258,12 @@ open class MotionTransition: NSObject {
} }
open var fromSubviews: [UIView] { open var fromSubviews: [UIView] {
return subviews(of: fromView)
}
open func subviews(of view: UIView) -> [UIView] {
var views: [UIView] = [] var views: [UIView] = []
subviews(of: fromView, views: &views) subviews(of: view, views: &views)
return views return views
} }
...@@ -263,91 +277,64 @@ open class MotionTransition: NSObject { ...@@ -263,91 +277,64 @@ open class MotionTransition: NSObject {
} }
} }
extension MotionTransition: UIViewControllerTransitioningDelegate { extension MotionTransition: UIViewControllerAnimatedTransitioning {
open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { @objc(animateTransition:)
return MotionTransitionPresentedAnimator() open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
} self.transitionContext = transitionContext
open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return MotionTransitionDismissedAnimator()
}
open func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return nil // MotionTransitionInteractiveAnimator()
}
open func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return nil // MotionTransitionInteractiveAnimator()
}
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return MotionTransitionPresentationController(presentedViewController: presented, presenting: presenting)
}
}
extension MotionTransition: UINavigationControllerDelegate { prepareToViewController()
open func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { prepareFromViewController()
isPresenting = operation == .push prepareContainerView()
return self prepareTransitionView()
prepareTransitionBackgroundView()
prepareTransitionSnapshot()
prepareToView()
prepareTransitionAnimation()
} }
open func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { @objc(transitionDuration:)
return MotionTransitionInteractiveAnimator() open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return delay + duration
} }
} }
extension MotionTransition: UITabBarControllerDelegate { extension MotionTransition {
open func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { fileprivate func prepareToViewController() {
isPresenting = true guard let v = transitionContext.viewController(forKey: .to) else {
self.fromViewController = fromViewController ?? fromVC return
self.toViewController = toViewController ?? toVC
return self
} }
toViewController = v
open func tabBarController(_ tabBarController: UITabBarController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return MotionTransitionInteractiveAnimator()
} }
}
extension MotionTransition: UIViewControllerAnimatedTransitioning { fileprivate func prepareFromViewController() {
@objc(animateTransition:) guard let v = transitionContext.viewController(forKey: .from) else {
open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let tVC = transitionContext.viewController(forKey: .to) else {
return return
} }
fromViewController = v
guard let fVC = transitionContext.viewController(forKey: .from) else {
return
} }
self.transitionContext = transitionContext fileprivate func prepareContainerView() {
containerView = transitionContext.containerView containerView = transitionContext.containerView
containerView.addSubview(transitionView)
transitionView.frame = containerView.bounds
toViewController = tVC
fromViewController = fVC
prepareAnimation()
} }
@objc(transitionDuration:) fileprivate func prepareTransitionView() {
open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { transitionView.frame = containerView.bounds
return delay + duration containerView.addSubview(transitionView)
} }
}
extension MotionTransition { fileprivate func prepareTransitionBackgroundView() {
fileprivate func prepareAnimation() { transitionBackgroundView.backgroundColor = fromView.backgroundColor
screenSnapshot = fromView.snapshot(afterUpdates: true) transitionBackgroundView.frame = transitionView.bounds
screenSnapshot.frame = containerView.bounds transitionView.addSubview(transitionBackgroundView)
containerView.addSubview(screenSnapshot) }
backgroundView.backgroundColor = fromView.backgroundColor fileprivate func prepareTransitionSnapshot() {
backgroundView.frame = transitionView.bounds transitionSnapshot = fromView.motionTransitionSnapshot(afterUpdates: true)
transitionView.addSubview(backgroundView) transitionSnapshot.frame = containerView.bounds
transitionView.addSubview(transitionSnapshot)
}
fileprivate func prepareToView() {
if isPresenting { if isPresenting {
containerView.insertSubview(toView, belowSubview: transitionView) containerView.insertSubview(toView, belowSubview: transitionView)
} }
...@@ -356,14 +343,14 @@ extension MotionTransition { ...@@ -356,14 +343,14 @@ extension MotionTransition {
toView.setNeedsLayout() toView.setNeedsLayout()
toView.layoutIfNeeded() toView.layoutIfNeeded()
toView.isHidden = false toView.isHidden = false
}
for tv in toSubviews { fileprivate func prepareTransitionAnimation() {
for fv in fromSubviews { for fv in fromSubviews {
for tv in toSubviews {
if tv.motionTransitionIdentifier == fv.motionTransitionIdentifier { if tv.motionTransitionIdentifier == fv.motionTransitionIdentifier {
var t: TimeInterval = 0 var t: TimeInterval = 0
var d: TimeInterval = 0 var d: TimeInterval = 0
var snapshotAnimations = [CABasicAnimation]()
var snapshotChildAnimations = [CABasicAnimation]()
var tf = MotionAnimationTimingFunction.easeInEaseOut var tf = MotionAnimationTimingFunction.easeInEaseOut
for ta in tv.motionTransitionAnimations { for ta in tv.motionTransitionAnimations {
...@@ -382,6 +369,9 @@ extension MotionTransition { ...@@ -382,6 +369,9 @@ extension MotionTransition {
} }
} }
var snapshotAnimations = [CABasicAnimation]()
var snapshotChildAnimations = [CABasicAnimation]()
snapshotAnimations.append(Motion.position(to: tv.motionPosition)) snapshotAnimations.append(Motion.position(to: tv.motionPosition))
let sizeAnimation = Motion.size(tv.bounds.size) let sizeAnimation = Motion.size(tv.bounds.size)
...@@ -398,8 +388,8 @@ extension MotionTransition { ...@@ -398,8 +388,8 @@ extension MotionTransition {
snapshotAnimations.append(cornerRadiusAnimation) snapshotAnimations.append(cornerRadiusAnimation)
snapshotChildAnimations.append(cornerRadiusAnimation) snapshotChildAnimations.append(cornerRadiusAnimation)
let snapshot = fv.snapshot(afterUpdates: true) let snapshot = fv.motionTransitionSnapshot(afterUpdates: true)
transitionView.insertSubview(snapshot, belowSubview: screenSnapshot) transitionView.insertSubview(snapshot, belowSubview: transitionSnapshot)
Motion.delay(t) { Motion.delay(t) {
for ta in tv.motionTransitionAnimations { for ta in tv.motionTransitionAnimations {
...@@ -429,142 +419,59 @@ extension MotionTransition { ...@@ -429,142 +419,59 @@ extension MotionTransition {
} }
} }
let d = transitionDuration(using: transitionContext) addBackgroundMotionAnimation()
if isPresenting, let v = backgroundView.backgroundColor {
backgroundView.motion(.backgroundColor(v), .duration(d))
} else if nil != backgroundView.backgroundColor {
backgroundView.motion(.backgroundColor(.clear), .duration(d))
}
Motion.delay(d) { [weak self] in
guard let s = self else {
return
}
defer { cleanupAnimation()
s.transitionContext.completeTransition(!s.transitionContext.transitionWasCancelled) cleanupFromView()
cleanupTransitionSnapshot()
} }
}
for v in s.toSubviews { extension MotionTransition {
v.isHidden = false fileprivate func addBackgroundMotionAnimation() {
transitionBackgroundView.motion(.backgroundColor(toView.backgroundColor ?? .clear), .duration(transitionDuration(using: transitionContext)))
} }
}
s.transitionView.removeFromSuperview() extension MotionTransition {
for v in s.transitionView.subviews { fileprivate func cleanupAnimation() {
v.removeFromSuperview() Motion.delay(transitionDuration(using: transitionContext)) { [weak self] in
} guard let s = self else {
return
} }
fromView.isHidden = true s.hideToSubviews()
s.clearTransitionView()
screenSnapshot.removeFromSuperview() s.completeTransition()
} }
}
open class MotionTransitionPresentedAnimator: MotionTransition {
public override init() {
super.init()
isPresenting = true
} }
}
open class MotionTransitionDismissedAnimator: MotionTransition {}
open class MotionTransitionInteractiveAnimator: MotionTransitionInteractiveDelegate {
open override func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
super.startInteractiveTransition(transitionContext)
fileprivate func cleanupFromView() {
Motion.delay(delay) { [weak self] in
self?.fromView.isHidden = true
} }
}
open class MotionTransitionInteractiveDelegate: UIPercentDrivenInteractiveTransition {
open var isPresenting = false
open var transitionContext: UIViewControllerContextTransitioning!
open var containerView: UIView!
open var toView: UIView!
open var toViewController: UIViewController!
open var toViewStartFrame: CGRect!
open var toViewFinalFrame: CGRect!
open var fromView: UIView!
open var fromViewController: UIViewController!
open var fromViewFinalFrame: CGRect!
open var panGesture: UIPanGestureRecognizer!
@objc(startInteractiveTransition:)
open override func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
super.startInteractiveTransition(transitionContext)
guard let tView = transitionContext.view(forKey: .to) else {
return
} }
guard let tVC = transitionContext.viewController(forKey: .to) else { fileprivate func cleanupTransitionSnapshot() {
return Motion.delay(delay) { [weak self] in
self?.transitionSnapshot.removeFromSuperview()
} }
guard let fView = transitionContext.view(forKey: .from) else {
return
} }
guard let fVC = transitionContext.viewController(forKey: .from) else { fileprivate func hideToSubviews() {
return toSubviews.forEach {
$0.isHidden = false
} }
self.transitionContext = transitionContext
containerView = transitionContext.containerView
toView = tView
toViewController = tVC
fromView = fView
fromViewController = fVC
toViewStartFrame = transitionContext.initialFrame(for: toViewController)
toViewFinalFrame = transitionContext.finalFrame(for: toViewController)
fromViewFinalFrame = transitionContext.finalFrame(for: fromViewController)
preparePanGesture()
} }
open func animationEnded(_ transitionCompleted: Bool) { fileprivate func clearTransitionView() {
// print("MotionTransitionAnimator", #function) transitionView.removeFromSuperview()
transitionView.subviews.forEach {
$0.removeFromSuperview()
} }
}
extension MotionTransitionInteractiveDelegate {
fileprivate func preparePanGesture() {
panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(recognizer:)))
panGesture.maximumNumberOfTouches = 1
containerView.addGestureRecognizer(panGesture)
} }
}
extension MotionTransitionInteractiveDelegate { fileprivate func completeTransition() {
@objc transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
fileprivate func handlePanGesture(recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
panGesture.setTranslation(.zero, in: containerView)
case .changed:
let translation = panGesture.translation(in: containerView)
/**
Compute how far the gesture recognizer tranveled on the
vertical axis.
*/
let percentageComplete = fabs(translation.y / containerView.bounds.height)
update(percentageComplete)
case .ended:
finish()
containerView.removeGestureRecognizer(panGesture)
default:break
}
} }
} }
...@@ -121,7 +121,7 @@ open class NavigationController: UINavigationController { ...@@ -121,7 +121,7 @@ open class NavigationController: UINavigationController {
open func prepare() { open func prepare() {
navigationBar.heightPreset = .normal navigationBar.heightPreset = .normal
navigationBar.width = view.width navigationBar.width = view.width
delegate = transitionDelegate // delegate = self
view.clipsToBounds = true view.clipsToBounds = true
view.backgroundColor = .white view.backgroundColor = .white
view.contentScaleFactor = Screen.scale view.contentScaleFactor = Screen.scale
......
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