AnimatedButton.swift 3.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
//
//  AnimatedButton.swift
//  lottie-swift
//
//  Created by Brandon Withrow on 2/4/19.
//

#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif

// MARK: - AnimatedButton

/// An interactive button that plays an animation when pressed.
open class AnimatedButton: AnimatedControl {

  // MARK: Lifecycle

  public override init(
    animation: LottieAnimation?,
    configuration: LottieConfiguration = .shared)
  {
    super.init(animation: animation, configuration: configuration)

    #if canImport(UIKit)
    isAccessibilityElement = true
    #elseif canImport(AppKit)
    setAccessibilityElement(true)
    #endif
  }

  public override init() {
    super.init()

    #if canImport(UIKit)
    isAccessibilityElement = true
    #elseif canImport(AppKit)
    setAccessibilityElement(true)
    #endif
  }

  required public init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    #if canImport(UIKit)
    isAccessibilityElement = true
    #elseif canImport(AppKit)
    setAccessibilityElement(true)
    #endif
  }

  // MARK: Open

  #if canImport(UIKit)
  open override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
    let _ = super.beginTracking(touch, with: event)
    let touchEvent = UIControl.Event.touchDown
    if let playRange = rangesForEvents[touchEvent.id] {
      animationView.play(fromProgress: playRange.from, toProgress: playRange.to, loopMode: .playOnce)
    }
    return true
  }

  open override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
    super.endTracking(touch, with: event)
    let touchEvent: UIControl.Event
    if let touch, bounds.contains(touch.location(in: self)) {
      touchEvent = UIControl.Event.touchUpInside
      performAction?()
    } else {
      touchEvent = UIControl.Event.touchUpOutside
    }

    if let playRange = rangesForEvents[touchEvent.id] {
      animationView.play(fromProgress: playRange.from, toProgress: playRange.to, loopMode: .playOnce)
    }
  }

  #elseif canImport(AppKit)
  open override func handle(_ event: LottieNSControlEvent) {
    super.handle(event)

    if let playRange = rangesForEvents[event.id] {
      animationView.play(fromProgress: playRange.from, toProgress: playRange.to, loopMode: .playOnce)
    }

    if event == .touchUpInside {
      performAction?()
    }
  }
  #endif

  // MARK: Public

  /// A closure that is called when the button is pressed / clicked
  public var performAction: (() -> Void)?

  #if canImport(UIKit)
  public override var accessibilityTraits: UIAccessibilityTraits {
    set { super.accessibilityTraits = newValue }
    get { super.accessibilityTraits.union(.button) }
  }
  #endif

  /// Sets the play range for the given UIControlEvent.
  public func setPlayRange(fromProgress: AnimationProgressTime, toProgress: AnimationProgressTime, event: LottieControlEvent) {
    rangesForEvents[event.id] = (from: fromProgress, to: toProgress)
  }

  /// Sets the play range for the given UIControlEvent.
  public func setPlayRange(fromMarker fromName: String, toMarker toName: String, event: LottieControlEvent) {
    if
      let start = animationView.progressTime(forMarker: fromName),
      let end = animationView.progressTime(forMarker: toName)
    {
      rangesForEvents[event.id] = (from: start, to: end)
    }
  }

  // MARK: Private

  private var rangesForEvents: [AnyHashable: (from: CGFloat, to: CGFloat)] = [LottieControlEvent.touchUpInside.id: (
    from: 0,
    to: 1)]
}