はじめに
いいねボタン
をタップしたときなどに端末が少し震えるような実装をするにはどうするのか気になり調べてみました。
触覚フィードバックを使うことで実現できるようです。
iOS17以降ではこれまでよりも簡単に実装できるようになったようです。
環境
Xcode 15.2
iOS17 ~
内容
sensoryFeedback
を使うことで実現できます
func sensoryFeedback<T>(
_ feedback: SensoryFeedback,
trigger: T,
condition: @escaping (T, T) -> Bool
) -> some View where T : Equatable
フィードバックの種類
種類はたくさん存在しますが、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~の実装ではより意識していこうと思います。
参考