LoginSignup
1
1

[SwiftUI] iOS17からの触覚フィードバックの簡単実装方法

Posted at

はじめに

いいねボタンをタップしたときなどに端末が少し震えるような実装をするにはどうするのか気になり調べてみました。
触覚フィードバックを使うことで実現できるようです。
iOS17以降ではこれまでよりも簡単に実装できるようになったようです。

環境

Xcode 15.2
iOS17 ~

内容

sensoryFeedbackを使うことで実現できます

func sensoryFeedback<T>(
    _ feedback: SensoryFeedback,
    trigger: T,
    condition: @escaping (T, T) -> Bool
) -> some View where T : Equatable

フィードバックの種類

SensoryFeedback

種類はたくさん存在しますが、iOSで使えるものは以下の通り

  • static let selection: SensoryFeedback
    UI要素が変更されたことを示す

  • static let success: SensoryFeedback
    アクションが完了したことを示す

  • static let warning: SensoryFeedback
    アクションが警告を発することを示す

  • static let error: SensoryFeedback
    エラーが発生したことを示す

その他、視覚体験を補完するのに使うものが存在する

  • static let impact: SensoryFeedback

トリガーとコンディション

enumでトリガーに使う状態を定義、触覚フィードバックを提供する条件を追加

struct MyView: View {
    @State private var phase = Phase.inactive

    var body: some View {
        ContentView(phase: $phase)
            .sensoryFeedback(.selection, trigger: phase) { old, new in
                old == .inactive || new == .expanded
            }
    }

    enum Phase {
        case inactive
        case preparing
        case active
        case expanded
    }
}

いいねボタンをタップした時にトリガーする方法

Boolの値でトリガーする場合はより簡単です

func sensoryFeedback<T>(
    _ feedback: SensoryFeedback,
    trigger: T
) -> some View where T : Equatable

    struct ListItemView: View {
        @State var isLiked: Bool = false

        var body: some View {
            VStack(spacing: 20) {
                HStack {
                    RoundedRectangle(cornerRadius: 20)
                        .fill(.gray)
                        .frame(width: 60, height: 60)
                        .padding(10)
                    Text("リストアイテム")
                        .frame(maxWidth: .infinity, alignment: .leading)
                    Button(action: {
                        isLiked.toggle()
                    }, label: {
                        Image(systemName: isLiked ? "heart.fill" : "heart")
                    })
                }
                Text("リストアイテムのテキスト")
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .multilineTextAlignment(.leading)
                    .padding(10)
            }
+           .sensoryFeedback(.success, trigger: isLiked)
        }
    }

例えばfalseからtrueに変わった時だけトリガーしたい場合はこんな感じで実現できそうです

    .sensoryFeedback(.success, trigger: isLiked) { _, new in
        new == true
    }

条件によってフィードバックのタイプを変えたい場合

func sensoryFeedback<T>(
    trigger: T,
    _ feedback: @escaping (T, T) -> SensoryFeedback?
) -> some View where T : Equatable

struct MyView: View {
    @State private var phase = Phase.inactive


    var body: some View {
        ContentView(phase: $phase)
            .sensoryFeedback(trigger: phase) { old, new in
                switch (old, new) {
                    case (.inactive, _): return .success
                    case (_, .expanded): return .impact
                    default: return nil
                }
            }
    }


    enum Phase {
        case inactive
        case preparing
        case active
        case expanded
    }
}

おわりに

これまでのUINotificationFeedbackGeneratorを使った触覚フィードバックの実装よりも簡単に実装できそうですね。
触覚フィードバックはUXの向上も期待できるものなので、iOS17~の実装ではより意識していこうと思います。

参考

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1