表示パスを更新する【実践的Macintoshプログラミング解説】

印刷用表示 |テキストサイズ 小 |中 |大 |

CoreData版 Repeating Motif Generator の開発 Repeating Motif Wonderland CoreData 実践的 Macintosh プログラミング解説

LinkIconホーム

更新日 2009-05-24

ビューを作る

表示パスを更新する

 データパスを元にしてビューの大きさに合わせた表示パスを作り出す処理を実装します。

データパスから表示パスを作る

 まず今ある表示パスを捨てて、データパスをコピーします。

 ベジエパスの変形にはアフィン変換を使います。アフィン変換を使うと図形の平行移動、拡大・縮小、そして回転操作をする事ができます(ちなみに反転操作は-1倍の拡大と考えれば実現できます)。
 NSAffineTransformクラスのオブジェクトに変換操作を登録し、全て登録したらtransformUsingAffineTransform:メソッドを使って、ベジエパスに変換を適用します。ここで注意すべきは変換操作の登録は逆順に登録しなければいけないという事です。

 最後にsetNeedsDisplay:メソッドでビュー全体の更新が必要であるという印を付けておきます。これでビューが再描画されて、今作った表示パスが画面に表示される事になります。

//
//  OscillatorView.m
//

#define     AMPLITUDE_MAX   100

#pragma mark -
#pragma mark Path

- (void)updateDisplayPath
{
    NSAffineTransform   *at = [NSAffineTransform transform];

    [displayPath release];
    displayPath = [dataPath copy];

    //  変換操作を逆順で登録する
    //      ベジエパスの中心をフレームの中心に移動
    [at translateXBy:[self bounds].size.width/2
                 yBy:[self bounds].size.height/2]; 
    //      サイズをフィットさせる
    [at scaleXBy:([self bounds].size.width-5)/[displayPath bounds].size.width
             yBy:([self bounds].size.height-5)/(AMPLITUDE_MAX*2.0)];
    //      ベジエパスの中心を原点に移動
    [at translateXBy:-NSMidX([displayPath bounds])
                 yBy:-NSMidY([displayPath bounds])];

    [displayPath transformUsingAffineTransform:at];
    [self setNeedsDisplay:YES];
}

イベントに応答する

 再表示が必要になるとNSViewには描画環境を整えた上でdrawRect:メッセージが送られますので、このメソッドに描画コードを実装します。 
 ここではまずビューの外枠を表示します。そして表示パスを描画するのですが、リサイズ中は描画を抑制します。これは処理を軽くするためです。

 さて、再描画が必要になるのは

  1.データパスが変更された時
  2.ビューのサイズが変わったとき

の二つのケースが考えられます。データパスが変更された時の処理は、既にアクセサメソッドで説明しました。ビューのサイズが変わった時はイニシャライザで説明した様に通知を受け取って対応します。通知を受け取るメソッドはframeDidChange:です。
 frameDidChange:が呼ばれる時はビューのサイズが変わっているのでupdateDisplayPathメソッドを呼んで表示パスを更新します。これで再描画まで実行されます。ただしリサイズ中は表示パスの更新を抑制します。

 これだけだとリサイズが終わった時に原図が描画されません。これに対処するにはviewDidEndLiveResizeというメッセージを受けた時にupdateDisplayPathを呼び出せばOKです。

//
//  OscillatorView.m
//

#pragma mark -
#pragma mark Event Handler

- (void)drawRect:(NSRect)rect
{
    [[NSBezierPath bezierPathWithRect:[self bounds]] stroke]; 

    if(![self inLiveResize])
        [displayPath stroke];
}

- (void)viewDidEndLiveResize
{
    [self updateDisplayPath];
    [super viewDidEndLiveResize];
}

- (void)frameDidChange:(NSNotification *)aNotification
{
    if(![self inLiveResize]) 
        [self updateDisplayPath];
}