Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> iOS

Cách phát hiện user không tương tác (inactivity) 5 giây trong iOS

Cần thiết cho: auto-logout security, ẩn UI controls khi xem video, screensaver, power saving...

Giải pháp: Timer + UITapGestureRecognizer

Bước 1: Tạo dự án

Xcode → Single View App → Tên: "DetectingInactivity".

Bước 2: Code ViewController.swift


import UIKit

class ViewController: UIViewController {
    
    var inactivityTimer: Timer?
    let inactivityTimeout: TimeInterval = 5.0 // 5 giây
    
    override func viewDidLoad() {
        super.viewDidLoad()
        startInactivityTimer()
        setupTapGesture()
    }
    
    // MARK: - Timer Management
    func startInactivityTimer() {
        inactivityTimer?.invalidate()
        inactivityTimer = Timer.scheduledTimer(timeInterval: inactivityTimeout,
                                               target: self,
                                               selector: #selector(userInactive),
                                               userInfo: nil,
                                               repeats: false) // Chỉ chạy 1 lần
    }
    
    func resetInactivityTimer() {
        startInactivityTimer() // Reset bằng cách tạo timer mới
    }
    
    @objc func userInactive() {
        print("👤 User không tương tác > \(inactivityTimeout) giây")
        // Thực hiện action: logout, ẩn UI, pause video...
        showInactivityAlert()
    }
    
    // MARK: - Gesture Recognizer
    func setupTapGesture() {
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(screenTapped))
        tapGesture.cancelsTouchesInView = false // Quan trọng: không chặn touch các view con
        view.addGestureRecognizer(tapGesture)
    }
    
    @objc func screenTapped(_ sender: UITapGestureRecognizer) {
        // Chỉ reset khi tap ra ngoài interactive elements
        // (button, textfield, slider... tự handle touch của mình)
        resetInactivityTimer()
        print("🔄 Timer reset do user interaction")
    }
    
    // MARK: - UI Feedback
    func showInactivityAlert() {
        let alert = UIAlertController(title: "Không hoạt động", 
                                      message: "Bạn đã không tương tác hơn 5 giây", 
                                      preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in
            self.resetInactivityTimer() // Reset sau khi userDismiss alert
        })
        present(alert, animated: true)
    }
    
    // Cleanup
    deinit {
        inactivityTimer?.invalidate()
    }
}

Cải tiến: Detect mọi loại interaction (không chỉ tap)


override func touchesBegan(_ touches: Set, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)
    resetInactivityTimer()
}

// Hoặc override sendEvent (AppDelegate) cho toàn app
// AppDelegate.swift
var inactivityTimer: Timer?

func application(_ application: UIApplication, didFinishLaunchingWithOptions...) -> Bool {
    // ...
    setupGlobalInactivityDetection()
    return true
}

func setupGlobalInactivityDetection() {
    inactivityTimer = Timer.scheduledTimer(timeInterval: 300, // 5 phút
                                           target: self,
                                           selector: #selector(globalInactivity),
                                           userInfo: nil,
                                           repeats: false)
}

override func sendEvent(_ event: UIEvent) {
    super.sendEvent(event)
    
    // Chỉ reset cho touch events
    if event.type == .touches {
        inactivityTimer?.invalidate()
        inactivityTimer = Timer.scheduledTimer(timeInterval: 300,
                                               target: self,
                                               selector: #selector(globalInactivity),
                                               userInfo: nil,
                                               repeats: false)
    }
}

@objc func globalInactivity() {
    print("Global inactivity 5 phút - Auto logout")
    // Post notification để VC handle logout
    NotificationCenter.default.post(name: .userInactivityTimeout, object: nil)
}

extension Notification.Name {
    static let userInactivityTimeout = Notification.Name("userInactivityTimeout")
}

Lưu ý quan trọng

  • cancelsTouchesInView = false cho phép touch truyền xuống button/textfield
  • Timer repeats: false (one-shot) hiệu quả hơn repeating timer
  • Luôn invalidate() timer cũ trước khi tạo mới
  • Cleanup trong deinit tránh leak
  • Đối với app phức tạp: dùng AppDelegate.sendEvent hoặc NotificationCenter cho global detection

Kịch bản ứng dụng thực tế

Use Case Timeout Action
Banking/Security app 2-5 phút Auto logout, clear sensitive data
Video player 3-5 giây Ẩn controls, hide status bar
Kiosk/Public display 30-60 giây Reset về màn hình home
Reading app 1-2 phút Auto-save position, dim screen