iOS13で公開予定の「Core Haptics」を使って、Haptic Feedback (触覚フィードバック) するコードを書いてみた

こんにちは。iOS エンジニアの森廣です。
先日行われたAppleのWWDC2019に参加させていただいたことをきっかけに、最新技術を追いかける楽しさに目覚めました。

今回は、その最新技術の中でも自社サービスに取り込めそうな技術を、実際のコードとともに紹介したいと思います。

Haptic Feedback とは

Haptic Feedback とは、ユーザーアクションに対してハードウェアを振動させる触覚フィードバックのことです。
例で言うと、App Storeでアプリを購入する時、間違ったパスワードを入力した時などに発生する振動が「Haptic Feedback」にあたります。

Haptic Feedback を導入するメリットとしては、ユーザーにより自然な感覚を与えられることで、没入感を高められることです。
(特にゲームやVR/AR領域での活用が見込めるかと思います。)

iOS10からは UIFeedbackGenerator を利用することで3種類のフィードバックを導入できましたが、iOS13から利用可能な Core Haptics ではよりカスタマイズ可能な Haptic Feedback を実装することが可能になりました。

ということで、実際に Core Haptics を用いて Haptic Feedback を実装してみたいと思います!

実装の概要

以下の順番で実装していきます。
1, ライブラリ導入
2, 再生準備
3, 再生
4, パターン変更

注意点としては、バージョン11.0以降のXcodeがMacにインストールされている必要があることです。
(2019年6月現在では、Apple Developer Centerからbeta版をダウンロード出来ます。)

もう一点、今回実装する Haptic Feedback は、画像や動画では表現しづらいのを予めご了承ください。
(実機で動かして、確認してみてください。)

実際のソースコード

ライブラリ導入

利用するクラスに Core Haptics を import します。

import CoreHaptics


再生準備

利用するクラスで HapticEngine を定義します。

var hapticEngine: CHHapticEngine?

HapticEngine の利用準備をする関数を書き、呼び出します。

func setupHapticEngine() {
    do {
        hapticEngine = try CHHapticEngine()
        try hapticEngine?.start()
    } catch {
        print("There was an error creating the engine")
    }

    hapticEngine?.stoppedHandler = { reason in
        print("The engine stopped")
    }

    hapticEngine?.resetHandler = { [weak self] in
        print("The engine reset")
            
        do {
            try self?.hapticEngine?.start()
        } catch {
            print("Failed to restart the engine")
        }
    }
}

(参考にしたページ)

Preparing Your App to Play Haptics | Apple Developer Documentation

再生

CHHapticEvent を作成する関数を作ります。

func createHapticEvents() -> [CHHapticEvent] {
    let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: 1)
    let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: 1)
    let event = CHHapticEvent(eventType: .hapticTransient, parameters: [intensity, sharpness], relativeTime: 0)
    return [event]
}

(intensityは強度、sharpnessは鋭さ)

CHHapticPatternPlayer を再生する関数を書き、呼び出します。

func playHapticEngine() {
    do {
        let hapticPattern = try CHHapticPattern(events: createHapticEvents(), parameters: [])
        let hapticPlayer = try hapticEngine?.makePlayer(with: hapticPattern)
        try hapticPlayer?.start(atTime: 0)
    } catch {
        print("Failed to play")
    }
}

(参考にしたページ)

Playing a Single-Tap Haptic Pattern | Apple Developer Documentation

パターン変更

上で作成した createHapticEvents を修正して、さまざまな Haptic をつくります。

(パターン1) 強くて鋭い Haptic

func createHapticEvents() -> [CHHapticEvent] {
    let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: 1)
    let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: 1)
    let event = CHHapticEvent(eventType: .hapticTransient, parameters: [intensity, sharpness], relativeTime: 0)
    return [event]
}


(パターン2) 最初は強くて鋭いが、だんだんフェードアウトしていく1秒の Haptic

func createHapticEvents() -> [CHHapticEvent] {
    var events: [CHHapticEvent] = []
    for i in stride(from: 0, to: 1, by: 0.1) {
        let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: Float(1 - i))
        let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: Float(1 - i))
        let event = CHHapticEvent(eventType: .hapticTransient, parameters: [intensity, sharpness], relativeTime: i)
        events.append(event)
    }
    return events
}


(パターン3) SOS (...---...) みたいな Haptic

func createHapticEvents() -> [CHHapticEvent] {
    let short1 = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 0)
    let short2 = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 0.2)
    let short3 = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 0.4)
    let long1 = CHHapticEvent(eventType: .hapticContinuous, parameters: [], relativeTime: 0.6, duration: 0.5)
    let long2 = CHHapticEvent(eventType: .hapticContinuous, parameters: [], relativeTime: 1.2, duration: 0.5)
    let long3 = CHHapticEvent(eventType: .hapticContinuous, parameters: [], relativeTime: 1.8, duration: 0.5)
    let short4 = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 2.4)
    let short5 = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 2.6)
    let short6 = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 2.8)
    return [short1, short2, short3, long1, long2, long3, short4, short5, short6]
}

(参考にしたページ)

How to play custom vibrations using Core Haptics - free Swift 5.0 example code and tips

コードの全体像

f:id:taketo_morihiro:20190710143409p:plain

おわりに

以上、iOS13で公開予定の「Core Haptics」を用いた Haptic Feedback の実装でした。
iOS13のリリース自体はまだ先でどうなるかわからないのですが、取り敢えずはこれを他のメンバーに提案してみたいと思います!

SHOWROOM は、最新技術を用いたり企画提案したいエンジニアにとってはとてもやりがいのある会社です。お気軽に問い合わせください!

recruit.showroom.co.jp