Synchronized scrolling between two instances of NSScrollView

I have two instances of NSScrollView representing a view for the same content. The second scroll view, however, has a smaller version of the document view presented in the first scroll view. Both width and height can be individually scaled, and the original width and height restrictions may be lost, but that does not matter.

My synchronized scrolling works, even taking into account that in the second scroll view you need to align your scrolling behavior based on scaling. There is one small grip that I pull out of my hair:

  • Since both views are happily viewing the smaller view, you need to slowly catch up with the larger view so that they both β€œcome” at the end of their document at the same time. This is not happening right now, and the result is that the smaller view is at the "end of the document" before the larger view.

The code for scrolling synchronization is based on an example found in Apple's documentation called Scrolling Sync. I adapted 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 do I need to change this code to achieve the desired effect (both scroll bars coming to the end of the document)?

EDIT: some clarification as it was confusing when I read it myself: a smaller view should slow down when scrolling through the first view to the end. That would probably mean re-evaluating this scaling factor ... but how?

EDIT 2: I changed the method based on Alex's assumption:

  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, a smaller view is β€œovertaken” by a larger view when both scrollers reach 0.7, which is not very good. Then the enlarged view scrolls past the end of the document.

+4
source share
2 answers

I finally figured it out. The answer from Alex was a good hint, but not a complete solution, as simply setting the floating point value of the scroller does nothing. This value needs to be translated into specific coordinates, which need to scroll through the contents of the scroll.

However, due to differences in the size of the scrolled document, you cannot just simply use this value, since the reduced view will be overtaken by the β€œnormal” view at some point. This will cause the normal view to scroll beyond the end of the document.

The second part of the solution was to wait for the normal size to wait with scrolling, until the reduced view scrolls its own width.

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); 

Waiting is achieved by subtracting the scroll width from the width times the scroller value. When the smaller version still crosses its first pixel scroll width, this calculation will result in a negative offset. Using MAX will prevent strange effects, and the original view will calmly wait for the value to become positive, and then start its own scrolling. This solution also works when the user resizes the application window.

+1
source

I think you may approach this wrong. I think you should get a percentage of how far each scroll scrolls in relation to itself and apply it to a different look. One example of how this can be done is using NSScroller's -floatValue :

 NSScroller *myScroll = [self verticalScroller]; NSScroller *otherScroll = [otherScrollView verticalScroller]; [myScroll setFloatValue:otherScroll.floatValue]; 
+1
source

Source: https://habr.com/ru/post/1380860/


All Articles