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,56 +39,63 @@ fileprivate struct MotionTransitionInstance { ...@@ -39,56 +39,63 @@ 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) {
AssociateObject(base: self, key: &MotionTransitionInstanceControllerKey, value: value) AssociateObject(base: self, key: &MotionTransitionInstanceControllerKey, value: value)
} }
} }
open var transitionDelegate: MotionTransition { open var isMotionTransitionEnabled: Bool {
return motionTransition.delegate get {
return motionTransition.isEnabled
}
set(value) {
if value {
modalPresentationStyle = .custom
transitioningDelegate = self
}
motionTransition.isEnabled = value
}
} }
} }
open class MotionTransitionViewController: UIViewController { extension UIViewController {
public init() { open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
super.init(nibName: nil, bundle: nil) return isMotionTransitionEnabled ? MotionTransition(isPresenting: true) : nil
prepare()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepare()
} }
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,73 +277,19 @@ open class MotionTransition: NSObject { ...@@ -263,73 +277,19 @@ open class MotionTransition: NSObject {
} }
} }
extension MotionTransition: UIViewControllerTransitioningDelegate {
open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return MotionTransitionPresentedAnimator()
}
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 {
open func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
isPresenting = operation == .push
return self
}
open func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return MotionTransitionInteractiveAnimator()
}
}
extension MotionTransition: UITabBarControllerDelegate {
open func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
isPresenting = true
self.fromViewController = fromViewController ?? fromVC
self.toViewController = toViewController ?? toVC
return self
}
open func tabBarController(_ tabBarController: UITabBarController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return MotionTransitionInteractiveAnimator()
}
}
extension MotionTransition: UIViewControllerAnimatedTransitioning { extension MotionTransition: UIViewControllerAnimatedTransitioning {
@objc(animateTransition:) @objc(animateTransition:)
open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let tVC = transitionContext.viewController(forKey: .to) else {
return
}
guard let fVC = transitionContext.viewController(forKey: .from) else {
return
}
self.transitionContext = transitionContext self.transitionContext = transitionContext
containerView = transitionContext.containerView prepareToViewController()
containerView.addSubview(transitionView) prepareFromViewController()
transitionView.frame = containerView.bounds prepareContainerView()
prepareTransitionView()
toViewController = tVC prepareTransitionBackgroundView()
fromViewController = fVC prepareTransitionSnapshot()
prepareToView()
prepareAnimation() prepareTransitionAnimation()
} }
@objc(transitionDuration:) @objc(transitionDuration:)
...@@ -339,15 +299,42 @@ extension MotionTransition: UIViewControllerAnimatedTransitioning { ...@@ -339,15 +299,42 @@ extension MotionTransition: UIViewControllerAnimatedTransitioning {
} }
extension MotionTransition { extension MotionTransition {
fileprivate func prepareAnimation() { fileprivate func prepareToViewController() {
screenSnapshot = fromView.snapshot(afterUpdates: true) guard let v = transitionContext.viewController(forKey: .to) else {
screenSnapshot.frame = containerView.bounds return
containerView.addSubview(screenSnapshot) }
toViewController = v
backgroundView.backgroundColor = fromView.backgroundColor }
backgroundView.frame = transitionView.bounds
transitionView.addSubview(backgroundView) fileprivate func prepareFromViewController() {
guard let v = transitionContext.viewController(forKey: .from) else {
return
}
fromViewController = v
}
fileprivate func prepareContainerView() {
containerView = transitionContext.containerView
}
fileprivate func prepareTransitionView() {
transitionView.frame = containerView.bounds
containerView.addSubview(transitionView)
}
fileprivate func prepareTransitionBackgroundView() {
transitionBackgroundView.backgroundColor = fromView.backgroundColor
transitionBackgroundView.frame = transitionView.bounds
transitionView.addSubview(transitionBackgroundView)
}
fileprivate func prepareTransitionSnapshot() {
transitionSnapshot = fromView.motionTransitionSnapshot(afterUpdates: true)
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 {
for fv in fromSubviews { fileprivate func prepareTransitionAnimation() {
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 {
s.transitionContext.completeTransition(!s.transitionContext.transitionWasCancelled)
}
for v in s.toSubviews {
v.isHidden = false
}
s.transitionView.removeFromSuperview()
for v in s.transitionView.subviews {
v.removeFromSuperview()
}
}
fromView.isHidden = true
screenSnapshot.removeFromSuperview() cleanupAnimation()
cleanupFromView()
cleanupTransitionSnapshot()
} }
} }
open class MotionTransitionPresentedAnimator: MotionTransition { extension MotionTransition {
public override init() { fileprivate func addBackgroundMotionAnimation() {
super.init() transitionBackgroundView.motion(.backgroundColor(toView.backgroundColor ?? .clear), .duration(transitionDuration(using: transitionContext)))
isPresenting = true
} }
} }
open class MotionTransitionDismissedAnimator: MotionTransition {} extension MotionTransition {
fileprivate func cleanupAnimation() {
open class MotionTransitionInteractiveAnimator: MotionTransitionInteractiveDelegate { Motion.delay(transitionDuration(using: transitionContext)) { [weak self] in
open override func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) { guard let s = self else {
super.startInteractiveTransition(transitionContext) return
}
s.hideToSubviews()
s.clearTransitionView()
s.completeTransition()
}
} }
}
open class MotionTransitionInteractiveDelegate: UIPercentDrivenInteractiveTransition {
open var isPresenting = false
open var transitionContext: UIViewControllerContextTransitioning!
open var containerView: UIView! fileprivate func cleanupFromView() {
Motion.delay(delay) { [weak self] in
open var toView: UIView! self?.fromView.isHidden = true
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 {
return
}
guard let fView = transitionContext.view(forKey: .from) else {
return
}
guard let fVC = transitionContext.viewController(forKey: .from) else {
return
}
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 cleanupTransitionSnapshot() {
// print("MotionTransitionAnimator", #function) Motion.delay(delay) { [weak self] in
self?.transitionSnapshot.removeFromSuperview()
}
} }
}
fileprivate func hideToSubviews() {
extension MotionTransitionInteractiveDelegate { toSubviews.forEach {
fileprivate func preparePanGesture() { $0.isHidden = false
panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(recognizer:))) }
panGesture.maximumNumberOfTouches = 1
containerView.addGestureRecognizer(panGesture)
} }
}
fileprivate func clearTransitionView() {
extension MotionTransitionInteractiveDelegate { transitionView.removeFromSuperview()
@objc transitionView.subviews.forEach {
fileprivate func handlePanGesture(recognizer: UIPanGestureRecognizer) { $0.removeFromSuperview()
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
} }
} }
fileprivate func completeTransition() {
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
} }
...@@ -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