5

iOSでSMPTEタイムコード(HH:MM:SS:FF)のカウントダウンタイマーを作成したいと思っています。基本的には、33.33333msの分解能を持つ単なるカウントダウンタイマーです。NSTimerが、このタイマーを作成するためにイベントを起動することを期待できるほど正確であるかどうかはわかりません。このタイマーがインクリメント/デクリメントするたびに、イベントを発生させるか、コードを呼び出したいと思います。

私はObjective-Cを初めて使用するので、コミュニティからの知恵を探しています。誰かがCADisplayLinkクラスを提案し、専門家のアドバイスを探しています。

4

3 に答える 3

14

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

CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timerFired:)];
displayLink.frameInterval = 2;
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

これは2フレームごとに起動します。これは1秒あたり30回で、これはあなたが求めているもののようです。

これはビデオフレーム処理に関連しているため、コールバックでの作業を非常に迅速に行う必要があることに注意してください。

于 2012-05-23T11:41:16.967 に答える
5

基本的に、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を使用する場合、メソッドは表示が更新されるのと同じ速さで呼び出されます。言い換えれば、それが役立つのと同じくらい速く、しかし速くはありません。あなたが時間を表示しているなら、それはおそらく行く方法です。

于 2012-05-23T07:31:40.457 に答える
2

iOS 4以降をターゲットにしている場合は、GrandCentralDispatchを使用できます。

// 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後にそのコードが呼び出されます。これがループソート取引になる場合はdispatch_after_f、ブロックの代わりに関数ポインターを使用する代わりに関数を使用することをお勧めします。

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();、タイマーを開始したいときに電話するだけです。繰り返しループの場合、これは上記のブロック方式よりも少しクリーンだと個人的には思いますが、1回限りのタスクでは、ブロック方式の方が間違いなく好きです。

詳細については、GrandCentralDispatchのドキュメントをご覧ください。

于 2012-05-23T07:09:03.443 に答える