Skip to content

Commit

Permalink
new animatable zPosition
Browse files Browse the repository at this point in the history
  • Loading branch information
lkzhao committed Feb 6, 2017
1 parent 976c273 commit b7e0b74
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ SERIES 2</string>
<state key="normal" title="Back"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="heroID" value="back"/>
<userDefinedRuntimeAttribute type="string" keyPath="heroModifierString" value="zPosition(2)"/>
<userDefinedRuntimeAttribute type="number" keyPath="zPosition">
<real key="value" value="2"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="hero_dismissViewController" destination="RcI-CE-08V" eventType="touchUpInside" id="KTp-Gj-3H1"/>
Expand Down Expand Up @@ -160,7 +162,9 @@ SERIES 2</string>
<state key="normal" title="Back"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="heroID" value="back"/>
<userDefinedRuntimeAttribute type="string" keyPath="heroModifierString" value="zPosition(2)"/>
<userDefinedRuntimeAttribute type="number" keyPath="zPosition">
<real key="value" value="2"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="hero_dismissViewController" destination="DOw-gI-E85" eventType="touchUpInside" id="boI-dE-7jw"/>
Expand Down Expand Up @@ -230,7 +234,9 @@ SERIES 2</string>
<state key="normal" title="Back"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="heroID" value="back"/>
<userDefinedRuntimeAttribute type="string" keyPath="heroModifierString" value="zPosition(2)"/>
<userDefinedRuntimeAttribute type="number" keyPath="zPosition">
<real key="value" value="2"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="hero_dismissViewController" destination="jVP-SB-1G7" eventType="touchUpInside" id="nEc-ur-ojD"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,16 @@ class AppleProductViewController: UIViewController, HeroViewControllerDelegate {
}

func applyShrinkModifiers() {
view.zPosition = 0
view.heroModifiers = nil
primaryLabel.heroModifiers = [.translate(x:-50, y:(view.center.y - primaryLabel.center.y)/10), .scale(0.9), HeroModifier.duration(0.3)]
secondaryLabel.heroModifiers = [.translate(x:-50, y:(view.center.y - secondaryLabel.center.y)/10), .scale(0.9), HeroModifier.duration(0.3)]
imageView.heroModifiers = [.translate(x:-80), .scale(0.9), HeroModifier.duration(0.3)]
}

func applySlideModifiers() {
view.heroModifiers = [.translate(x: view.bounds.width), .zPosition(2), .duration(0.3)]
view.zPosition = 2
view.heroModifiers = [.translate(x: view.bounds.width), .duration(0.3)]
primaryLabel.heroModifiers = [.translate(x:100), .duration(0.3)]
secondaryLabel.heroModifiers = [.translate(x:100), .duration(0.3)]
imageView.heroModifiers = nil
Expand Down
9 changes: 9 additions & 0 deletions Examples/HeroExamples/UIKit+HeroExamples.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ public extension UIView {
layer.shadowOffset = newValue
}
}
@IBInspectable public var zPosition: CGFloat {
get {
return layer.zPosition
}

set {
layer.zPosition = newValue
}
}
}

func viewController(forStoryboardName: String) -> UIViewController {
Expand Down
39 changes: 19 additions & 20 deletions Sources/CascadePreprocessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,36 +71,35 @@ class CascadePreprocessor: BasePreprocessor {
}

func process(views: [UIView]) {
for (viewIndex, fv) in views.enumerated() {
guard let (deltaTime, direction, delayMatchedViews) = context[fv]?.cascade else { continue }
for view in views {
guard let (deltaTime, direction, delayMatchedViews) = context[view]?.cascade else { continue }

var parentView = fv
if fv is UITableView, let wrapperView = fv.subviews.get(0) {
var parentView = view
if view is UITableView, let wrapperView = view.subviews.get(0) {
parentView = wrapperView
}

let sortedSubviews = parentView.subviews.filter {
return context.pairedView(for: $0) == nil
}.sorted(by: direction.comparator)
let sortedSubviews = parentView.subviews.sorted(by: direction.comparator)

let initialDelay = context[fv]!.delay
for (i, v) in sortedSubviews.enumerated() {
let initialDelay = context[view]!.delay
let finalDelay = TimeInterval(sortedSubviews.count) * deltaTime + initialDelay

for (i, subview) in sortedSubviews.enumerated() {
let delay = TimeInterval(i) * deltaTime + initialDelay
context[v]?.delay = delay
}

if delayMatchedViews {
for i in (viewIndex+1)..<views.count {
let otherView = views[i]
if otherView.superview == fv.superview {
break
func applyDelay(view:UIView) {
if context.pairedView(for: view) == nil {
context[view]?.delay = delay
} else if delayMatchedViews, let paired = context.pairedView(for: view) {
context[view]?.delay = finalDelay
context[paired]?.delay = finalDelay
}
if let pairedView = context.pairedView(for: otherView) {
let delay = TimeInterval(sortedSubviews.count) * deltaTime + initialDelay
context[otherView]!.delay = delay
context[pairedView]!.delay = delay
for subview in view.subviews {
applyDelay(view: subview)
}
}

applyDelay(view: subview)
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion Sources/Hero.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public class Hero: NSObject {
fileprivate var progressUpdateObservers: [HeroProgressUpdateObserver]?

/// max duration needed by the default animator and plugins
fileprivate var totalDuration: TimeInterval = 0.0
public fileprivate(set) var totalDuration: TimeInterval = 0.0
fileprivate var duration: TimeInterval = 0.0
fileprivate var beginTime: TimeInterval? {
didSet {
Expand Down Expand Up @@ -336,6 +336,13 @@ internal extension Hero {
// if no animator can animate toView & fromView, set the effect to fade // i.e. default effect
context[toView] = [.fade]
animatingViews[0].1.insert(toView, at: 0)

if toView.layer.zPosition < fromView.layer.zPosition {
// in this case, we have to animate the zPosition as well. otherwise the fade animation will be hidden.
context[toView]!.append(.zPosition(fromView.layer.zPosition))
context[fromView] = [.zPosition(toView.layer.zPosition)]
animatingViews[0].0.insert(fromView, at: 0)
}
}

// wait for a frame if using navigation controller.
Expand Down Expand Up @@ -363,6 +370,11 @@ internal extension Hero {
}
}

if !skipDefaultAnimation {
// change the duration of the default fade animation to be the total duration of the animation
self.animators.first?.apply(state: [.duration(totalDuration)], to: self.toView)
}

// we are done with setting up, so remove the covering snapshot
completeSnapshot.removeFromSuperview()
self.totalDuration = totalDuration
Expand Down
17 changes: 6 additions & 11 deletions Sources/HeroContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
import UIKit

public class HeroContext {
fileprivate var heroIDToSourceView = [String: UIView]()
fileprivate var heroIDToDestinationView = [String: UIView]()
fileprivate var snapshotViews = [UIView: UIView]()
fileprivate var viewAlphas = [UIView: CGFloat]()
fileprivate var targetStates = [UIView: HeroTargetState]()
internal var heroIDToSourceView = [String: UIView]()
internal var heroIDToDestinationView = [String: UIView]()
internal var snapshotViews = [UIView: UIView]()
internal var viewAlphas = [UIView: CGFloat]()
internal var targetStates = [UIView: HeroTargetState]()

internal init(container: UIView, fromView: UIView, toView: UIView) {
fromViews = HeroContext.processViewTree(view: fromView, container: container, idMap: &heroIDToSourceView, stateMap: &targetStates)
Expand Down Expand Up @@ -166,12 +166,7 @@ extension HeroContext {
}

snapshot.layer.cornerRadius = view.layer.cornerRadius
if let zPosition = self[view]?.zPosition {
snapshot.layer.zPosition = zPosition
} else {
snapshot.layer.zPosition = view.layer.zPosition
}

snapshot.layer.zPosition = view.layer.zPosition
snapshot.layer.opacity = view.layer.opacity
snapshot.layer.isOpaque = view.layer.isOpaque
snapshot.layer.anchorPoint = view.layer.anchorPoint
Expand Down
10 changes: 10 additions & 0 deletions Sources/HeroDefaultAnimatorViewContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ internal class HeroDefaultAnimatorViewContext {
if let cornerRadius = targetState.cornerRadius {
rtn["cornerRadius"] = NSNumber(value: cornerRadius.native)
}
if let zPosition = targetState.zPosition {
rtn["zPosition"] = NSNumber(value: zPosition.native)
}
if let transform = targetState.transform {
rtn["transform"] = NSValue(caTransform3D:transform)
}
Expand Down Expand Up @@ -238,6 +241,13 @@ internal class HeroDefaultAnimatorViewContext {
}
addAnimation(key: key, beginTime: 0, fromValue: targetValue, toValue: targetValue)
}

// support changing duration
if let duration = state.duration {
self.targetState.duration = duration
self.duration = duration
animate(delay: self.targetState.delay - Hero.shared.progress * Hero.shared.totalDuration)
}
}

func resume(timePassed: TimeInterval, reverse: Bool) {
Expand Down
50 changes: 36 additions & 14 deletions Sources/HeroModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,42 @@ extension HeroModifier {
}
}


extension HeroModifier {
/**
Set the opacity for the view to animate from/to.
- Parameters:
- opacity: opacity for the view to animate from/to
*/
public static func opacity(_ opacity: CGFloat) -> HeroModifier {
return HeroModifier { targetState in
targetState.opacity = opacity
}
}

/**
Set the cornerRadius for the view to animate from/to.
- Parameters:
- cornerRadius: cornerRadius for the view to animate from/to
*/
public static func cornerRadius(_ cornerRadius: CGFloat) -> HeroModifier {
return HeroModifier { targetState in
targetState.cornerRadius = cornerRadius
}
}

/**
Set the zPosition for the view to animate from/to.
- Parameters:
- zPosition: zPosition for the view to animate from/to
*/
public static func zPosition(_ zPosition: CGFloat) -> HeroModifier {
return HeroModifier { targetState in
targetState.zPosition = zPosition
}
}
}

// timing modifiers
extension HeroModifier {
/**
Expand Down Expand Up @@ -263,20 +299,6 @@ extension HeroModifier {
public static var useGlobalCoordinateSpace: HeroModifier = HeroModifier { targetState in
targetState.useGlobalCoordinateSpace = true
}

/**
Sets the zPosition during the animation, not animatable.

During animation, Hero might incorrectly infer the order to draw your views. Use this modifier to adjust
the view draw order.
- Parameters:
- zPosition: zPosition during the animation
*/
public static func zPosition(_ zPosition: CGFloat) -> HeroModifier {
return HeroModifier { targetState in
targetState.zPosition = zPosition
}
}

/**
ignore all heroModifiers attributes for a view's direct subviews.
Expand Down
9 changes: 6 additions & 3 deletions Sources/HeroTargetState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,22 @@ public enum HeroSnapshotType {
}

public struct HeroTargetState {
internal var opacity: CGFloat?
internal var cornerRadius: CGFloat?
internal var position: CGPoint?
internal var size: CGSize?
internal var transform: CATransform3D?
internal var opacity: CGFloat?
internal var cornerRadius: CGFloat?
internal var zPosition: CGFloat?

internal var spring: (CGFloat, CGFloat)?
internal var delay: TimeInterval = 0
internal var duration: TimeInterval?
internal var timingFunction: CAMediaTimingFunction?

internal var arc: CGFloat?
internal var zPosition: CGFloat?
internal var source: String?
internal var cascade: (TimeInterval, CascadeDirection, Bool)?

internal var ignoreSubviewModifiers: Bool?
internal var useGlobalCoordinateSpace: Bool?
internal var useScaleBasedSizeChange: Bool?
Expand Down
1 change: 0 additions & 1 deletion Sources/MatchPreprocessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ class MatchPreprocessor: BasePreprocessor {
tvState.source = id
fvState.source = id

fvState.zPosition = tvState.zPosition
fvState.arc = tvState.arc
fvState.duration = tvState.duration
fvState.timingFunction = tvState.timingFunction
Expand Down
3 changes: 3 additions & 0 deletions Sources/SourcePreprocessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class SourcePreprocessor: BasePreprocessor {
if view.layer.cornerRadius != targetView.layer.cornerRadius {
state.cornerRadius = targetView.layer.cornerRadius
}
if view.layer.zPosition != targetView.layer.zPosition {
state.zPosition = targetView.layer.zPosition
}
if view.layer.transform != targetView.layer.transform {
state.transform = targetView.layer.transform
}
Expand Down

0 comments on commit b7e0b74

Please sign in to comment.