Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSScrollView doesn't draw document view?

I have created a custom view that implements initWithFrame and drawRect. In IB, I created a window and placed the custom view in the center of the window. I then used Editor>Embed In>Scroll View to place the custom view into a scroll view. And then, nothing? The initWithFrame message is sent to the custom view, but the drawRect message is never sent.

Similar behavior can be produced without my specific custom view. If a wrapping label is embedded in a scrollview in the same way, it will also not be drawn.

How do I get the scrollview to draw its contents?

Also, on a somewhat related note, what is the correct way to programmatically resize the view embedded in the scrollview?

Thanks!

like image 246
user1574591 Avatar asked Jan 04 '14 07:01

user1574591


1 Answers

NSScrollView is not like other views. It does things just differently enough that it makes everyone crazy at first, and especially once you've gotten used to Auto Layout thinking. It is also one of those views (along with NSTableView, NSOutlineView and NSCollectionView and more) where there are a lot of pieces to set up before you can start seeing things working. Auto Layout adds to this a bit, too.

NSScrollView contains at least 3 subviews by default:

  • One NSClipView object. (Apple calls this the Content View. It is what is visible.)
  • Two NSScroller objects.

The NSClipView or Content View object contains an NSView of some sort.

(Apple calls this the Document View)

If you add a plain NSScrollView to your xib from the object library, that will appear to have a size in Interface Builder, but at run time it will have a zero size. This will kick your butt every time. You can see this by adding logging it.

NSLog(@" size of document view:%@", NSStringFromRect(_myScrollView.documentView.frame);

Even if you add a subview and call setNeedsDisplay:YES all day long, you won't see a damn thing.

There are things you can do though. First, you can set the size of the documentView with setFrameSize:NSMakeSize(somePointsWide, somePointsHigh)

You can also make it a custom NSView subclass that has an implementation of intrinsicContentSize that figures this out based on whatever logic rules you want to put in there (including some default minimum).

You could also simply say that all subviews must be at least N points from the documentView or more on each edge with Auto Layout. The VFL (visual format language) would look like this: @"H:|-nPoints-[aSubviewOfDocumentView]-nPoints-|" and this @"V:|-nPoints-[aSubviewOfDocumentView]-nPoints-|" (make sure your subviews have an intrinsic size or some subview of their own that gives them a minimum size... and implements - (BOOL)translatesAutoresizingMaskIntoConstraints { return NO; } and turn off Auto Resizes Subviews for your documentView by the checkbox in IB, or by calling setAutoResizesSubviews:NO or you'll be hating life.)

Now this leads to a weird thing. With Auto Layout, it's generally good to stop thinking about frames and rects as much as possible, but with the NSScrollView documentView, you might want to consider not using Auto Layout for positioning subviews. In some cases it might be simpler not to, and just let the documentView grow to accommodate. If you use the above "nPoints" approach, and your subviews are draggable, you want to simply capture the NSPoint for the subview's frame origin (use it to determine your delta) and then the mouseDown point and when a drag finished, capture the mouse point and calculate the delta for your new frame origin point. Then call setFrameOrigin: with the new point.

like image 64
uchuugaka Avatar answered Oct 03 '22 10:10

uchuugaka