2012-05-23 5 views
5

iOSでSMPTEタイムコード(HH:MM:SS:FF)用のカウントダウンタイマーを作成しようとしています。基本的には、33.33333msの分解能を持つカウントダウンタイマーです。私はNSTimerがこのタイマーを作成するイベントを発生させるのに十分正確であるかどうかはわかりません。このタイマーが増減するたびに、イベントを発生させたり、コードを呼びたいと思います。Objective-C/iOSで正確なタイマーイベントを作成するにはどうすればよいですか?

私はObjective-Cを初めて使うので、私はコミュニティから知恵を探しています。誰かが専門家の助言を求めてCADisplayLinkクラスを提案しました。

答えて

12

CADisplayLinkをお試しください。リフレッシュレート(60 fps)で起動します。

これは2フレームごとに発生します。これは1秒間に30回、あなたの後ろにあるようです。

これはビデオフレーム処理に関連しているので、コールバックで作業をすばやく行う必要があります。

+0

_frameInterval_はiOS 10.0では廃止予定でしたので、_preferredFramesPerSecond_を使用してください: 'displayLink.preferredFramesPerSecond = 30.0;' for 30fps – WongWray

1

あなたはiOSの4+をターゲットにしている場合は、グランドセントラル派遣を使用することができます。

// Set the time, '33333333' nanoseconds in the future (33.333333ms) 
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 33333333); 
// Schedule our code to run 
dispatch_after(time, dispatch_get_main_queue(), ^{ 
    // your code to run here... 
}); 

これは33.333333ms後にそのコードを呼び出します。

void DoWork(void *context); 

void ScheduleWork() { 
    // Set the time, '33333333' nanoseconds in the future (33.333333ms) 
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 33333333); 
    // Schedule our 'DoWork' function to run 
    // Here I pass in NULL for the 'context', whatever you set that to will 
    // get passed to the DoWork function 
    dispatch_after_f(time, dispatch_get_main_queue(), NULL, &DoWork); 
} 

void DoWork(void *context) { 
    // ... 
    // Do your work here, updating an on screen counter or something 
    // ... 

    // Schedule our DoWork function again, maybe add an if statement 
    // so it eventually stops 
    ScheduleWork(); 
} 

そして、あなたはタイマーを開始したいときにだけScheduleWork();を呼び出します。これはみかん取引ループになるだろうされている場合は、代わりにブロックの関数ポインタを使用して代わりにdispatch_after_f機能を使用する場合があります繰り返しループの場合、私は個人的にはこれが上記のブロックメソッドより少しきれいだと思っていますが、一度の作業ではブロックメソッドを確実に使用しています。

詳細については、Grand Central Dispatch docsを参照してください。

+1

'dispatch_after'は' NSTimer'より正確ではないと思います。実際、iOS 4では後者が前者の観点から実装されていることは確かです。 – benzado

+0

私は毎回新しいスレッドをスケジューリングするといくつかの不正確さを課すと思いますが、実際にはそれをテストしていないことを認めます。 IOSには少しのオーバーヘッドがあり、試してみる価値があります。 –

4

基本的には、NSTimerまたはdispatch_afterのいずれかの保証はありません。彼らはメインスレッドでトリガするコードをスケジュールしますが、他の何かが実行に時間がかかり、メインスレッドをブロックすると、タイマーは起動しません。

しかし、あなたは簡単にメインスレッド(非同期I/Oのみを使用)をブロックすることは避けられ、物事はかなり良くなるはずです。

あなたはタイマーコードで何をする必要があるのか​​正確には言いませんが、カウントダウンを表示するだけでよいのであれば、システム時間に基づいてSMPTE時間を計算する限り、タイマー間隔に基づいて経過する必要がある秒数ではありません。あなたがそうするなら、あなたはほぼ確実に漂って、実際の時間と同期しなくなります。代わりに、それに基づいて、すべての計算を行う、あなたの開始時刻を注意して:

// Setup 
timerStartDate = [[NSDate alloc] init]; 
[NSTimer scheduledTimer... 

- (void)timerDidFire:(NSTimer *)timer 
{ 
    NSTImeInterval elapsed = [timerStartDate timeIntervalSinceNow]; 
    NSString *smtpeCode = [self formatSMTPEFromMilliseconds:elapsed]; 
    self.label.text = smtpeCode; 
} 

今、あなたは関係なく、タイマーが起動される頻度正しいタイムコードが表示されません。 (タイマーが頻繁に発射されない場合、タイマーは更新されませんが、更新すると正確になります)。

CADisplayLinkを使用すると、メソッドが呼び出されます表示が更新されるのと同じくらい速くなります。換言すれば、それは有用であるが、速くはない。あなたが時間を表示しているなら、それはおそらく行く方法です。

+0

ありがとう!これをビデオフレームの伸張の時間基準として使用するため、NSTimerを使用して確実にディスプレイのクロックを調整することはできません。 CADisplayLinkの可能性は高いですが、最大速度(33ms)でロックするので、33msを超えてフレームを描画するのではなく、圧縮解除に時間がかかりすぎる場合は少なくなります。 –

+0

私はこのアプローチをdispatch_afterで使いました。ありがとう! –

関連する問題