在两个NSScrollView实例之间同步滚动

时间:2011-11-11 22:39:44

标签: objective-c cocoa nsscrollview

我有两个NSScrollView实例都呈现相同内容的视图。然而,第二滚动视图具有在第一滚动视图中呈现的文档视图的缩小版本。宽度和高度都可以单独缩放,原始的宽度 - 高度约束可能会丢失,但这并不重要。

我有同步滚动工作,甚至考虑到第二个滚动视图需要根据缩放对齐其滚动行为。有一点小问题我一直把头发拉过来:

  • 由于两个视图都快乐地沿着较小的视图滚动,需要慢慢赶上较大的视图,以便它们同时“到达”文档的末尾。现在没有发生这种情况,结果是较小的视图位于较大视图之前的“文档结束”。

同步滚动的代码基于Apple的文档“同步滚动视图”中的示例。我已将synchronizedViewContentBoundsDidChange:改为以下代码:

- (void) synchronizedViewContentBoundsDidChange: (NSNotification *) notification {

    // get the changed content view from the notification
    NSClipView *changedContentView = [notification object];

    // get the origin of the NSClipView of the scroll view that
    // we're watching
    NSPoint changedBoundsOrigin = [changedContentView documentVisibleRect].origin;;

    // get our current origin
    NSPoint curOffset = [[self contentView] bounds].origin;
    NSPoint newOffset = curOffset;

    // scrolling is synchronized in the horizontal plane
    // so only modify the x component of the offset
    // "scale" variable will correct for difference in size between views
    NSSize ownSize = [[self documentView] frame].size;
    NSSize otherSize = [[[self synchronizedScrollView] documentView] frame].size;
    float scale = otherSize.width / ownSize.width;
    newOffset.x = floor(changedBoundsOrigin.x / scale);

    // if our synced position is different from our current
    // position, reposition our content view
    if (!NSEqualPoints(curOffset, changedBoundsOrigin)) {
        // note that a scroll view watching this one will
        // get notified here
        [[self contentView] scrollToPoint:newOffset];
        // we have to tell the NSScrollView to update its
        // scrollers
        [self reflectScrolledClipView:[self contentView]];
    }

}

我如何更改该代码以达到所需的效果(两个滚动条到达文档末尾)?

编辑:有些澄清,因为当我自己回读它时会感到困惑:当滚动第一个视图到达结尾时,较小的视图需要减慢速度。这可能意味着重新评估该缩放因子......但是如何?

编辑2:我根据Alex的建议更改了方法:

    NSScroller *myScroll = [self horizontalScroller];
NSScroller *otherScroll = [[self synchronizedScrollView] horizontalScroller];

//[otherScroll setFloatValue: [myScroll floatValue]];

NSLog(@"My scroller value: %f", [myScroll floatValue]);
NSLog(@"Other scroller value: %f", [otherScroll floatValue]);

// Get the changed content view from the notification.
NSClipView *changedContentView = [notification object];

// Get the origin of the NSClipView of the scroll view that we're watching.
NSPoint changedBoundsOrigin = [changedContentView documentVisibleRect].origin;;

// Get our current origin.
NSPoint curOffset = [[self contentView] bounds].origin;
NSPoint newOffset = curOffset;

// Scrolling is synchronized in the horizontal plane so only modify the x component of the offset.
NSSize ownSize = [[self documentView] frame].size;
newOffset.x = floor(ownSize.width * [otherScroll floatValue]);

// If our synced position is different from our current position, reposition our content view.
if (!NSEqualPoints(curOffset, changedBoundsOrigin)) {
    // Note that a scroll view watching this one will get notified here.
    [[self contentView] scrollToPoint: newOffset];
    // We have to tell the NSScrollView to update its scrollers.
    [self reflectScrolledClipView:[self contentView]];
}

使用此方法,当两个滚动条达到0.7的值时,较小的视图会被较大的视图“超越”,这是不好的。然后,较大的视图滚动到文档末尾。

2 个答案:

答案 0 :(得分:1)

我认为你可能会以错误的方式接近这一点。我认为你应该得到每个卷轴相对于自身滚动的距离的百分比,并将其应用于另一个视图。如何使用NSScroller's -floatValue

这样做的一个示例
NSScroller *myScroll = [self verticalScroller];
NSScroller *otherScroll = [otherScrollView verticalScroller];

[myScroll setFloatValue:otherScroll.floatValue];

答案 1 :(得分:1)

我终于明白了。 Alex的答案是一个很好的提示,但不是完整的解决方案,因为只设置一个滚动条的浮点值并没有做任何事情。该值需要转换为滚动视图需要滚动其内容的特定坐标。

但是,由于滚动文档视图的大小不同,您不能简单地使用此值,因为缩小视图将在某个时候被“正常”视图超越。这将导致普通视图滚动到文档末尾。

解决方案的第二部分是使正常大小的视图等待滚动,直到缩小的视图滚动自己的宽度。

代码:

// Scrolling is synchronized in the horizontal plane so only modify the x component of the offset.
    NSSize ownSize = [[self documentView] frame].size;
    newOffset.x = MAX(floor(ownSize.width * [otherScroll floatValue] - [self frame].size.width),0);

通过从宽度乘以滚动条的值减去滚动视图的宽度来实现等待。当按比例缩小的版本仍然遍历其第一个像素的滚动视图宽度时,此计算将导致负偏移。使用MAX将防止奇怪的效果,原始视图将静静等待,直到值变为正,然后开始自己的滚动。当用户调整应用程序窗口大小时,此解决方案也可以使用。