スクロール ビューとテーブル ビューを正常に動作させ、NSScroller を追加することで、最終的に問題を解決することができました。スクローラーを簡単に非表示にするために、Auto Layout を使用して Interface Builder に追加することにしました。(オブジェクト ライブラリにはスクローラーが含まれていませんが、カスタム ビューを追加して、そのクラスを NSScroller に設定できます。) スクローラーの高さを制約として設定し、スクローラーと制約をコードのアウトレットにバインドします。
@property (nonatomic, retain) IBOutlet NSScroller *scroller;
@property (nonatomic, unsafe_unretained) IBOutlet NSLayoutConstraint *scrollerHeightConstraint;
これで、必要に応じてスクローラーを表示または非表示にすることができます。
if (_zoomedIn) {
_scrollerHeightConstraint.constant = [NSScroller scrollerWidthForControlSize:NSRegularControlSize scrollerStyle:NSScrollerStyleOverlay];
[_scroller setKnobProportion:(_visibleRange / _maxVisibleRange)];
[_scroller setDoubleValue:_visibleRangePosition];
[_scroller setEnabled:YES];
} else {
_scrollerHeightConstraint.constant = 0.0;
}
ここで、プロパティ visibleRange、maxVisibleRange、および visibleRangePosition は、それぞれ可視範囲の長さ (スクローラー ノブで表される)、合計範囲 (スクローラー スロットで表される)、および可視範囲の開始 (ノブの位置) です。これらは、スクローラーの送信アクションを Interface Builder の次のメソッドにバインドすることで読み取ることができます。
- (IBAction)scrollAction:(id)sender {
switch (self.scroller.hitPart) {
case NSScrollerNoPart:
break;
case NSScrollerDecrementPage:
_visibleRangePosition = MAX(_visibleRangePosition - _visibleRange / _maxVisibleRange, 0.0);
self.scroller.doubleValue = _visibleRangePosition;
break;
case NSScrollerIncrementPage:
_visibleRangePosition = MIN(_visibleRangePosition + _visibleRange / _maxVisibleRange, 1.0);
self.scroller.doubleValue = _visibleRangePosition;
break;
case NSScrollerKnob:
case NSScrollerKnobSlot:
_visibleRangePosition = self.scroller.doubleValue;
break;
default:
NSLog(@"unsupported scroller part code %lu", (unsigned long)self.scroller.hitPart);
}
// Make the custom cell views draw themselves here.
}
ジェスチャでスクロールを機能させるには、カスタム セル ビュー クラスに -scrollWheel: を実装する必要があります。
- (void)scrollWheel:(NSEvent *)event {
if (event.deltaX != 0.0) {
NSScroller *scroller = appDelegate.scroller;
if (scroller.isEnabled) {
double delta = event.deltaX / (NSWidth(scroller.bounds) * (1.0 - scroller.knobProportion));
scroller.doubleValue = MIN(MAX(scroller.doubleValue - delta, 0.0), 1.0);
}
}
if (event.deltaY != 0.0) {
[self.nextResponder scrollWheel:event];
}
}
イベントをスクローラーに渡すことができたと思ったのですが、どうやらイベントを処理していないようです。上記のコードは跳ね返りを処理していないようで、勢いスクロールは常に機能するとは限りません。ノブが動きの途中で止まることがあります。これは、スクローラーのスタイルがデフォルトで NSScrollerStyleLegacy になっていることに関係していると思います。NSScrollerStyleOverlay に設定すると、レイアウトの変更が必要になるため、まだ試していません。
もう 1 つの問題は、スクロール ビューの場合のように、隅でスクローラーが互いに混ざり合わないことです (以下を参照)。多分 NSScrollerStyleOverlay もこれを修正するでしょう。
