[ios] iPhone : 마지막 화면 터치 이후의 사용자 비 활동 / 유휴 시간 감지
사용자가 특정 시간 동안 화면을 터치하지 않은 경우 특정 작업을 수행하는 기능을 구현 한 사람이 있습니까? 최선의 방법을 찾으려고 노력하고 있습니다.
UIApplication에는 다소 관련이있는 메소드가 있습니다.
[UIApplication sharedApplication].idleTimerDisabled;
대신 다음과 같은 것이 있으면 좋을 것입니다.
NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed;
그런 다음 타이머를 설정 하고이 값을 주기적으로 확인하고 임계 값을 초과하면 조치를 취할 수 있습니다.
바라건대 내가 찾고있는 것을 설명합니다. 이미이 문제를 해결 한 사람이 있습니까, 아니면 어떻게 할 것인지에 대한 생각이 있습니까? 감사.
답변
내가 찾은 답변은 다음과 같습니다.
응용 프로그램에 하위 클래스 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에게 위임 클래스 (이 예제에서는 AppDelegate)를 주요 클래스로 사용하도록 지시하십시오.
int retVal = UIApplicationMain(argc, argv, @"AppDelegate", @"AppDelegate");
답변
UIApplication을 서브 클래스 화하지 않아도되는 유휴 타이머 솔루션의 변형이 있습니다. 특정 UIViewController 서브 클래스에서 작동하므로 대화 형 앱이나 게임과 같은 하나의 뷰 컨트롤러 만 있거나 특정 뷰 컨트롤러에서 유휴 시간 초과를 처리하려는 경우 유용합니다.
또한 유휴 타이머가 재설정 될 때마다 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
(간결성을 위해 메모리 정리 코드는 제외되었습니다.)
답변
스위프트 v 3.1
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<Int8>.self, capacity: Int(CommandLine.argc)) {argv in
_ = UIApplicationMain(CommandLine.argc, argv, NSStringFromClass(InterractionUIApplication.self), NSStringFromClass(AppDelegate.self))
}
다른 클래스에서 알림 관찰
NotificationCenter.default.addObserver(self, selector: #selector(someFuncitonName), name: Notification.Name.TimeOutUserInteraction, object: nil)
답변
이 스레드는 큰 도움이되었으며 알림을 보내는 UIWindow 하위 클래스로 묶었습니다. 실제로 느슨한 결합을 만들기 위해 알림을 선택했지만 대리자를 쉽게 추가 할 수 있습니다.
요점은 다음과 같습니다.
또한 UIApplication 서브 클래스 문제의 원인은 NIB가 애플리케이션과 델리게이트를 포함하므로 2 개의 UIApplication 오브젝트를 작성하도록 설정 되었기 때문입니다. UIWindow 서브 클래스는 훌륭하게 작동합니다.
답변
실제로 서브 클래 싱 아이디어는 훌륭합니다. 델리게이트를 UIApplication
서브 클래스로 만들지 마십시오 . 상속 된 다른 파일 UIApplication
(예 : myApp)을 만듭니다. IB에서 fileOwner
객체 의 클래스를 myApp
myApp.m으로 설정하고 sendEvent
위와 같이 메소드를 구현하십시오 . main.m에서 :
int retVal = UIApplicationMain(argc,argv,@"myApp.m",@"myApp.m")
et voilà!
답변
방금 동작에 의해 제어되는 게임, 즉 화면 잠금이 비활성화되었지만 메뉴 모드에있을 때 다시 활성화해야하는 게임에서이 문제가 발생했습니다. 타이머 대신 setIdleTimerDisabled
다음 메소드를 제공하는 작은 클래스 내로의 모든 호출을 캡슐화했습니다 .
- (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
AppDelegate의 applicationWillResignActive
메소드 에서 호출됩니다 .
기사를 작성하고 iPhone 게임에서 싱글 톤 클래스 IdleTimerManager 유휴 타이머 처리에 대한 코드를 제공했습니다.
답변
활동을 감지하는 다른 방법은 다음과 같습니다.
에 타이머가 추가되어 활동 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];