开发者

Pinning NSScrollView's document view while resizing a window

开发者 https://www.devze.com 2023-01-09 08:46 出处:网络
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 lar

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 edg开发者_如何学Goe 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)];

    [...]

}


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


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

@interface FlippedClipView : NSClipView 
@end

@implementation FlippedClipView
- (BOOL)isFlipped
{
    return YES;
}
@end


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消