iPhone: обнаружение бездействия пользователя / время простоя с момента последнего касания экрана

Кто-нибудь реализовал функцию, которая, если пользователь не коснулся экрана в течение определенного периода времени, вы предпринимаете определенное действие? Я пытаюсь найти лучший способ сделать это.

В UIApplication есть этот несколько-связанный метод:

[UIApplication sharedApplication].idleTimerDisabled; 

Было бы неплохо, если бы у вас было что-то вроде этого:

 NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed; 

Затем я мог бы настроить таймер и периодически проверять это значение и предпринимать некоторые действия, когда он превышает пороговое значение.

Надеюсь, это объясняет, что я ищу. Кто-нибудь решился на эту проблему или подумал о том, как вы это сделаете? Благодарю.

Вот ответ, который я искал:

Попросите делегировать приложение подclassа UIApplication. В файле реализации переопределите метод sendEvent:

 - (void)sendEvent:(UIEvent *)event { [super sendEvent:event]; // Only want to reset the timer on a Began touch or an Ended touch, to reduce the number of timer resets. NSSet *allTouches = [event allTouches]; if ([allTouches count] > 0) { // allTouches count only ever seems to be 1, so anyObject works here. UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase; if (phase == UITouchPhaseBegan || phase == UITouchPhaseEnded) [self resetIdleTimer]; } } - (void)resetIdleTimer { if (idleTimer) { [idleTimer invalidate]; [idleTimer release]; } idleTimer = [[NSTimer scheduledTimerWithTimeInterval:maxIdleTime target:self selector:@selector(idleTimerExceeded) userInfo:nil repeats:NO] retain]; } - (void)idleTimerExceeded { NSLog(@"idle time exceeded"); } 

где maxIdleTime и idleTimer являются переменными экземпляра.

Чтобы это работало, вам также нужно изменить свой main.m, чтобы сообщить UIApplicationMain, чтобы использовать ваш class делегата (в этом примере AppDelegate) в качестве основного classа:

 int retVal = UIApplicationMain(argc, argv, @"AppDelegate", @"AppDelegate"); 

У меня есть вариант решения таймера бездействия, который не требует подclassа UIApplication. Он работает с конкретным подclassом UIViewController, поэтому полезно, если у вас есть только один controller вида (например, интерактивное приложение или игра) или только хотите обрабатывать тайм-аут простоя в определенном controllerе представления.

Он также не воссоздает объект NSTimer при каждом сбросе таймера простоя. Он создает только новый, если срабатывает таймер.

Ваш код может вызвать resetIdleTimer для любых других событий, которые могут потребовать аннулирования таймера простоя (например, значительного входа акселерометра).

 @interface MainViewController : UIViewController { NSTimer *idleTimer; } @end #define kMaxIdleTimeSeconds 60.0 @implementation MainViewController #pragma mark - #pragma mark Handling idle timeout - (void)resetIdleTimer { if (!idleTimer) { idleTimer = [[NSTimer scheduledTimerWithTimeInterval:kMaxIdleTimeSeconds target:self selector:@selector(idleTimerExceeded) userInfo:nil repeats:NO] retain]; } else { if (fabs([idleTimer.fireDate timeIntervalSinceNow]) < kMaxIdleTimeSeconds-1.0) { [idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:kMaxIdleTimeSeconds]]; } } } - (void)idleTimerExceeded { [idleTimer release]; idleTimer = nil; [self startScreenSaverOrSomethingInteresting]; [self resetIdleTimer]; } - (UIResponder *)nextResponder { [self resetIdleTimer]; return [super nextResponder]; } - (void)viewDidLoad { [super viewDidLoad]; [self resetIdleTimer]; } @end 

(для краткости исключается код очистки памяти).

Этот stream был большой помощью, и я завернул его в подclass UIWindow, который отправляет уведомления. Я выбрал уведомления, чтобы сделать его настоящим свободным соединением, но вы можете добавить делегата достаточно легко.

Вот суть:

http://gist.github.com/365998

Кроме того, причина проблемы подclassа UIApplication заключается в том, что NIB настроен для создания двух объектов UIApplication, поскольку он содержит приложение и делегат. Подclass UIWindow отлично работает.

Для быстрого v 3.1

dont’t забыть комментарий этой строки в AppDelegate // @ UIApplicationMain

 extension NSNotification.Name { public static let TimeOutUserInteraction: NSNotification.Name = NSNotification.Name(rawValue: "TimeOutUserInteraction") } class InterractionUIApplication: UIApplication { static let ApplicationDidTimoutNotification = "AppTimout" // The timeout in seconds for when to fire the idle timer. let timeoutInSeconds: TimeInterval = 15 * 60 var idleTimer: Timer? // Listen for any touch. If the screen receives a touch, the timer is reset. override func sendEvent(_ event: UIEvent) { super.sendEvent(event) if idleTimer != nil { self.resetIdleTimer() } if let touches = event.allTouches { for touch in touches { if touch.phase == UITouchPhase.began { self.resetIdleTimer() } } } } // Resent the timer because there was user interaction. func resetIdleTimer() { if let idleTimer = idleTimer { idleTimer.invalidate() } idleTimer = Timer.scheduledTimer(timeInterval: timeoutInSeconds, target: self, selector: #selector(self.idleTimerExceeded), userInfo: nil, repeats: false) } // If the timer reaches the limit as defined in timeoutInSeconds, post this notification. func idleTimerExceeded() { NotificationCenter.default.post(name:Notification.Name.TimeOutUserInteraction, object: nil) } } 

создайте файл main.swif и добавьте это (имя важно)

 CommandLine.unsafeArgv.withMemoryRebound(to: UnsafeMutablePointer.self, capacity: Int(CommandLine.argc)) {argv in _ = UIApplicationMain(CommandLine.argc, argv, NSStringFromClass(InterractionUIApplication.self), NSStringFromClass(AppDelegate.self)) } 

Наблюдение за уведомлением в любом другом classе

 NotificationCenter.default.addObserver(self, selector: #selector(someFuncitonName), name: Notification.Name.TimeOutUserInteraction, object: nil) 

Я просто столкнулся с этой проблемой с игрой, которая контролируется движениями, т. Е. Отключена блокировка экрана, но должна активировать ее снова в режиме меню. Вместо таймера я инкапсулировал все вызовы setIdleTimerDisabled в небольшом classе, предоставляя следующие методы:

 - (void) enableIdleTimerDelayed { [self performSelector:@selector (enableIdleTimer) withObject:nil afterDelay:60]; } - (void) enableIdleTimer { [NSObject cancelPreviousPerformRequestsWithTarget:self]; [[UIApplication sharedApplication] setIdleTimerDisabled:NO]; } - (void) disableIdleTimer { [NSObject cancelPreviousPerformRequestsWithTarget:self]; [[UIApplication sharedApplication] setIdleTimerDisabled:YES]; } 

disableIdleTimer деактивирует таймер простоя, enableIdleTimerDelayed при входе в меню или что-то, что должно запускаться с активным таймером в режиме ожидания, и enableIdleTimer вызывается из вашего applicationWillResignActive метода AppDelegate, чтобы гарантировать, что все ваши изменения будут правильно настроены на поведение по умолчанию системы.
Я написал статью и предоставил код для одиночного classа IdleTimerManager Idle Timer Handling в iPhone Games

Вот еще один способ обнаружить активность:

Таймер добавляется в UITrackingRunLoopMode , поэтому он может срабатывать только при наличии активности UITracking . Он также имеет приятное преимущество не спамить вас для всех событий касания, тем самым сообщая, была ли активность за последние секунды ACTIVITY_DETECT_TIMER_RESOLUTION . Я назвал селектор keepAlive поскольку для этого он подходит. Вы, конечно, можете делать все, что захотите, с информацией о том, что в последнее время была активность.

 _touchesTimer = [NSTimer timerWithTimeInterval:ACTIVITY_DETECT_TIMER_RESOLUTION target:self selector:@selector(keepAlive) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:_touchesTimer forMode:UITrackingRunLoopMode]; 

На самом деле идея подclassа отлично работает. Просто не делайте ваш делегат подclassом UIApplication . Создайте другой файл, который наследуется от UIApplication (например, myApp). В IB задайте class объекта fileOwner для myApp и в myApp.m реализуем метод sendEvent как указано выше. В main.m do:

 int retVal = UIApplicationMain(argc,argv,@"myApp.m",@"myApp.m") 

и вуаля!

В конечном счете вам нужно определить, что вы считаете праздным, – это недействительный результат, когда пользователь не касается экрана или это состояние системы, если не используются вычислительные ресурсы? Возможно, во многих приложениях пользователь может что-то делать, даже если он не активно взаимодействует с устройством через сенсорный экран. Хотя пользователь, вероятно, знаком с концепцией спящего устройства и уведомлением о том, что это произойдет через затемнение экрана, не обязательно, чтобы они ожидали, что что-то произойдет, если они простаивают – вам нужно быть осторожными о том, что вы будете делать. Но вернемся к первоначальному утверждению – если вы считаете, что 1-й случай является вашим определением, нет действительно простого способа сделать это. Вам нужно будет получить каждое событие касания, передавая его по цепочке ответчиков по мере необходимости, отмечая время его получения. Это даст вам основание для расчета простоя. Если вы считаете, что второй случай является вашим определением, вы можете играть с уведомлением NSPostWhenIdle, чтобы попытаться выполнить свою логику в то время.

Все это кажется слишком сложным. Я считаю, что это более чистый способ проксировать все необходимые вам штрихи.

 fileprivate var timer ... //timer logic here @objc public class CatchAllGesture : UIGestureRecognizer { override public func touchesBegan(_ touches: Set, with event: UIEvent) { super.touchesBegan(touches, with: event) } override public func touchesEnded(_ touches: Set, with event: UIEvent) { //reset your timer here state = .failed super.touchesEnded(touches, with: event) } override public func touchesMoved(_ touches: Set, with event: UIEvent) { super.touchesMoved(touches, with: event) } } @objc extension YOURAPPAppDelegate { func addGesture () { let aGesture = CatchAllGesture(target: nil, action: nil) aGesture.cancelsTouchesInView = false self.window.addGestureRecognizer(aGesture) } } 

В вашем приложении делегат закончил метод запуска, просто позвоните в addGesture и все настроено. Все прикосновения пройдут через методы CatchAllGesture, не препятствуя другим функциям.

  • Как программно изменить оттенок UIImage?
  • Получение времени, прошедшего в Objective-C
  • Сохранение UIColor и загрузка из NSUserDefaults
  • Округленный UIView с использованием CALayers - только некоторые углы - как?
  • Как написать простой метод Ping в Cocoa / Objective-C
  • Есть ли способ подавить предупреждения в Xcode?
  • Как я могу изменить NSArray в Objective-C?
  • Как получить счетчик ссылок NSObject?
  • Как написать данные в plist?
  • Могу ли я отключить прокрутку звука UIPickerView?
  • Сохранить видео Youtube для iPhone в приложении
  • Interesting Posts

    Создайте xcarchive в определенной папке из командной строки

    Обработка исключений в R

    Спецификаторы формата printf для uint32_t и size_t

    REST Complex / Композитные / Вложенные ресурсы

    Никакое соединение не может быть сделано, потому что целевая машина активно отказалась?

    Какой лучший инструмент для автоматического резервного копирования выбранных папок из Windows на внешний жесткий диск?

    Используйте Google вместо Bing с поиском в Windows 10

    Есть ли гарантия выравнивания возврата адреса с помощью новой операции C ++?

    Поддержка языков для операторов цепного сравнения (x <y <z)

    Как ссылаться на ресурсы изображений в XAML?

    Как ограничить пропускную способность в Java?

    Как переключить направление записи (LTR / RTL) в Word 2007?

    Оператор PowerShell и -contains

    Как суммировать диапазон ячеек со значениями N / A?

    Должен ли метод поиска возвращать ‘null’ или вызывать исключение, если он не может получить возвращаемое значение?

    Давайте будем гением компьютера.