LoginSignup
1

posted at

updated at

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

はじめに

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でタイムアウトになる時間を設定します。

おわり

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

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
What you can do with signing up
1