1
1

【SwiftUI】iPhoneのロック画面で使用できるライトボタンを再現してみた

Posted at

はじめに

iPhone標準の動作を再現してみると勉強になるので、ロック画面で使用できるライトボタンを再現してみました。

サンプルアプリ

Simulator Screen Recording - iPhone 15 - 2024-01-10 at 22.14.23.gif

実装

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

updatingisDetectingLongPressを変更しています。
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)

onChangeisOnの変更を監視して、ライトをつける処理を実行しています。

.onChange(of: isOn) {
    guard let device = AVCaptureDevice.default(for: .video) else { return }
    if device.hasTorch {
        try? device.lockForConfiguration()
        device.torchMode = isOn ? .on : .off
    }
}

おわり

ライトを付ける処理初めて書きました
権限とか必要なしに使えるんですね

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