2

Mac OS X 10.7 上の Cocoa ベースのアプリケーションに奇妙な問題があることに気付きました。何らかの理由で (ここでは重要ではありません)、カスタム ビューの drawRect メソッドの外側に描画する必要がある場合があります。

ビューで lockFocus/lockFocusIfCanDraw を呼び出し、現在のコンテキストを要求し、関数の CGContext ファミリ (CoreGrapchis) を使用して実際の描画を行い、最後に CGContextFlush を実行する必要があります (ウィンドウをフラッシュしたり、NSGraphicsContext クラス メソッドを使用して行うこともできます)。フラッシュ)。

この手順は、実際には NSView の -display メソッドを呼び出している場合と同じです。

問題は...「自然な」方法よりも3〜4倍遅いことです(setNeedsDisplayを呼び出すか、Cocoaがこれを行うように要求したときにdrawRectから描画します)。たとえば、ビューの setNeedsDisplay を単純に呼び出すことはできません。この「-display のような」機能が必要です。

テストの例 (タイマーを使用) では、簡単にするために、-display を呼び出しています (一般に、アプリが行うのと同じ作業を行うため) 対 -setNeedsDisplay で、「-display」のタイミングが 3- であることがわかります。 「-setNeedsDisplay」の 4 倍の長さ。

これが私の CustomView クラス (実装) の例です。

#import <QuartzCore/QuartzCore.h>

#import "CustomView.h"

@implementation CustomView
{
   CFTimeInterval startTime;
   NSTimer *timer;
   unsigned step;
}

- (id)initWithFrame:(NSRect)frame
{
    return [super initWithFrame : frame];
}

- (void)drawRect:(NSRect)dirtyRect
{
   CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];

   if(!timer)
   {
      CGContextSetRGBFillColor(ctx, 1., 1., 1., 1.);
      CGContextFillRect(ctx, dirtyRect);
   }
   else
   {
      CGContextSetRGBFillColor(ctx, 0., 0., 0., 1.);
      CGContextFillRect(ctx, CGRectMake(step * 1.5, 100, 2., 2.));
   }
}

- (void) mouseDown : (NSEvent *)theEvent
{
   if (!timer)
   {
      startTime = CACurrentMediaTime();
      timer = [NSTimer scheduledTimerWithTimeInterval : 0.006 target : self selector : @selector(handleTimer:) userInfo : nil repeats : YES];
      step = 0;
   }
}

- (void) handleTimer : (NSTimer *) dummy
{
   if(step < 200)
   {
      ++step;
#if 1
      [self display];
#else
      [self setNeedsDisplay : YES];
#endif
   }
   else 
   {
      [timer invalidate];
      timer = nil;
      NSLog(@"animation time is: %g", CACurrentMediaTime() - startTime);
   }
}

@end

CACurrentMediaTime が私の目的にとって本当に良い機能ではない場合でも、明らかな時間差を示すことができると思います (そして、測定なしで簡単に気付くことができます - 表示は本当に遅いです)。handleTimer メソッドには 2 つのセクションがあります。pp ディレクティブで「1」を「0」に変更すると、-display/-setNeedsDisplay の両方を試すことができます。したがって、たとえば、次の出力があります。

-表示: 3.32 秒。(?)

-setNeedsDisplay: 1.2 秒。

「Instruments」アプリによって作成されたコール ツリー/所要時間を見てみましたが、あまり役に立ちませんでした。

編集: うーん、私は今見ることができます:実際には、 setNeedsDisplay ビューはすべてのタイマーイベントで再描画されません!

4

1 に答える 1

0

drawRect メソッドで CG 関数にドロップダウンする必要はありません。

このコードは同等です:

- (void)drawRect:(NSRect)dirtyRect
{
   if(!timer)
   {
      [[NSColor whiteColor] set];
      NSRectFill(dirtyRect);
   }
   else
   {
      [[NSColor blackColor] set];
       NSRectFill(NSMakeRect(step * 1.5, 100.0, 2.0, 2.0));
   }
}

-display と -setNeedsDisplay に関しては、前者はすぐに描画を行い、後者はフラグを設定します。イベント ループを通過するたびに、そのフラグが true の場合、ウィンドウは問題のビューに -display を送信し、国旗。

もう 1 つ: NSTimer を使用してアニメーションを駆動するこのアプローチは、少し時代遅れです。この種のことを行う方法を学ぶには、Core Animation のドキュメントを読む必要があります。

于 2012-06-25T11:39:12.327 に答える