Frage

Hat jemand implementiert eine Funktion, wo, wenn der Benutzer den Bildschirm für einen bestimmten Zeitraum nicht berührt hat, nehmen Sie eine bestimmte Aktion? Ich versuche, den besten Weg, um herauszufinden, das zu tun.

Es ist die etwas bezogene Methode in UIApplication:

[UIApplication sharedApplication].idleTimerDisabled;

Es wäre schön, wenn Sie stattdessen so etwas wie dies hat:

NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed;

Dann könnte ich einen Timer einstellen und regelmäßig diesen Wert überprüfen, und einige Maßnahmen ergreifen, wenn es einen Schwellenwert überschreitet.

Hoffentlich, das erklärt, was ich suche. Hat jemand dieses Problem bereits in Angriff genommen, oder haben keine Gedanken darüber, wie Sie es tun würde? Danke.

War es hilfreich?

Lösung

Hier ist die Antwort, die ich gesucht hatte:

Haben Sie Ihre Anwendung delegieren Unterklasse UIApplication. In der Implementierungsdatei überschreibt die sendevent: Methode wie folgt:

- (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");
}

wo MaxIdleTime und idleTimer sind Instanzvariablen.

Damit dies funktioniert, müssen Sie auch Ihre main.m ändern UIApplicationMain zu sagen, Ihre Stellvertretung Klasse zu verwenden (in diesem Beispiel AppDelegate) als Hauptklasse:

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

Andere Tipps

Ich habe eine Variation der Leerlaufzeitgeber-Lösung, die nicht Subklassifizieren UIApplication erfordern. Es funktioniert auf einer bestimmten UIViewController Unterklasse, so nützlich ist, wenn Sie nur einen View-Controller haben (wie eine interaktive App oder ein Spiel haben kann) oder wollen nur Idle Timeout in einem bestimmten View-Controller behandeln.

Es ist auch nicht neu erstellen NSTimer Objekt jedes Mal, wenn der Leerlaufzeitgeber zurückgesetzt wird. Es schafft nur eine neue, wenn der Timer ausgelöst wird.

Ihr Code kann resetIdleTimer für alle anderen Ereignisse aufrufen, die den Leerlaufzeitgeber (wie signifikante Beschleunigungsmesser-Eingang).

müssen möglicherweise ungültig
@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

(Speicher der Kürze halber ausgeschlossen Bereinigungscode.)

Für die schnell v 3.1

Dont't vergessen, diese Linie in AppDelegate Kommentar // @ 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-Datei erstellen, und fügen Sie diesen (Name wichtig ist)

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

Die Beobachtung Benachrichtigung in einer anderen Klasse

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

Dieser Thread war eine große Hilfe, und ich wickelte es in eine UIWindow Unterklasse auf, die Benachrichtigungen aussendet. Ich entschied mich für Benachrichtigungen es eine echte lose Kopplung zu machen, aber Sie können einen Delegaten leicht genug, um hinzuzufügen.

Hier ist der Kern:

http://gist.github.com/365998

Auch ist der Grund für das UIApplication Unterklasse Problem, dass die NIB-Setup ist dann 2 UIApplication Objekte zu erstellen, da es die Anwendung und die Delegaten enthält. UIWindow Unterklasse funktioniert allerdings groß.

Ich lief in dieses Problem mit einem Spiel, das durch Bewegungen gesteuert wird, das heißt hat Bildschirm deaktiviert sperren, aber soll es wieder aktivieren, wenn im Menü-Modus. Statt eines Timer verkapselt ich alle Anrufe innerhalb einer kleinen Klasse bietet die folgenden Methoden 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 deaktiviert Idletimers, enableIdleTimerDelayed wenn das Menü Eingabe oder was auch immer mit Leerlaufzeitgeber aktiv und enableIdleTimer heißt von Ihrem AppDelegate des applicationWillResignActive Methode, um sicherzustellen, alle Änderungen zurückgesetzt richtig an das System Standardverhalten ausgeführt werden soll.
Ich schrieb einen Artikel und lieferte den Code für die Singletonklasse IdleTimerManager Handhabung Idle Timer in iPhone Games

Hier ist eine andere Art und Weise Aktivitäten zu erkennen:

Der Timer wird in UITrackingRunLoopMode hinzugefügt, so kann es nur Feuer, wenn es UITracking Aktivität. Es hat auch den schönen Vorteil, nicht Sie für alle Touch-Ereignisse Spamming, so zu informieren, wenn es in den letzten ACTIVITY_DETECT_TIMER_RESOLUTION Sekunden Aktivität ist. Ich nannte den Wähler keepAlive, da es einen entsprechenden Anwendungsfall dafür scheint. Sie können natürlich tun, was Sie mit den Informationen wünschen, dass es Aktivität vor kurzem.

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

Tatsächlich ist die Subklassifizieren Idee funktioniert super. Nur nicht Ihre Stellvertretung der UIApplication Unterklasse machen. Erstellen Sie eine andere Datei, die von UIApplication erbt (zum Beispiel myApp). In IB setzen die Klasse des fileOwner Objekt myApp und in myApp.m die sendEvent Verfahren wie oben implementieren. In main.m tun:

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

et voilà!

Schließlich müssen Sie festlegen, was Sie als müßig sein - ist das Ergebnis des Benutzers im Leerlauf nicht den Bildschirm zu berühren oder ist es der Zustand des Systems, wenn keine Rechenressourcen werden verwendet? Es ist möglich, in vielen Anwendungen für die Benutzer etwas zu tun, auch wenn sie nicht aktiv mit dem Gerät über den Touchscreen interagieren. Während der Benutzer wohl vertraut mit dem Konzept der Einrichtung ist dem Einschlafen und dem Hinweis, dass es über Bildschirm Dimmen passieren wird, ist es nicht unbedingt der Fall, dass sie etwas erwarten werde geschehen, wenn sie untätig sind - müssen Sie vorsichtig sein, über das, was Sie tun würden. Aber kommen wir zurück auf die ursprüngliche Aussage - wenn Sie den ersten Fall sollten Sie Ihre Definition sein, gibt es keine wirklich einfache Möglichkeit, dies zu tun. Sie müssten jedes Berührungsereignis erhalten, ist es entlang auf der Antwortkette vorbei nach Bedarf, während der Zeit der Feststellung, das sie empfangen wurde. Das gibt Ihnen eine gewisse Grundlage für die Herstellung der Leerlaufberechnung. Wenn Sie den zweiten Fall sollten Sie Ihre Definition sein, können Sie mit einer NSPostWhenIdle Benachrichtigung spielen, um zu versuchen und Ihre Logik zu dieser Zeit durchführen.

Es gibt einen Weg, um diese App breit zu machen, ohne dass einzelne Controller mit etwas zu tun. Fügen Sie einfach einen Gestenerkenner, die nicht berührt nicht gelöscht werden. Auf diese Weise werden alle Noten für den Timer verfolgt werden und andere Berührungen und Gesten sind gar nicht so niemand betroffen sonst darüber wissen muss.

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)
    }
}

In der App Delegierten tat Finish Startmethode, rufen Sie einfach addGesture und du bist fertig. Alle Noten werden die CatchAllGesture Methoden durchlaufen, ohne dass die Funktionalität der anderen verhindern.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top