Commit 6df970aa by Daniel Dahan

reworked TransitionPreprocessor

parent 5c4a028f
...@@ -35,37 +35,28 @@ public enum CascadeDirection { ...@@ -35,37 +35,28 @@ public enum CascadeDirection {
case rightToLeft case rightToLeft
case radial(center:CGPoint) case radial(center:CGPoint)
case inverseRadial(center:CGPoint) case inverseRadial(center:CGPoint)
var comparator: (UIView, UIView) -> Bool { var comparator: (UIView, UIView) -> Bool {
switch self { switch self {
case .topToBottom: case .topToBottom:
return { return $0.frame.minY < $1.frame.minY } return { return $0.frame.minY < $1.frame.minY }
case .bottomToTop: case .bottomToTop:
return { return $0.frame.maxY == $1.frame.maxY ? $0.frame.maxX > $1.frame.maxX : $0.frame.maxY > $1.frame.maxY } return { return $0.frame.maxY == $1.frame.maxY ? $0.frame.maxX > $1.frame.maxX : $0.frame.maxY > $1.frame.maxY }
case .leftToRight: case .leftToRight:
return { return $0.frame.minX < $1.frame.minX } return { return $0.frame.minX < $1.frame.minX }
case .rightToLeft: case .rightToLeft:
return { return $0.frame.maxX > $1.frame.maxX } return { return $0.frame.maxX > $1.frame.maxX }
case .radial(let center): case .radial(let center):
return { return $0.center.distance(center) < $1.center.distance(center) } return { return $0.center.distance(center) < $1.center.distance(center) }
case .inverseRadial(let center): case .inverseRadial(let center):
return { return $0.center.distance(center) > $1.center.distance(center) } return { return $0.center.distance(center) > $1.center.distance(center) }
} }
} }
init?(_ string: String) {
switch string {
case "bottomToTop":
self = .bottomToTop
case "leftToRight":
self = .leftToRight
case "rightToLeft":
self = .rightToLeft
case "topToBottom":
self = .topToBottom
default:
return nil
}
}
} }
class CascadePreprocessor: MotionPreprocessor { class CascadePreprocessor: MotionPreprocessor {
......
...@@ -36,6 +36,7 @@ public enum MotionTransitionType { ...@@ -36,6 +36,7 @@ public enum MotionTransitionType {
case down case down
} }
case none
case auto case auto
case push(direction: Direction) case push(direction: Direction)
case pull(direction: Direction) case pull(direction: Direction)
...@@ -51,80 +52,117 @@ public enum MotionTransitionType { ...@@ -51,80 +52,117 @@ public enum MotionTransitionType {
indirect case selectBy(presenting: MotionTransitionType, dismissing: MotionTransitionType) indirect case selectBy(presenting: MotionTransitionType, dismissing: MotionTransitionType)
/**
Sets the presenting and dismissing transitions.
- Parameter presenting: A MotionTransitionType.
- Returns: A MotionTransitionType.
*/
public static func autoReverse(presenting: MotionTransitionType) -> MotionTransitionType { public static func autoReverse(presenting: MotionTransitionType) -> MotionTransitionType {
return .selectBy(presenting: presenting, dismissing: presenting.reversed()) return .selectBy(presenting: presenting, dismissing: presenting.reversed())
} }
case none /// Returns a reversal transition.
func reversed() -> MotionTransitionType { func reversed() -> MotionTransitionType {
switch self { switch self {
case .push(direction: .up): case .push(direction: .up):
return .pull(direction: .down) return .pull(direction: .down)
case .push(direction: .right): case .push(direction: .right):
return .pull(direction: .left) return .pull(direction: .left)
case .push(direction: .down): case .push(direction: .down):
return .pull(direction: .up) return .pull(direction: .up)
case .push(direction: .left): case .push(direction: .left):
return .pull(direction: .right) return .pull(direction: .right)
case .pull(direction: .up): case .pull(direction: .up):
return .push(direction: .down) return .push(direction: .down)
case .pull(direction: .right): case .pull(direction: .right):
return .push(direction: .left) return .push(direction: .left)
case .pull(direction: .down): case .pull(direction: .down):
return .push(direction: .up) return .push(direction: .up)
case .pull(direction: .left): case .pull(direction: .left):
return .push(direction: .right) return .push(direction: .right)
case .cover(direction: .up): case .cover(direction: .up):
return .uncover(direction: .down) return .uncover(direction: .down)
case .cover(direction: .right): case .cover(direction: .right):
return .uncover(direction: .left) return .uncover(direction: .left)
case .cover(direction: .down): case .cover(direction: .down):
return .uncover(direction: .up) return .uncover(direction: .up)
case .cover(direction: .left): case .cover(direction: .left):
return .uncover(direction: .right) return .uncover(direction: .right)
case .uncover(direction: .up): case .uncover(direction: .up):
return .cover(direction: .down) return .cover(direction: .down)
case .uncover(direction: .right): case .uncover(direction: .right):
return .cover(direction: .left) return .cover(direction: .left)
case .uncover(direction: .down): case .uncover(direction: .down):
return .cover(direction: .up) return .cover(direction: .up)
case .uncover(direction: .left): case .uncover(direction: .left):
return .cover(direction: .right) return .cover(direction: .right)
case .slide(direction: .up): case .slide(direction: .up):
return .slide(direction: .down) return .slide(direction: .down)
case .slide(direction: .down): case .slide(direction: .down):
return .slide(direction: .up) return .slide(direction: .up)
case .slide(direction: .left): case .slide(direction: .left):
return .slide(direction: .right) return .slide(direction: .right)
case .slide(direction: .right): case .slide(direction: .right):
return .slide(direction: .left) return .slide(direction: .left)
case .zoomSlide(direction: .up): case .zoomSlide(direction: .up):
return .zoomSlide(direction: .down) return .zoomSlide(direction: .down)
case .zoomSlide(direction: .down): case .zoomSlide(direction: .down):
return .zoomSlide(direction: .up) return .zoomSlide(direction: .up)
case .zoomSlide(direction: .left): case .zoomSlide(direction: .left):
return .zoomSlide(direction: .right) return .zoomSlide(direction: .right)
case .zoomSlide(direction: .right): case .zoomSlide(direction: .right):
return .zoomSlide(direction: .left) return .zoomSlide(direction: .left)
case .pageIn(direction: .up): case .pageIn(direction: .up):
return .pageOut(direction: .down) return .pageOut(direction: .down)
case .pageIn(direction: .right): case .pageIn(direction: .right):
return .pageOut(direction: .left) return .pageOut(direction: .left)
case .pageIn(direction: .down): case .pageIn(direction: .down):
return .pageOut(direction: .up) return .pageOut(direction: .up)
case .pageIn(direction: .left): case .pageIn(direction: .left):
return .pageOut(direction: .right) return .pageOut(direction: .right)
case .pageOut(direction: .up): case .pageOut(direction: .up):
return .pageIn(direction: .down) return .pageIn(direction: .down)
case .pageOut(direction: .right): case .pageOut(direction: .right):
return .pageIn(direction: .left) return .pageIn(direction: .left)
case .pageOut(direction: .down): case .pageOut(direction: .down):
return .pageIn(direction: .up) return .pageIn(direction: .up)
case .pageOut(direction: .left): case .pageOut(direction: .left):
return .pageIn(direction: .right) return .pageIn(direction: .right)
case .zoom: case .zoom:
return .zoomOut return .zoomOut
case .zoomOut: case .zoomOut:
return .zoom return .zoom
...@@ -132,44 +170,50 @@ public enum MotionTransitionType { ...@@ -132,44 +170,50 @@ public enum MotionTransitionType {
return self return self
} }
} }
public var label: String? {
let mirror = Mirror(reflecting: self)
if let associated = mirror.children.first {
let valuesMirror = Mirror(reflecting: associated.value)
if !valuesMirror.children.isEmpty {
let parameters = valuesMirror.children.map { ".\($0.value)" }.joined(separator: ",")
return ".\(associated.label ?? "")(\(parameters))"
}
return ".\(associated.label ?? "")(.\(associated.value))"
}
return ".\(self)"
}
} }
class TransitionPreprocessor: MotionPreprocessor { class TransitionPreprocessor: MotionPreprocessor {
/// A reference to a MotionContext. /// A reference to a MotionContext instance.
weak var context: MotionContext! weak var context: MotionContext!
/// A reference to a Motion instance.
weak var motion: Motion? weak var motion: Motion?
/**
An initializer that accepts a given Motion instance.
- Parameter motion: A Motion instance.
*/
init(motion: Motion) { init(motion: Motion) {
self.motion = motion self.motion = motion
} }
/**
Shifts the transition by a given size.
- Parameter direction: A MotionTransitionType.Direction.
- Parameter isAppearing: A boolean indicating whether it is appearing
or not.
- Parameter size: An optional CGSize.
- Parameter transpose: A boolean indicating to change the `x` point for `y`
and `y` point for `x`.
- Returns: A CGPoint.
*/
func shift(direction: MotionTransitionType.Direction, isAppearing: Bool, size: CGSize? = nil, transpose: Bool = false) -> CGPoint { func shift(direction: MotionTransitionType.Direction, isAppearing: Bool, size: CGSize? = nil, transpose: Bool = false) -> CGPoint {
let size = size ?? context.container.bounds.size let size = size ?? context.container.bounds.size
let rtn: CGPoint let point: CGPoint
switch direction { switch direction {
case .left, .right: case .left, .right:
rtn = CGPoint(x: (direction == .right) == isAppearing ? -size.width : size.width, y: 0) point = CGPoint(x: (.right == direction) == isAppearing ? -size.width : size.width, y: 0)
case .up, .down: case .up, .down:
rtn = CGPoint(x: 0, y: (direction == .down) == isAppearing ? -size.height : size.height) point = CGPoint(x: 0, y: (.down == direction) == isAppearing ? -size.height : size.height)
} }
if transpose { if transpose {
return CGPoint(x: rtn.y, y: rtn.x) return CGPoint(x: point.y, y: point.x)
} }
return rtn
return point
} }
/** /**
...@@ -178,40 +222,48 @@ class TransitionPreprocessor: MotionPreprocessor { ...@@ -178,40 +222,48 @@ class TransitionPreprocessor: MotionPreprocessor {
- Parameter toViews: An Array of UIViews. - Parameter toViews: An Array of UIViews.
*/ */
func process(fromViews: [UIView], toViews: [UIView]) { func process(fromViews: [UIView], toViews: [UIView]) {
guard let motion = motion else { return } guard let m = motion else {
var defaultAnimation = motion.defaultAnimation return
let inNavigationController = motion.isNavigationController }
let inTabBarController = motion.isTabBarController
let toViewController = motion.toViewController var defaultAnimation = m.defaultAnimation
let fromViewController = motion.fromViewController let isNavigationController = m.isNavigationController
let presenting = motion.isPresenting let isTabBarController = m.isTabBarController
let fromOverFullScreen = motion.fromOverFullScreen let toViewController = m.toViewController
let toOverFullScreen = motion.toOverFullScreen let fromViewController = m.fromViewController
let toView = motion.toView let isPresenting = m.isPresenting
let fromView = motion.fromView let fromOverFullScreen = m.fromOverFullScreen
let animators = motion.animators let toOverFullScreen = m.toOverFullScreen
let toView = m.toView
let fromView = m.fromView
let animators = m.animators
if case .auto = defaultAnimation { if case .auto = defaultAnimation {
if inNavigationController, let navAnim = toViewController?.navigationController?.motionNavigationTransitionType { if isNavigationController, let navAnim = toViewController?.navigationController?.motionNavigationTransitionType {
defaultAnimation = navAnim defaultAnimation = navAnim
} else if inTabBarController, let tabAnim = toViewController?.tabBarController?.motionTabBarTransitionType {
} else if isTabBarController, let tabAnim = toViewController?.tabBarController?.motionTabBarTransitionType {
defaultAnimation = tabAnim defaultAnimation = tabAnim
} else if let modalAnim = (presenting ? toViewController : fromViewController)?.motionModalTransitionType {
} else if let modalAnim = (isPresenting ? toViewController : fromViewController)?.motionModalTransitionType {
defaultAnimation = modalAnim defaultAnimation = modalAnim
} }
} }
if case .selectBy(let presentAnim, let dismissAnim) = defaultAnimation { if case .selectBy(let presentAnim, let dismissAnim) = defaultAnimation {
defaultAnimation = presenting ? presentAnim : dismissAnim defaultAnimation = isPresenting ? presentAnim : dismissAnim
} }
if case .auto = defaultAnimation { if case .auto = defaultAnimation {
if animators!.contains(where: { $0.canAnimate(view: toView, isAppearing: true) || $0.canAnimate(view: fromView, isAppearing: false) }) { if animators!.contains(where: { $0.canAnimate(view: toView, isAppearing: true) || $0.canAnimate(view: fromView, isAppearing: false) }) {
defaultAnimation = .none defaultAnimation = .none
} else if inNavigationController {
defaultAnimation = presenting ? .push(direction:.left) : .pull(direction:.right) } else if isNavigationController {
} else if inTabBarController { defaultAnimation = isPresenting ? .push(direction:.left) : .pull(direction:.right)
defaultAnimation = presenting ? .slide(direction:.left) : .slide(direction:.right)
} else if isTabBarController {
defaultAnimation = isPresenting ? .slide(direction:.left) : .slide(direction:.right)
} else { } else {
defaultAnimation = .fade defaultAnimation = .fade
} }
...@@ -229,79 +281,97 @@ class TransitionPreprocessor: MotionPreprocessor { ...@@ -229,79 +281,97 @@ class TransitionPreprocessor: MotionPreprocessor {
.shadow(radius: 5), .shadow(radius: 5),
.shadow(offset: .zero), .shadow(offset: .zero),
.masksToBounds(false)] .masksToBounds(false)]
switch defaultAnimation { switch defaultAnimation {
case .push(let direction): case .push(let direction):
context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)), context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)),
.shadow(opacity: 0), .shadow(opacity: 0),
.beginWith(transitions: shadowState), .beginWith(transitions: shadowState),
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false) / 3), context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false) / 3),
.overlay(color: .black, opacity: 0.1), .overlay(color: .black, opacity: 0.1),
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
case .pull(let direction): case .pull(let direction):
motion.insertToViewFirst = true m.insertToViewFirst = true
context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)), context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)),
.shadow(opacity: 0), .shadow(opacity: 0),
.beginWith(transitions: shadowState)]) .beginWith(transitions: shadowState)])
context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true) / 3), context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true) / 3),
.overlay(color: .black, opacity: 0.1)]) .overlay(color: .black, opacity: 0.1)])
case .slide(let direction): case .slide(let direction):
context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false))]) context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false))])
context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true))]) context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true))])
case .zoomSlide(let direction): case .zoomSlide(let direction):
context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)), .scale(to: 0.8)]) context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)), .scale(to: 0.8)])
context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)), .scale(to: 0.8)]) context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)), .scale(to: 0.8)])
case .cover(let direction): case .cover(let direction):
context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)), context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)),
.shadow(opacity: 0), .shadow(opacity: 0),
.beginWith(transitions: shadowState), .beginWith(transitions: shadowState),
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
context[fromView]!.append(contentsOf: [.overlay(color: .black, opacity: 0.1), context[fromView]!.append(contentsOf: [.overlay(color: .black, opacity: 0.1),
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
case .uncover(let direction): case .uncover(let direction):
motion.insertToViewFirst = true m.insertToViewFirst = true
context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)), context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)),
.shadow(opacity: 0), .shadow(opacity: 0),
.beginWith(transitions: shadowState)]) .beginWith(transitions: shadowState)])
context[toView]!.append(contentsOf: [.overlay(color: .black, opacity: 0.1)]) context[toView]!.append(contentsOf: [.overlay(color: .black, opacity: 0.1)])
case .pageIn(let direction): case .pageIn(let direction):
context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)), context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)),
.shadow(opacity: 0), .shadow(opacity: 0),
.beginWith(transitions: shadowState), .beginWith(transitions: shadowState),
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
context[fromView]!.append(contentsOf: [.scale(to: 0.7), context[fromView]!.append(contentsOf: [.scale(to: 0.7),
.overlay(color: .black, opacity: 0.1), .overlay(color: .black, opacity: 0.1),
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
case .pageOut(let direction): case .pageOut(let direction):
motion.insertToViewFirst = true m.insertToViewFirst = true
context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)), context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)),
.shadow(opacity: 0), .shadow(opacity: 0),
.beginWith(transitions: shadowState)]) .beginWith(transitions: shadowState)])
context[toView]!.append(contentsOf: [.scale(to: 0.7), context[toView]!.append(contentsOf: [.scale(to: 0.7),
.overlay(color: .black, opacity: 0.1)]) .overlay(color: .black, opacity: 0.1)])
case .fade: case .fade:
// TODO: clean up this. overFullScreen logic shouldn't be here // TODO: clean up this. overFullScreen logic shouldn't be here
if !(fromOverFullScreen && !presenting) { if !(fromOverFullScreen && !isPresenting) {
context[toView] = [.fade] context[toView] = [.fade]
} }
#if os(tvOS) #if os(tvOS)
context[fromView] = [.fade] context[fromView] = [.fade]
#else #else
if (!presenting && toOverFullScreen) || !fromView.isOpaque || (fromView.backgroundColor?.alphaComponent ?? 1) < 1 { if (!isPresenting && toOverFullScreen) || !fromView.isOpaque || (fromView.backgroundColor?.alphaComponent ?? 1) < 1 {
context[fromView] = [.fade] context[fromView] = [.fade]
} }
#endif #endif
context[toView]!.append(.preferredDurationMatchesLongest) context[toView]!.append(.preferredDurationMatchesLongest)
context[fromView]!.append(.preferredDurationMatchesLongest) context[fromView]!.append(.preferredDurationMatchesLongest)
case .zoom: case .zoom:
motion.insertToViewFirst = true m.insertToViewFirst = true
context[fromView]!.append(contentsOf: [.scale(to: 1.3), .fade]) context[fromView]!.append(contentsOf: [.scale(to: 1.3), .fade])
context[toView]!.append(contentsOf: [.scale(to: 0.7)]) context[toView]!.append(contentsOf: [.scale(to: 0.7)])
case .zoomOut: case .zoomOut:
context[toView]!.append(contentsOf: [.scale(to: 1.3), .fade]) context[toView]!.append(contentsOf: [.scale(to: 1.3), .fade])
context[fromView]!.append(contentsOf: [.scale(to: 0.7)]) context[fromView]!.append(contentsOf: [.scale(to: 0.7)])
default: default:
fatalError("Not implemented") fatalError("Not implemented")
} }
......
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