學習目標:

  • Gesture Recognizer
  • CALayer & UIBezierPath
  • Class & Function

練習成果:

image

原始碼:

GitHub - takawang/olympic-drone

緣起:

這次的練習,主要是看到下面這篇文章的啟發,不過我簡化了觸模散開與加速度這個部分,主要是練習 swift 實作。

[Vanilla JS] 평창올림픽 드론쇼 만들기

說明:

實作的構想還算直覺,除了長壓連續觸發需要研究一下。

  1. 使用 javascript 的 canvas getImageData() 將 SVG 存成 json 檔 (可參考上文連結中的 getDotPos 方法。
  2. 在 swift 中,透過讀取 json 檔,存成座標點的類別陣列,這兩步驟也可以用來畫其他圖案,不用手工一直描點。
load all points from json file
import UIKit

// Point definition for json object
class Point: Decodable {
  let x, y: Int
  let color: String

  // load all points from json file.
  //
  // ref: https://stackoverflow.com/a/50042872
  // ref: https://praveenkommuri.medium.com/how-to-read-parse-local-json-file-in-swift-28f6cec747cf
  static func loadFromJson(fileName name: String) -> [Point] {
    do {
      guard let filePath = Bundle.main.path(forResource: name, ofType: "json") else { return [] }
      let fileUrl = URL(fileURLWithPath: filePath)
      let data = try Data(contentsOf: fileUrl)
      let points = try JSONDecoder().decode([Point].self, from: data)
      return points

    } catch {
      // should not happen!
      print("Something went wrong: \(error)")
      return []
    }
  }
}

  1. 設計 Drone 類別,讓 Drone 初始位置是隨機的,目的位置是剛剛載入的圖形座標。
  2. 將兩座標分成 20 份,持續按壓連續觸發時,一次移動一份。
  3. 當到達目的座標時,改變 Drone 的顏色。
handle long press event
import UIKit

class ViewController: UIViewController {
  var touchCountTimer: Timer?
  var touchBeginTime: Double = 0
  let droneCollection = DroneCollectionController(fromJsonFile: "ring") // composition

  override func viewDidLoad() {
    super.viewDidLoad()

    droneCollection.attachToLayer(to: self.view.layer) // attach drone layer
    addLongPressGesture() // handle press
  }

  // register gesture recognizer
  //
  // ref: [How can i count time on long-pressed button using UILongPressGestureRecognizer](https://stackoverflow.com/a/53146530)
  func addLongPressGesture(){
    let longPress = UILongPressGestureRecognizer(target: self, action: #selector(longPress(gesture:)))
    longPress.minimumPressDuration = 0.1
    longPress.numberOfTouchesRequired = 1
    self.view.addGestureRecognizer(longPress)
  }

  // handle long press event
  //
  // ref: [How can i count time on long-pressed button using UILongPressGestureRecognizer](https://stackoverflow.com/a/53146530)
  @objc func longPress(gesture: UILongPressGestureRecognizer) {
    switch gesture.state {
    case UIGestureRecognizer.State.began:
      touchCountTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { [self] (timer) in
        // MARK: - do animation
        droneCollection.animation()
      })
      touchBeginTime = Date().timeIntervalSince1970
    case .ended, .failed, .cancelled:
      touchCountTimer?.invalidate() // Stops the timer
      DroneCollectionController.isArrived = false
    case .changed: // wipe
      break
    default:
      print("unknown")
    }
  }
}