Pinning NSScrollView's document view while resizing a window
Asked Answered
B

3

5

This seems like such a trivial question to ask, but it's been vexing me for a while.

I have a window which contains a scroll view. Typically, the scroll view has a documentView that is vertically larger than the clip view, so you get a vertical scroller to see all the content inside the scroll view.

When I resize the window, I recalculate the size of the content in the scroll view's documentView (because the scroll view can get thinner, which might make it necessary for the documentView to grow in height). A side effect, however, is that when resizing the window, the documentView keeps the bottom visible edge flush with the bottom edge of the clip view (that is, it makes sure that the last line of text is always visible). This is a weird effect, because typically, windows keep the top visible edge of the document view flush with the top edge of the clip view (that is, the topmost line of text in the document view remains pinned to the top).

So, my initial thought to fix this problem was just to implement a windowDidResize: or windowWillResize:toSize: notification, note the delta between the old window frame's height and the new height, and then simply scroll the scroll view that much in order to keep the top line of the scroll view pinned to the top.

However, for some reason, this doesn't seem to work. It almost works, but some resizing deltas seem to be a pixel off, and if you resize the window fast enough, it sometimes is ~10 pixels off. So the effect is that the top line of the scroll view is almost pinned to the top, but not quite, and it's distracting.

Is there a better way to do this? Here's the relevant code:

- (void)windowDidResize:(NSNotification *)notification;
{
    if ([notification object] == mainWindow) {
        CGFloat currentWindowHeight = [mainWindow frame].size.height;

        // previousWindowHeight is an ivar
        NSNumber *heightDeltaNum = [NSNumber numberWithFloat:(currentWindowHeight - previousWindowHeight)];
        previousWindowHeight = currentWindowHeight;
        [[NSNotificationCenter defaultCenter] postNotificationName:@"AFSnapScrollView" object:heightDeltaNum];
    }
}

- (void)snapScrollViewNotification:(NSNotification *)theNotification;
{
    [self snapScrollView];

    NSNumber *heightDeltaNum = [theNotification object];

    CGFloat newY = [[tagScrollView documentView] visibleRect].origin.y - [heightDeltaNum floatValue];
    NSPoint pointToScroll = NSMakePoint(0,newY);
    [[tagScrollView documentView] scrollPoint:pointToScroll];
}

- (void)snapScrollView;
{

    [...]

    // adjust the view frame
    [[tagScrollView documentView] setFrame:NSMakeRect(0, 0, existingDocumentFrame.size.width, newDocumentViewHeight)];

    [...]

}
Bartolemo answered 28/7, 2010 at 7:37 Comment(1)
See also #2384514Warble
F
11

Your document view needs to override the -(BOOL)isFlipped method and return YES.

Floribunda answered 28/7, 2010 at 10:17 Comment(2)
Hm, did not realize that affected behavior of scroll views. Works perfectly, does exactly what I want (once I modified parts of the snapScrollView method to adjust for the flipped coordinates). Thanks very much!Bartolemo
I have found this failed if I had a contentOffset set on the bottom of the scrollView. The offset would be off by that amount after a scroll.Spectrophotometer
E
1

turning clipview not need to modify documentView, after flip coordinates.

@interface FlippedClipView : NSClipView 
@end

@implementation FlippedClipView
- (BOOL)isFlipped
{
    return YES;
}
@end
Elderberry answered 8/1, 2014 at 13:13 Comment(0)
S
0

I found that if I added contentInsets to my scrollView, the offset setting at the end of a resize was off by the size of my bottom inset. Very frustrating.

I fixed it with the following in a custom NSScrollView.

    var lastOffset = NSPoint.zero

    override func viewWillStartLiveResize() {
        lastOffset = documentVisibleRect.origin
    }

    override func viewDidEndLiveResize() {
        documentView?.scroll(lastOffset)
    }

If you have documentViews that resize when the scrollView resizes, you'll need to do the offset calculations when you set them back. You'll need to set the offset after each of the layout() calls in-between the live resize calls to keep the movement smooth too.

Spectrophotometer answered 1/2, 2019 at 14:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.