LoginSignup
1
1

More than 1 year has passed since last update.

【SwiftUI】一定時間操作がないことを検知する

Last updated at Posted at 2022-11-23

はじめに

PCの場合、一定時間操作がないとスクリーンセーバーが起動します。
そのような挙動を再現したかったのですが、公式のAPIにはなさそうだったので自作してみました。

サンプルアプリ

色々迷った結果、このようなアプリにしました。
画面を放置していると画像を非表示にします。
画面収録_2022-11-24_11_06_42_AdobeExpress.gif

仕様

監視するスクリーンの範囲は.onTimeout()を付けているViewの大きさです。

実装

import SwiftUI

public extension View {
    func onTimeout(seconds: TimeInterval = 30, perform: ((ScreenStates) -> Void)?) -> some View {
        Timeout(content: self, seconds: seconds) { status in
            if let perform {
                perform(status)
            }
        }
    }
}

public enum ScreenStates {
    case active
    case timeout
}

fileprivate struct Timeout<T: View>: View {
    var content: T
    var seconds: TimeInterval
    var perform: ((ScreenStates) -> Void)

    @State var timer: Timer? = nil
    @State var states: ScreenStates = .timeout

    var body: some View {
        content
            .onTapGesture {
                start()
            }
            .onAppear {
                start()
            }
            .onDisappear {
                stop()
            }
    }

    private func changeScreenStatus(status: ScreenStates) {
        if self.states != status {
            states = status
            perform(status)
        }
    }

    private func start() {
        changeScreenStatus(status: .active)
        timer?.invalidate()
        timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: false) { _ in
            changeScreenStatus(status: .timeout)
        }
    }

    private func stop() {
        timer?.invalidate()
    }
}

使い方

import SwiftUI

struct ContentView: View {
    @State var isScreenHidden: Bool = false
    private let url = "https://avatars.githubusercontent.com/u/84154073"
    var body: some View {
        ZStack {
            Color(uiColor: .systemGroupedBackground).ignoresSafeArea()

            VStack(spacing: 5) {
                if isScreenHidden {
                    Image(systemName: "lock.fill")
                    Text("一定時間操作がなかったため、画面を非表示にしました")
                } else {
                    AsyncImage(url: URL(string: url)!)
                }
            }
        }
        .onTimeout(seconds: 5) { status in
            switch status {
            case .active:
                withAnimation { isScreenHidden = false }
            case .timeout:
                withAnimation { isScreenHidden = true }
            }
        }
    }
}

引数のsecondsでタイムアウトになる時間を設定します。

おわり

ライブラリにしたのでスターください!
これからアップデートする予定です!

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