154

ユーザーが一定時間画面に触れていない場合に特定のアクションを実行する機能を実装した人はいますか? 私はそれを行うための最良の方法を見つけようとしています。

UIApplication には、このやや関連するメソッドがあります。

[UIApplication sharedApplication].idleTimerDisabled;

代わりに次のようなものがあればいいでしょう。

NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed;

次に、タイマーを設定して定期的にこの値をチェックし、しきい値を超えたときに何らかのアクションを実行できます。

うまくいけば、それは私が探しているものを説明しています。誰かがすでにこの問題に取り組んでいますか、またはどのように行うかについて何か考えがありますか? ありがとう。

4

10 に答える 10

156

これが私が探していた答えです:

アプリケーションで 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 を変更して、デリゲート クラス (この例では AppDelegate) をプリンシパル クラスとして使用するよう UIApplicationMain に指示する必要があります。

int retVal = UIApplicationMain(argc, argv, @"AppDelegate", @"AppDelegate");
于 2008-11-21T17:20:34.183 に答える
88

UIApplication のサブクラス化を必要としないアイドル タイマー ソリューションのバリエーションがあります。これは特定の UIViewController サブクラスで動作するため、ビュー コントローラーが 1 つしかない場合 (インタラクティブなアプリやゲームのように)、または特定のビュー コントローラーでアイドル タイムアウトのみを処理したい場合に便利です。

また、アイドル タイマーがリセットされるたびに 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

(簡潔にするために、メモリ クリーンアップ コードは除外されています。)

于 2011-03-11T06:54:03.587 に答える
12

このスレッドは非常に役に立ちました。通知を送信する UIWindow サブクラスにまとめました。本当の疎結合にするために通知を選択しましたが、デリゲートを簡単に追加できます。

要点は次のとおりです。

http://gist.github.com/365998

また、UIApplication サブクラスの問題の理由は、アプリケーションとデリゲートが含まれているため、NIB が 2 つの UIApplication オブジェクトを作成するようにセットアップされているためです。ただし、UIWindow サブクラスはうまく機能します。

于 2010-04-14T16:14:37.950 に答える
5

実際、サブクラス化のアイデアはうまく機能します。デリゲートをUIApplicationサブクラスにしないでください。から継承する別のファイルを作成しますUIApplication(例: myApp)。IB でfileOwnerオブジェクトのクラスをmyAppmyApp.m に設定し、sendEvent上記のようにメソッドを実装します。main.m で次のことを行います。

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

ほら!

于 2010-08-27T01:16:32.053 に答える
4

モーションによって制御されるゲーム、つまり画面ロックが無効になっているゲームでこの問題に遭遇しましたが、メニューモードでは再度有効にする必要があります。タイマーの代わりに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メニューに入るとき、またはアイドルタイマーをアクティブにして実行する必要があるものは何でも、アイドルタイマーを非アクティブにenableIdleTimerし、AppDelegateのapplicationWillResignActiveメソッドから呼び出されて、すべての変更がシステムのデフォルト動作に適切にリセットされるようにします。
私は記事を書き、シングルトン クラス IdleTimerManager Idle Timer Handling in iPhone Gamesのコードを提供しました。

于 2013-02-06T08:57:59.993 に答える
4

アクティビティを検出する別の方法を次に示します。

タイマーは に追加されているため、アクティビティ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];
于 2014-08-13T18:32:28.490 に答える
3

最終的には、アイドル状態と見なすものを定義する必要があります。ユーザーが画面に触れなかった結果としてアイドル状態になるのでしょうか、それともコンピューティングリソースが使用されていない場合のシステムの状態でしょうか。多くのアプリケーションでは、タッチスクリーンを介してデバイスとアクティブに対話していなくても、ユーザーが何かをしている可能性があります。ユーザーはおそらく、デバイスがスリープ状態になるという概念と、画面の調光によって発生するという通知に精通していると思いますが、アイドル状態の場合に何かが発生することを期待しているとは限りません。注意が必要です。あなたが何をするかについて。しかし、元のステートメントに戻ります。最初のケースを自分の定義と見なす場合、これを行う簡単な方法はありません。各タッチイベントを受信する必要がありますが、受信した時刻を記録しながら、必要に応じてレスポンダーチェーンに渡します。これにより、アイドル計算を行うための基礎が得られます。2番目のケースを定義と見なす場合は、NSPostWhenIdle通知を試して、その時点でロジックを実行してみてください。

于 2008-11-07T21:15:08.340 に答える