1

私は第4版に取り組んでいます。Hillegass/Preble Cocoa の本を読んでいて、Core Animation で理解できない問題にぶつかりました。

具体的には、おそらく一度に 1 つずつ、レイヤー ホスティング ビューに画像のリストを表示する次のループがあります。期待される結果は、呼び出されたときに各画像が一度に 1 つずつ飛び出すことpresentImageです。実際の結果は、ループが完了するまでビューが空のままであり、その後、画像が一度に飛び出すことです。遅延中に、 への複数の呼び出しを示す複数のログ メッセージが表示されpresentImageます。これらのログ メッセージが停止し、ループが完了したことを示すと、すべての画像が一度に飛び出します。

// loop to present each image in list of URLs
// called at end of applicationDidFinishLaunching
NSTimeInterval t0 = [NSDate timeIntervalSinceReferenceDate];
for(id url in urls) {
    NSImage *img = [[NSImage alloc] initWithContentsOfURL:url];
    if(!img)
        continue;

    NSImage *thumbImg = [self thumbImageFromImage:img];

    [self presentImage:thumbImg];

    NSString *dt = [NSString stringWithFormat:@"%0.1fs",
                   [NSDate timeIntervalSinceReferenceDate]-t0];

    NSLog(@"Presented %@ at %@ sec",url,dt);
}

このthumbImageFromImageメソッドは、あなたが思っていることを実行します (より小さな画像を作成します)。のコードpresentImageは次のとおりです。

- (void)presentImage:(NSImage *)image
{
    CGRect superlayerBounds = view.layer.bounds;
    NSPoint center = NSMakePoint(CGRectGetMidX(superlayerBounds), CGRectGetMidY(superlayerBounds));

    NSRect imageBounds = NSMakeRect(0, 0, image.size.width, image.size.height);

    CGPoint randomPoint = CGPointMake(CGRectGetMaxX(superlayerBounds)*(double)random()/(double)RAND_MAX, CGRectGetMaxY(superlayerBounds)*(double)random()/(double)RAND_MAX);

    CAMediaTimingFunction *tf = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

    CABasicAnimation *posAnim = [CABasicAnimation animation];
    posAnim.fromValue = [NSValue valueWithPoint:center];
    posAnim.duration = 1.5;
    posAnim.timingFunction = tf;

    CABasicAnimation *bdsAnim = [CABasicAnimation animation];
    bdsAnim.fromValue = [NSValue valueWithRect:NSZeroRect];
    bdsAnim.duration = 1.5;
    bdsAnim.timingFunction = tf;

    CALayer *layer = [CALayer layer];
    layer.contents = image;
    layer.actions = [NSDictionary dictionaryWithObjectsAndKeys:posAnim,@"position", bdsAnim, @"bounds", nil];

    [CATransaction begin];
    [view.layer addSublayer:layer];
    layer.position = randomPoint;
    layer.bounds = NSRectToCGRect(imageBounds);
    [CATransaction commit];
}

観測された結果は、insideの呼び出しではなく、 [CATransaction begin]andの[CATransaction commit]呼び出しがループを囲んでいるかのようです。実際、内側からandを削除し、代わりにループを括弧で囲むと、同じ動作が一度にすべて観察されます。実際、 and の呼び出しを完全に削除すると、同じ動作が一度にすべて観察されます。foraddSublayerpresentImage[CATransaction begin][CATransaction commit]presentImagefor[CATransaction begin][CATransaction commit]

4

2 に答える 2

3

良い質問。演習の設計方法を考えると、あなたが見ているのは予想されることです。その理由は、レイヤーのアニメーション(および描画)がメインスレッドで行われるためです。メインスレッドは現在、画像の読み込みとサムネイルの作成に使用されています。すべての画像が読み込まれ、サムネイルが作成されるまで、メインスレッドは実際に要求したアニメーションの実行に取り掛かることができません。 CATransactionこのシングルスレッドのコンテキストでは、あまり影響はありません。

次の章(34、同時実行性)では、バックグラウンドスレッドで画像を準備することでこれを改善します(を使用NSOperationQueue)。これにより、メインスレッドが解放され、新しい画像が表示され、各画像がバックグラウンドで読み込まれてサイズ変更されるときにアニメーションが実行されます。

于 2012-04-15T00:39:40.270 に答える
0

さて、このバグを数週間私から解放した後、これが理にかなっているように見え、今では(ある程度)明白に見えるので、私がそれほど遠くないことを願っています。

上記の私の元の質問で与えられたコードから、メインスレッドはアニメーションを含むすべてのUI更新を担当しますが、メインスレッドはpresentImage完了するまでUIの更新を効果的にブロックされます。これはpresentImage、スレッドへの分割方法に関係なく当てはまります(34章のように)。

たとえば、presentImage上記のコードのようにボタンをループするのではなく、ボタンのターゲットとして呼び出した場合、各アニメーションはボタンをクリックするとすぐに開始され、クリックの速さ(またはそうでない)に関係なく、期待どおりに進行します。ボタン。これを行うと、私が期待するように(そして私が誤ってオリジナルから期待したように)複数の同時アニメーションが発生します。

公式の技術的な詳細を正確に把握したかどうかはわかりませんが、少なくとも答えとしては満足しています。

于 2012-05-04T00:44:00.467 に答える