CocoaDev

Edit AllPages


Overview —-

If you want to center the document view inside of a scrollview when the document view is smaller than the scrollview, you have a couple options. You can:

The first option is simple, but can be annoying to deal with and cause other problems within the document view. The second option is the obvious evolution of the first splitting the responsibilities into two views. It’s easy to implement, but there always seems to be some unexpect gotcha everytime I’ve done it. The third solution attacks the problem head on, making the clip view center the document view which makes the most sense because the clip view is responsible for the actual layout.


Centering NSClipView —-

There is an article (dated 2002, but still relevant) which shows one approach to creating a centering clip view. The article is available here: http://www.bergdesign.com/developer/index_files/88a764e343ce7190c4372d1425b3b6a3-0.html

In my experience, this code no longer works out of the box. I have tweaked the code, and it now works properly in all of my tests.

The example project for this is here: http://www.sethwillits.com/temp/CenteredScrollView.zip

The only drawback to using an NSClipView subclass is that you need to replace the clipview in the scrollview at runtime since it’s not possible in Interface Builder / Xcode 4. The AGCenteringClipView class includes a convenience method to aid with this.

// // AGCenteringClipView.h // CenteredScroll // // Created by Seth Willits on 8/15/2011. //

#import <Cocoa/Cocoa.h>

@interface AGCenteringClipView : NSClipView { NSPoint mLookingAt; // the proportion up and across the view, not coordinates. }

@end

// // AGCenteringClipView.m // CenteredScroll // // Created by Seth Willits on 8/15/2011. //

#import “AGCenteringClipView.h”

@interface AGCenteringClipView (Private)

@implementation AGCenteringClipView

// —————————————- // We need to override this so that the superclass doesn’t override our new origin point.

// —————————————- // These two methods get called whenever the NSClipView’s subview changes. // We save the old center of interest, call the superclass to let it do its work, // then move the scroll point to try and put the old center of interest // back in the center of the view if possible.

// —————————————- // We have some redundancy in the fact that setFrame: appears to call/send setFrameOrigin: // and setFrameSize: to do its work, but we need to override these individual methods in case // either one gets called independently. Because none of them explicitly cause a screen update, // it’s ok to do a little extra work behind the scenes because it wastes very little time. // It’s probably the result of a single UI action anyway so it’s not like it’s slowing // down a huge iteration by being called thousands of times.

@end

#pragma mark

@implementation AGCenteringClipView (Private)

@end


The Seashore Project (http://seashore.sourceforge.net/) has an [[NSClipView subclass that does just this. The source is free to download (GPL’d, I believe), so give it a look. Specifically, check out CenteringClipView.h/m. - MattBall


Centering Enclosing View —-

In this example, there’s a 4-level view hierarchy: NSScrollView -> NSClipView -> ScrollDocumentView -> MyView.

The ScrollDocumentView always makes sure to resize itself so that it is at minimum the size of the scrollview’s clip view, and always large enough to contain the real “document view”, the “MyView”. MyView’s responsibility is to always center itself in its superview when its frame changes.

It’s important that the MyView instance have no autoresizing for width/height resizing, and that all of its margins remain flexible.

@implementation ScrollDocumentView

@end

@implementation MyView

@end

The example project for this is here: http://www.sethwillits.com/temp/CenteredScrollView.zip


Miscellaneous —-

Note: not mentioned in this article, is that it appears you need to also provide this method in the view you are drawing:

When I did not do this, I was getting double images in the view. I guess if you really wanted to get “smart” about this, you would set NO if the document view was getting centered, and YES if not.


That’s exactly what I did, and the code for copiesOnScroll looks like this


I had the same problem as the original poster, wanting to center an image (within an NSImageView) inside an NSScrollView. I have also experimented with subclassing the NSClipView, but for the very problem of simply centering an image, I found another solution to be much easier and faster. I took advantage of NSImageView centering its image automatically if the image is smaller than the view’s frame. Therefore I simply sublassed NSImageView (similarly as in the very elegant MarginView example above), having it resize to whatever is larger, its image or the frame of the containing NSClipView. It’s that easy… Here’s the code:

In the .h file:

#import <Cocoa/Cocoa.h>

@interface SBSScrollableImageView : NSImageView {

BOOL			insideScrollView; }

@end

In the .m file:

#import “SBSScrollableImageView.h”

@implementation SBSScrollableImageView

@end

Hope it helps someone (probably not the original poster with one year’s delay)…

–MarCocoa