programing

iPhone: 마지막 화면 터치 이후 사용자 비활성/유휴시간

mailnote 2023. 5. 26. 22:09
반응형

iPhone: 마지막 화면 터치 이후 사용자 비활성/유휴시간

사용자가 일정 기간 동안 화면을 터치하지 않으면 특정 동작을 수행하는 기능을 구현한 사람이 있습니까?저는 그것을 하는 가장 좋은 방법을 찾으려고 노력하고 있습니다.

UIA 애플리케이션에는 다음과 같은 다소 관련된 방법이 있습니다.

[UIApplication sharedApplication].idleTimerDisabled;

대신 다음과 같은 것이 있으면 좋을 것입니다.

NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed;

그런 다음 타이머를 설정하고 주기적으로 이 값을 확인하고 임계값을 초과할 경우 조치를 취할 수 있습니다.

그게 제가 찾고 있는 것을 설명해주길 바랍니다.이 문제를 이미 해결한 사람이 있거나 어떻게 해결할 것인지에 대해 생각해 본 사람이 있습니까?감사해요.

제가 찾던 답은 다음과 같습니다.

애플리케이션 대리자가 하위 클래스 UIA 애플리케이션을 사용하도록 합니다.구현 파일에서 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");

저는 UIA 애플리케이션을 하위 분류할 필요가 없는 유휴 타이머 솔루션의 변형을 가지고 있습니다.특정 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

(간단한 설명을 위해 메모리 정리 코드는 제외됨)

swift v 3.1의 경우

AppDelegate //@에서 이 행에 대해 언급하는 것을 잊지 마십시오.UIA 응용 프로그램 기본

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 하위 클래스는 잘 작동합니다.

개별 컨트롤러가 아무것도 하지 않아도 이 앱을 광범위하게 수행할 수 있는 방법이 있습니다.터치를 취소하지 않는 제스처 인식기를 추가하기만 하면 됩니다.이렇게 하면 모든 터치가 타이머에 대해 추적되고 다른 터치와 제스처는 전혀 영향을 받지 않으므로 다른 사람이 알 필요가 없습니다.

fileprivate var timer ... //timer logic here

@objc public class CatchAllGesture : UIGestureRecognizer {
    override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesBegan(touches, with: event)
    }
    override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        //reset your timer here
        state = .failed
        super.touchesEnded(touches, with: event)
    }
    override public func touchesMoved(_ touches: Set<UITouch>, 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의 메서드를 거치며 다른 메서드의 기능을 방해하지 않습니다.

사실 하위 분류 아이디어는 아주 효과적입니다.단지 당신의 대리인을 그 사람으로 만들지 마세요.UIApplication아의서상속다파만른들에서 상속받은 UIApplication(예: 내 앱).에서 IB의 합니다.fileOwner…에 이의 myApp에서 그고내 앱을 합니다.에서 실행합니다.sendEvent상기와 같은 방법주 .m 작업:

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

et voila!

방금 모션으로 제어되는 게임에서 이 문제가 발생했습니다. 즉, 화면 잠금이 비활성화되었지만 메뉴 모드일 때 다시 활성화해야 합니다.는 타이머 했습니다.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 및 idle timer active로 할 때 합니다.enableIdleTimer앱대로호출다에서 됩니다.applicationWillResignActive모든 변경 사항이 시스템 기본 동작으로 올바르게 재설정되도록 하는 방법입니다.
iPhone Games에서 싱글톤 클래스 IdleTimerManager IdleTimerManager IdleTimer Handling을 위한 코드를 작성하여 제공하였습니다.

활동을 탐지하는 다른 방법은 다음과 같습니다.

타이머는 다음에 추가됩니다.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];

궁극적으로 유휴 상태로 간주하는 것을 정의해야 합니다. 유휴 상태는 사용자가 화면을 터치하지 않아서 발생하는 것입니까? 아니면 컴퓨팅 리소스가 사용되지 않는 경우 시스템 상태입니까?터치 스크린을 통해 기기와 능동적으로 상호 작용하지 않더라도 많은 응용 프로그램에서 사용자가 무언가를 수행할 수 있습니다.사용자는 장치가 절전 모드로 전환되는 개념과 화면 조광을 통해 절전 모드로 전환되는 것에 대해 잘 알고 있을 수 있지만, 유휴 상태일 경우 반드시 무언가가 발생할 것이라고 예상하는 것은 아닙니다. 사용자는 무엇을 할지 주의해야 합니다.하지만 원래의 진술로 돌아가서 - 만약 당신이 첫 번째 사례를 당신의 정의로 여긴다면, 이것을 할 수 있는 정말 쉬운 방법은 없습니다.각 터치 이벤트를 수신하여 수신 시간을 기록하면서 필요에 따라 응답기 체인에 전달해야 합니다.그러면 유휴 계산을 할 수 있는 근거를 얻을 수 있습니다.두 번째 사례를 정의한다면 NSPost를 사용할 수 있습니다.해당 시간에 논리를 수행하기 위해 알림을 처리할 때.

Outside는 2021년이며 UIA 애플리케이션을 확장하지 않고 이를 처리하기 위한 접근 방식을 공유하고자 합니다.타이머를 만들고 재설정하는 방법에 대해서는 설명하지 않겠습니다.하지만 오히려 모든 사건을 잡는 방법.따라서 AppDelegate는 다음과 같이 시작합니다.

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

따라서 UIWindow를 하위 클래스로 분류하고 재정의하기만 하면 됩니다.sendEvent아래와 같이

import UIKit

class MyWindow: UIWindow {

    override func sendEvent(_ event: UIEvent){
        super.sendEvent(event)
        NSLog("Application received an event. Do whatever you want")
    }
}

나중에 클래스와 함께 창 만들기:

self.window = MyWindow(frame: UIScreen.main.bounds)

언급URL : https://stackoverflow.com/questions/273450/iphone-detecting-user-inactivity-idle-time-since-last-screen-touch

반응형