Reputation: 4797
I have two instances of NSScrollView both presenting a view on the same content. The second scroll view however has a scaled down version of the document view presented in the first scroll view. Both width and height can be individually scaled and the original width - height constraints can be lost, but this is of no importance.
I have the synchronised scrolling working, even taking into account that the second scroll view needs to align its scrolling behaviour based on the scaling. There's one little snag I've been pulling my hairs out over:
The code for synchronised scrolling is based on the example found in Apple's documentation titled "Synchronizing Scroll Views". I have adapted the synchronizedViewContentBoundsDidChange:
to the following code:
- (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]];
}
}
How would I need to change that code so that the required effect (both scroll bars arriving at an end of document) is achieved?
EDIT: Some clarification as it was confusing when I read it back myself: The smaller view needs to slow down when scrolling the first view reaches the end. This would probably mean re-evaluating that scaling factor... but how?
EDIT 2: I changed the method based on Alex's suggestion:
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]];
}
Using this method the smaller view is "overtaken" by the larger view when both scrollers reach a value of 0.7, which is not good. The larger view then scrolls past its end of document.
Upvotes: 4
Views: 1524
Reputation: 4797
I finally figured it out. The answer from Alex was a good hint but not the full solution as just setting the float value of a scroller doesn't do anything. That value needs translation to specific coordinates to which the scroll view needs to scroll its contents.
However, due to differences in size of the scrolled document view, you cannot just simply use this value, as the scaled down view will be overtaken by the "normal" view at some point. This will cause the normal view to scroll past its end of document.
The second part of the solution was to make the normal sized view wait with scrolling until the scaled down view has scrolled its own width.
The code:
// 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);
The waiting is achieved by subtracting the width of the scroll view from the width times the value of the scroller. When the scaled down version is still traversing its first scroll view width of pixels, this calculation will result in a negative offset. Using MAX will prevent strange effects and the original view will quietly wait until the value turns positive and then start its own scrolling. This solution also works when the user resizes the app window.
Upvotes: 1
Reputation: 3601
I think you might be approaching this in the wrong way. I think you should be getting a percentage of how far down each scroll be is scrolled in relation to itself and apply that to the other view. One example of how this could be done is this way using NSScroller's
-floatValue
:
NSScroller *myScroll = [self verticalScroller];
NSScroller *otherScroll = [otherScrollView verticalScroller];
[myScroll setFloatValue:otherScroll.floatValue];
Upvotes: 1