NSScrollView

NSScrollView

Application Kit -NSScrollView

スクロールビューの構造

Keywords: content view, document view

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

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

makeSubviewsOf.jpg

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

・NSScrollView

・NSClipView

・NSView

・NSScroller

・NSScroller

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

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

Application Kit/NSScrollView.h

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

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

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



Application Kit - NSScrollView

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

Keywords: NSClipView, isFlipped

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

leftLower.jpg

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に関わらず、左上固定のスクロールビューを実現することができる。

leftUpper.jpg

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





Application Kit