如何在 NSScrollView 中控制 NSTableView 的绘制?
Posted
技术标签:
【中文标题】如何在 NSScrollView 中控制 NSTableView 的绘制?【英文标题】:How to control drawing of NSTableView in NSScrollView? 【发布时间】:2014-11-17 11:43:40 【问题描述】:无论水平滚动条的位置如何,如何让 NSTableView 始终显示相同的列?在最右边的可见列中,我有自定义单元格视图。我希望水平滚动条控制在这些自定义视图中绘制的内容。垂直滚动应该可以正常工作。
我尝试了几种方法,但都没有成功。例如,我可以控制水平滚动条的旋钮比例,方法是让表格视图更宽,或者让滚动视图认为它的文档视图实际上比它更宽。一种方法是继承 NSClipView 并覆盖 -documentRect,如下所示:
-(NSRect)documentRect
NSRect rect = [super documentRect];
rect.size.width += [[NSApp delegate] hiddenRangeWidth];
return rect;
但是,虽然滚动旋钮看起来应该是这样,并且我可以在不移动表格视图的情况下向右拖动它,但当我开始向另一个方向滚动时,旋钮会返回到左边缘。我也有无法让水平滚动条自动出现的问题。原始类也会发生这种情况,而不仅仅是我的自定义剪辑视图。这些问题有关系吗?
我还尝试用自定义视图替换文档视图,该自定义视图充当剪辑视图和表格视图之间的代理。它的 -drawRect: 调用 table view 的 -drawRect:。但是,没有绘制任何内容。我想这是因为表视图现在没有超级视图。如果表视图作为子视图添加到此代理视图中,它会随之移动。如何让它在水平轴上静止?
所以,重申一下:
-
使表格视图可滚动的最佳方法是什么,同时无论水平滚动条的位置如何,始终显示相同的列?
获取滚轮位置和旋钮比例的最佳方法是什么?我应该为 NSClipView 中的 NSViewBoundsDidChangeNotification 添加观察者吗?
【问题讨论】:
这听起来很奇怪。你能更详细地描述你想要达到的效果吗?要彻底。可能还有其他方法可以做到这一点...... 我使用绑定来填充表格。目前它有两列,右边的一列由包含 XY 图的自定义单元格视图组成。我使用 Core Plot 来绘制绘图。我希望能够像在普通表格视图中一样滚动表格行。但是,我想断开水平滚动条与表格视图的连接,并使用它来控制绘图范围。我有两个原因:1)左列很像行标题。 2)情节数据集巨大。我不想绘制任何不可见的东西,因为它会对性能产生有害影响。 您认为以编程方式将水平 NSScroller 添加为独立元素会更好吗?然后它将是 NSScrollView 的兄弟,我不需要更改滚动视图和剪辑视图的行为。 您是否尝试过并排放置两个表格视图?左边的不会显示滚动条。您将保持滚动视图垂直同步,如Synchronizing Scroll Views 中所述。 两个并排的表格视图肯定是实现行标题的一种方式。但是,最初的问题(如何断开水平滚动条与视图的连接,并使其控制单元格视图的绘制)仍然存在于右表视图中。 【参考方案1】:我终于设法通过让滚动视图和表格视图正常运行并添加一个 NSScroller 来解决问题。为了让滚动条更容易隐藏,我决定使用自动布局并将其添加到 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 需要更改布局,所以我还没有尝试过。
另一个问题是滚动条不会像在滚动视图中那样在角落里相互融合(见下文)。也许 NSScrollerStyleOverlay 也能解决这个问题。
【讨论】:
以上是关于如何在 NSScrollView 中控制 NSTableView 的绘制?的主要内容,如果未能解决你的问题,请参考以下文章
如何在放大的 NSScrollView 中修复 NSTextView 的奇怪大小