HMDT - Logic and Intuition -

about HMDT

Cocoa Programming Tips 1001

Application Kit

NSScrollView

Application Kit - NSScrollView

スクロールビューの構造

Keywords: content view, document view

NSScrollView は、他の View を Subview として取り込んで、スクロールバー付きで表示できる View だ。その構造を調べてみよう。

例として、Interface Builder でスクロールビューを作ってみる。Custom View を貼付けて、[Layout] -> [Make subviews of] -> [Scroll View] で作ることができるんだ。

さて、このとき View 階層はどうなっているか?調べてみると、こんな感じになっている。

  • NSScrollView
    • NSClipView
      • NSView
    • NSScroller
    • NSScroller

NSClipView の中に NSView があるのが分かるよね。NSView が、実際にスクロールされる View だ。

で、Scroll View では、この NSClipView を Content View、スクロールされる View を Document View って呼んでいるんだ。それぞれ、contentViewdocumentView で取り出すことができる。

Application Kit/NSScrollView.h

- (NSClipView *)contentView;
- (id)documentView;

contentView は NSClipView を返しているのに対して、documentView は id 型で返しているのに注意。

あと、他に含まれている 2 つの NSScroller は、縦と横のスクロールバーだね。

Application Kit - NSScrollView

左上固定のスクロールビュー

Keywords: NSClipView, isFlipped

NSScrollViewを使えば、簡単にスクロールバーつきのビューを実現できる。だけど、落とし穴が一つ。Cocoaの座標系は原点左下なので、スクロールビューも左下固定になってしまうのだ。これは使いにくい。やっぱり、左上固定にしてほしいよね。

NSViewには、原点を左下から左上に変更するために、isFlippedというメソッドが用意されている。おそらく、これを上書きすればいいんだけど、さてどのクラスでやればいいのか?

まず、documentViewでやる方法がある。documentViewになるクラスで、isFlippedを上書きしてYESを返すようにする。これで、左上固定になる。

ただし、documentViewが左下原点を前提に作られていたり、反転するとなにかと不具合がおこる場合もあるだろう。というわけで、ここではcontentViewで上書きする方法をやってみる。NSScrollViewのcontentViewである、NSClipViewのサブクラスを作る。このサブクラスで、isFlippedを上書きするのだ。

サブクラスを作る事自体は簡単だ。ただ、これをNSScrollViewに埋め込むのがちょっと面倒。普通に作ると、NSScrollViewは通常のNSClipViewを使う。そこで、初期化が終わった段階で、NSClipViewを新たに作ったサブクラスでスワップしてやることにする。

実際にやってみよう。まず、NSClipViewのサブクラスFlippedClipViewを作る。このクラスではisFlippedを上書きして、YESを返す。これで、左上が原点になる。

FlippedClipView.m (sample)

- (BOOL)isFlipped
{
    return YES;
}

次に、アプリケーションのコントローラとなるクラスの中で、contentViewのスワップを行う。こんな感じで。

AppController.m (sample)

- (void)awakeFromNib
{
    // Swap clip view
    NSClipView*         oldClipView;
    FlippedClipView*    newClipView;
    oldClipView = [_scrollView contentView];
    newClipView = [[FlippedClipView alloc] initWithFrame:[oldClipView frame]];
    [newClipView setDrawsBackground:[oldClipView drawsBackground]];
    [newClipView setBackgroundColor:[oldClipView backgroundColor]];
    [newClipView setDocumentView:[oldClipView documentView]];
    
    // Move subviews
    NSEnumerator*   enumerator;
    NSView*         subview;
    enumerator = [[oldClipView subviews] objectEnumerator];
    while (subview = [enumerator nextObject]) {
        [subview removeFromSuperview];
        [newClipView addSubview:subview];
    }
    
    // Set content view
    [_scrollView setContentView:newClipView];
    
    [newClipView release];
}

ソースコード中でFlippedClipeViewのインスタンスを作っている。ポイントは3つ。1つは、古いNSClipViewの設定をすべて移す事。これらの設定は、Interface Builderで行われている。2つは、サブビューも移動してやる事。そして3つは、setContentViewとsetDocumentViewを呼び出して、関連付けを行っておくこと。

こうしておけば、documentViewに関わらず、左上固定のスクロールビューを実現することができる。

■サンプルダウンロード:
LeftUpperScroll.zip

back to top content

Copyright © 2002-2006 HMDT. All rights reserved.