Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How exactly should an override of NSView's -layout method work?

I have a hierarchy of custom NSViews which I'm laying out in a style not supported by autolayout constraints. Right now, I'm just watching for resize events, and calling -setFrame on all my subviews (and their subviews...), but that's not scaling very well, in terms of source code complexity. (When I add one new subview at the bottom of the hierarchy, I have to add its layout logic way up in the top view -- that doesn't seem ideal.)

The proper way to solve this, as I understand it, is to override NSView's -layout method (and then call -setNeedsLayout when I want to force a layout, at some level of the tree).

Unfortunately, the documentation for that is a bit terse, and (despite the "CocoaSlides" link there) there's no sample code from Apple that overrides this method. I tried searching on Github, but it turns out that "layout" is a rather common word in Objective-C code, and I have yet to find one that's the one I want.

For example, let's say I have an AAView (NSView), which has some BBView's inside it which display some word-wrapped text, and it wants to display them with at 100% of its own width (vaguely like an NSStackView, I think, though that's another totally undocumented feature of Cocoa).

Because the BBViews have a height which is dependent on their width (due to their word wrapping), they can't have an -intrinsicContentSize (correct?). AAView's -layout method wants to place the BBViews, i.e., to set their frameSize and frameOrigin. In order to know BBView's size, though, it needs to ask BBView -- but BBView can't set its own size in its -layout method (only for its subviews, if it has any). So any implementation of -layout in these classes also needs some out-of-band methods (like, say, a -requestedHeightForWidth:(CGFloat)width on BBView), at which point I'm not sure that's any better than my own ad-hoc -setFrame calls (i.e., what CocoaSlides actually does).

Can somebody point me at some sample code which implements -layout for custom NSViews? I'm just not seeing how this is supposed to work. Am I totally barking up the wrong tree?

like image 973
user241221 Avatar asked Nov 10 '22 15:11

user241221


1 Answers

I believe you can do what you want with autolayout using -intrinsicContentSize on BBView, assuming I understand the problem.

If your BBView always wants to always be the exact width of your AAView (or something close to that), you can just set standard horizontal constraints to enforce this. That solves the width.

To do the height, override -setFrameSize: on BBView, and if the width has changed from the old width, call -invalidateIntrinsicContentSize.

In your -intrinsicContentSize on BBView, you look at your current width and calculate the height based on how your strings lay out inside it. Return {self.frame.width, calculatedHeight} and you’re home-free. (There are obvious optimizations to this if -intrinsicContentSize is called a lot and your layout is expensive.)

EDIT: Actually, you should return (NSSize){NSViewNoInstrinsicMetric, calculatedHeight}, not the current width. Sorry.

like image 54
Wil Shipley Avatar answered Dec 07 '22 12:12

Wil Shipley