Skip to content

Instantly share code, notes, and snippets.

@OrkhanAlikhanov
Last active November 8, 2020 06:12
Show Gist options
  • Select an option

  • Save OrkhanAlikhanov/79ce67a1739e7a4a4dba013d00663bd4 to your computer and use it in GitHub Desktop.

Select an option

Save OrkhanAlikhanov/79ce67a1739e7a4a4dba013d00663bd4 to your computer and use it in GitHub Desktop.

Revisions

  1. OrkhanAlikhanov revised this gist Sep 17, 2018. 1 changed file with 16 additions and 16 deletions.
    32 changes: 16 additions & 16 deletions TransitionAnimator.swift
    Original file line number Diff line number Diff line change
    @@ -14,7 +14,6 @@ private class TransitionAnimator {
    guard let layer = layer else {
    return
    }

    layer.speed = 1
    layer.timeOffset = 0
    let t = CACurrentMediaTime()
    @@ -40,44 +39,49 @@ private class TransitionAnimator {
    }

    pause()

    let s = CACurrentMediaTime()
    layer.setValue(s, forKey: "lastStartedAt")
    layer.beginTime = s
    let currentDuration = duration * CFTimeInterval(progress)
    let currentDuration = duration * 2 * CFTimeInterval(progress)
    layer.setValue(currentDuration, forKey: "currentDuration")
    layer.timeOffset = currentDuration
    }

    func pause() {
    guard let layer = layer else {
    return
    }

    let t = CACurrentMediaTime()
    layer.speed = 0
    layer.beginTime = t
    let currentDuration = (layer.value(forKey: "currentDuration") as! TimeInterval) + t - (layer.value(forKey: "lastStartedAt") as! TimeInterval)
    let currentDuration = (layer.value(forKey: "currentDuration") as! TimeInterval) + Double(layer.speed) * (t - (layer.value(forKey: "lastStartedAt") as! TimeInterval))
    layer.setValue(currentDuration, forKey: "currentDuration")
    layer.speed = 0
    layer.timeOffset = currentDuration
    }

    func resume() {
    guard let layer = layer else {
    return
    }

    layer.speed = 1
    let s = CACurrentMediaTime()
    layer.setValue(s, forKey: "lastStartedAt")
    layer.beginTime = s
    }

    func cancel() {
    func cancel(isAnimated: Bool = true) {
    guard let layer = layer, let group = self.group else {
    return
    }

    guard isAnimated else {
    layer.removeAnimation(forKey: groupName)
    return
    }

    let newDuration = layer.timeOffset
    let newAnimations: [CABasicAnimation] = group.animations!.compactMap { $0 as? CABasicAnimation }.map {
    let a = CABasicAnimation()
    @@ -87,6 +91,7 @@ private class TransitionAnimator {
    return a
    }

    layer.removeAnimation(forKey: groupName)
    addGroup(newAnimations, duration: newDuration)
    }

    @@ -95,15 +100,10 @@ private class TransitionAnimator {
    return
    }

    let newDuration = group.duration - layer.timeOffset
    let newAnimations: [CABasicAnimation] = group.animations!.compactMap { $0 as? CABasicAnimation }.map {
    let a = CABasicAnimation()
    a.keyPath = $0.keyPath
    a.fromValue = layer.presentation()!.value(forKeyPath: $0.keyPath!)
    group.animations!.compactMap { $0 as? CABasicAnimation }.forEach {
    layer.setValue($0.toValue, forKeyPath: $0.keyPath!)
    return a
    }

    addGroup(newAnimations, duration: newDuration)
    resume()
    }
    }
    }
  2. OrkhanAlikhanov created this gist Sep 17, 2018.
    109 changes: 109 additions & 0 deletions TransitionAnimator.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,109 @@
    private class TransitionAnimator {
    let groupName = "transition.group"
    weak var layer: CALayer?

    init(_ view: UIView) {
    self.layer = view.layer
    }

    private var group: CAAnimationGroup? {
    return layer?.animation(forKey: groupName) as? CAAnimationGroup
    }

    private func addGroup(_ animations: [CAAnimation], duration: TimeInterval) {
    guard let layer = layer else {
    return
    }

    layer.speed = 1
    layer.timeOffset = 0
    let t = CACurrentMediaTime()
    layer.beginTime = t
    layer.setValue(t, forKey: "lastStartedAt")
    layer.setValue(0, forKey: "currentDuration")
    layer.removeAllAnimations()

    let g = CAAnimationGroup()
    g.animations = animations
    g.duration = duration
    layer.add(g, forKey: groupName)
    }

    func start(_ animations: [CAAnimation], duration: TimeInterval) {
    addGroup(animations, duration: duration)
    pause()
    }

    func seek(_ progress: CGFloat) {
    guard let layer = layer, let duration = group?.duration else {
    return
    }

    pause()

    let s = CACurrentMediaTime()
    layer.setValue(s, forKey: "lastStartedAt")
    layer.beginTime = s
    let currentDuration = duration * CFTimeInterval(progress)
    layer.setValue(currentDuration, forKey: "currentDuration")
    layer.timeOffset = currentDuration
    }

    func pause() {
    guard let layer = layer else {
    return
    }

    let t = CACurrentMediaTime()
    layer.speed = 0
    layer.beginTime = t
    let currentDuration = (layer.value(forKey: "currentDuration") as! TimeInterval) + t - (layer.value(forKey: "lastStartedAt") as! TimeInterval)
    layer.setValue(currentDuration, forKey: "currentDuration")
    layer.timeOffset = currentDuration
    }

    func resume() {
    guard let layer = layer else {
    return
    }

    layer.speed = 1
    let s = CACurrentMediaTime()
    layer.setValue(s, forKey: "lastStartedAt")
    layer.beginTime = s
    }

    func cancel() {
    guard let layer = layer, let group = self.group else {
    return
    }

    let newDuration = layer.timeOffset
    let newAnimations: [CABasicAnimation] = group.animations!.compactMap { $0 as? CABasicAnimation }.map {
    let a = CABasicAnimation()
    a.keyPath = $0.keyPath
    a.toValue = layer.value(forKeyPath: $0.keyPath!)
    a.fromValue = layer.presentation()!.value(forKeyPath: $0.keyPath!)
    return a
    }

    addGroup(newAnimations, duration: newDuration)
    }

    func finish() {
    guard let layer = layer, let group = self.group else {
    return
    }

    let newDuration = group.duration - layer.timeOffset
    let newAnimations: [CABasicAnimation] = group.animations!.compactMap { $0 as? CABasicAnimation }.map {
    let a = CABasicAnimation()
    a.keyPath = $0.keyPath
    a.fromValue = layer.presentation()!.value(forKeyPath: $0.keyPath!)
    layer.setValue($0.toValue, forKeyPath: $0.keyPath!)
    return a
    }

    addGroup(newAnimations, duration: newDuration)
    }
    }