如何在更改比例时保持NSScrollView中的滚动位置?

时间:2009-08-18 22:29:56

标签: cocoa scroll zoom nsscrollview

我有一个NSScrollView(myScrollView)包装的NSView(myView)。使用放大/缩小按钮,用户可以改变myView的比例。如果用户当前滚动到myView中的特定位置,我希望在缩放后将该部分视图保留在屏幕上。

我的代码看起来像这样:

    // preserve current position in scrollview
    NSRect oldVisibleRect = [[myScrollView contentView] documentVisibleRect];
    NSPoint oldCenter = NSPointFromCGPoint(CGPointMake(oldVisibleRect.origin.x + (oldVisibleRect.size.width  / 2.0),
                                                       oldVisibleRect.origin.y + (oldVisibleRect.size.height / 2.0)));

    // adjust my zoom
    ++displayZoom;
    [self scaleUnitSquareToSize:NSSizeFromCGSize(CGSizeMake(0.5, 0.5))];
    [self calculateBounds];  // make sure my frame & bounds are at least as big as the visible content view
    [self display];

    // Adjust scroll view to keep the same position.
    NSRect newVisibleRect = [[myScrollView contentView] documentVisibleRect];
    NSPoint newOffset = NSPointFromCGPoint(CGPointMake((oldCenter.x * 0.5) - (newVisibleRect.size.width  / 2.0),
                                                       (oldCenter.y * 0.5) - (newVisibleRect.size.height / 2.0)));
    if (newOffset.x < 0)
        newOffset.x = 0;
    if (newOffset.y < 0)
        newOffset.y = 0;

    [[myScrollView contentView] scrollToPoint: newOffset];
    [myScrollView reflectScrolledClipView: [myScrollView contentView]];

它似乎有点接近,但它不太正确,我无法弄清楚我做错了什么。我的两个问题是:

1)是否没有内置的东西:

   [myView adjustScaleBy: 0.5 whilePreservingLocationInScrollview:myScrollView];

2)如果没有,任何人都可以在我的“长途跋涉”方法中看到我做错了吗?

谢谢!

3 个答案:

答案 0 :(得分:11)

缩放后保持相同的滚动位置并不容易。你需要决定的一件事就是“相同”是什么意思 - 在缩放之前你想要可见区域的顶部,中间还是底部在缩放之后保持原位吗?

或者,更直观地说,您是否希望保持位置在可见矩形下方的百分比等于您在开始时向下滚动文档的百分比(例如,因此滚动条拇指的中心不会在刻度期间向上或向下移动,拇指只会增大或缩小。)

如果你想要后一种效果,一种方法是获取NSScrollView的verticalScroller和horizo​​ntalScroller,然后读取它们的'floatValue'。它们从0到1标准化,其中“0”表示您位于文档的顶部,1表示您已经结束。向滚动条询问这个问题的好处是,如果文档比NSScrollView短,那么滚动条在所有情况下仍然会为'floatValue'返回一个合理的答案,所以你不需要特殊情况。

调整大小后,将NSScrollView的滚动位置设置为与缩放之前相同的百分比 - 但遗憾的是,这是我挥动手的位置。我的代码中有一段时间没有这样做,但我记得你不能直接设置NSScrollers的'floatValue - 它们会LOOK滚动,但它们实际上不会影响NSScrollView。

所以,你必须写一些数学来根据你想要通过它的百分比来计算文档中的新左上角 - 例如,它看起来像,“如果文档现在比scrollView的contentView短,则滚动到0,否则滚动到文档的((contentView的高度 - documentView的高度)* oldVerticalPercentage)。 X轴当然是相似的。

另外,我几乎肯定你不需要在这里打电话 - 显然,一般不应该打电话给它。 (-setNeedsDisplay:最多。)

-Wil

答案 1 :(得分:7)

我认为你喜欢输入太多...; - )

// instead of this:
NSPoint oldCenter = NSPointFromCGPoint(CGPointMake(oldVisibleRect.origin.x +
    (oldVisibleRect.size.width  / 2.0),

// use this:
NSPoint oldCenter = NSMakePoint(NSMidX(oldVisibleRect), NSMaxY(oldVisibleRect));

// likewise instead of this:
[self scaleUnitSquareToSize:NSSizeFromCGSize(CGSizeMake(0.5, 0.5))];

// use this:
[self scaleUnitSquareToSize:NSMakeSize(0.5, 0.5)];

// and instead of this
NSPoint newOffset = NSPointFromCGPoint(CGPointMake(
    (oldCenter.x * 0.5) - (newVisibleRect.size.width  / 2.0),
    (oldCenter.y * 0.5) - (newVisibleRect.size.height / 2.0)));

// use this:
NSPoint newOffset NSMakePoint(
    (oldCenter.x - NSWidth(newVisibleRect)) / 2.f,
    (oldCenter.y - NSHeight(newVisibleRect)) / 2.f);

答案 2 :(得分:6)

这是一个老问题,但我希望有人在寻找这个问题,我的答案很有用......

float zoomFactor = 1.3;

-(void)zoomIn
{
    NSRect visible = [scrollView documentVisibleRect];
    NSRect newrect = NSInsetRect(visible, NSWidth(visible)*(1 - 1/zoomFactor)/2.0, NSHeight(visible)*(1 - 1/zoomFactor)/2.0);
    NSRect frame = [scrollView.documentView frame];

    [scrollView.documentView scaleUnitSquareToSize:NSMakeSize(zoomFactor, zoomFactor)];
    [scrollView.documentView setFrame:NSMakeRect(0, 0, frame.size.width * zoomFactor, frame.size.height * zoomFactor)];

    [[scrollView documentView] scrollPoint:newrect.origin];
}

-(void)zoomOut
{
    NSRect visible = [scrollView documentVisibleRect];
    NSRect newrect = NSOffsetRect(visible, -NSWidth(visible)*(zoomFactor - 1)/2.0, -NSHeight(visible)*(zoomFactor - 1)/2.0);

    NSRect frame = [scrollView.documentView frame];

    [scrollView.documentView scaleUnitSquareToSize:NSMakeSize(1/zoomFactor, 1/zoomFactor)];
    [scrollView.documentView setFrame:NSMakeRect(0, 0, frame.size.width / zoomFactor, frame.size.height / zoomFactor)];

    [[scrollView documentView] scrollPoint:newrect.origin];
}