[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 하위 클래스로 묶었습니다. 실제로 느슨한 결합을 만들기 위해 알림을 선택했지만 대리자를 쉽게 추가 할 수 있습니다.

요점은 다음과 같습니다.

http://gist.github.com/365998

또한 UIApplication 서브 클래스 문제의 원인은 NIB가 애플리케이션과 델리게이트를 포함하므로 2 개의 UIApplication 오브젝트를 작성하도록 설정 되었기 때문입니다. UIWindow 서브 클래스는 훌륭하게 작동합니다.


답변

실제로 서브 클래 싱 아이디어는 훌륭합니다. 델리게이트를 UIApplication서브 클래스로 만들지 마십시오 . 상속 된 다른 파일 UIApplication(예 : myApp)을 만듭니다. IB에서 fileOwner객체 의 클래스를 myAppmyApp.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];
}

disableIdleTimerenableIdleTimerDelayed메뉴로 들어갈 때 유휴 타이머를 비활성화 하거나 유휴 타이머가 활성화 된 상태에서 실행해야하는 모든 항목을 변경하고 모든 변경 사항이 시스템 기본 동작으로 올바르게 재설정되도록 enableIdleTimerAppDelegate의 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];