はじめに
iPhone標準の動作を再現してみると勉強になるので、ロック画面で使用できるライトボタンを再現してみました。
サンプルアプリ
実装
import SwiftUI
import AVFoundation
struct ContentView: View {
@GestureState private var isDetectingLongPress = false
@State private var isOn = false
var longPress: some Gesture {
LongPressGesture()
.updating($isDetectingLongPress) { currentState, gestureState, transaction in
gestureState = currentState
}
.onEnded { _ in
isOn.toggle()
}
}
var body: some View {
VStack {
Color.cyan.ignoresSafeArea()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.overlay(alignment: .bottomLeading) {
Image(systemName: isOn ? "flashlight.on.fill" : "flashlight.off.fill")
.resizable()
.scaledToFit()
.frame(height: 25)
.foregroundStyle(isOn ? .black : .white)
.contentShape(.circle)
.frame(width: 45, height: 45)
.background(isOn ? .white.opacity(0.5) : .black.opacity(0.5), in: .circle)
.background(Material.bar, in: .circle)
.padding(40)
.scaleEffect(isDetectingLongPress ? 1.5 : 1.0)
.animation(.bouncy(duration: 0.4, extraBounce: 0.25), value: isDetectingLongPress)
.gesture(longPress)
.sensoryFeedback(.impact(flexibility: .rigid, intensity: 1), trigger: isDetectingLongPress)
}
.onChange(of: isOn) {
guard let device = AVCaptureDevice.default(for: .video) else { return }
if device.hasTorch {
try? device.lockForConfiguration()
device.torchMode = isOn ? .on : .off
}
}
}
}
解説
まずは@GestureState
です。
これに関してはこちらの記事で解説しているのでそちらを見てください
@GestureState private var isDetectingLongPress = false
ライトのオンオフを管理している変数です。
@State private var isOn = false
updating
でisDetectingLongPress
を変更しています。
onEnded
で指を離した時にisOn
を変更しています。
var longPress: some Gesture {
LongPressGesture()
.updating($isDetectingLongPress) { currentState, gestureState, transaction in
gestureState = currentState
}
.onEnded { _ in
isOn.toggle()
}
}
続いてViewです。
animationに.bouncy(duration: 0.4, extraBounce: 0.25)
を指定することによってバウンドするようなアニメーションにすることができます。
.animation(.bouncy(duration: 0.4, extraBounce: 0.25), value: isDetectingLongPress)
sensoryFeedback
で押した時のブルッとような触覚フィードバックを実装することができます。
こちらはiOS17から使用可能です。
.sensoryFeedback(.impact(flexibility: .rigid, intensity: 1), trigger: isDetectingLongPress)
onChange
でisOn
の変更を監視して、ライトをつける処理を実行しています。
.onChange(of: isOn) {
guard let device = AVCaptureDevice.default(for: .video) else { return }
if device.hasTorch {
try? device.lockForConfiguration()
device.torchMode = isOn ? .on : .off
}
}
おわり
ライトを付ける処理初めて書きました
権限とか必要なしに使えるんですね