More Layout Questions


Dave
 

Hi,

I have a couple of questions related to manual layout on the Mac.

Using manual layout, does setting the “frame: of a view cause “layout” to be called or do I need to call “setNeedsLayout” specifically?


In general, is there a particular way in which the NSView “layout” method is supposed to work or is it up to the developer to choose?


From the docs:

Override this method if your custom view needs to perform custom layout not expressible using the constraint-based layout system. In this case you are responsible for setting needsLayout to YES when something that impacts your custom layout changes.

You may not invalidate any constraints as part of your layout phase, nor invalidate the layout of your superview or views outside of your view hierarchy. You also may not invoke a drawing pass as part of layout.

You must call [super layout] as part of your implementation.

Should the “layout” method change the value of their “frame” property or should this be done by the superview?

I have a View Hierarchy and when the window resizes, I change the sizes and positions of the views within each subview. It seem to me that there are two ways of doing it given the following Hierarchy:

WindowTrackingView (.view)
LeftAreaView
ContainerViewA

RightAreaView
ContainerViewB

So, when the window resized, WindowTrackingView::layout gets called with its “frame” property set to the new size. 

At this point there are two options:

1. WindowTrackingView::layout simply calls “setNeedsLayout” for each of its subviews and each subview sets their own frame based on the “frame" rectangle of the superview via their own “layout” method.

2.  WindowTrackingView::layout calculates and sets the frame of each of its subviews based on its own “frame” in this case and then calls “setNeedsDisplay” (unless this is already done if the “frame” changes?).

Is any of these methods preferred or is it up to the developer to choose based on the job in hand?


Any help on this greatly appreciated, I’ve written a test app and have almost got my head around manual layout, I just need to understand if this last bit.

Thanks in advance.

All the Best
Dave
















Sandor Szatmari
 

On Sep 21, 2018, at 08:36, Dave <dave@...> wrote:

Hi,

I have a couple of questions related to manual layout on the Mac.

Using manual layout, does setting the “frame: of a view cause “layout” to be called or do I need to call “setNeedsLayout” specifically?

Don’t know but should be empirical.

Sandor



In general, is there a particular way in which the NSView “layout” method is supposed to work or is it up to the developer to choose?


From the docs:

Override this method if your custom view needs to perform custom layout not expressible using the constraint-based layout system. In this case you are responsible for setting needsLayout to YES when something that impacts your custom layout changes.

You may not invalidate any constraints as part of your layout phase, nor invalidate the layout of your superview or views outside of your view hierarchy. You also may not invoke a drawing pass as part of layout.

You must call [super layout] as part of your implementation.

Should the “layout” method change the value of their “frame” property or should this be done by the superview?

I have a View Hierarchy and when the window resizes, I change the sizes and positions of the views within each subview. It seem to me that there are two ways of doing it given the following Hierarchy:

WindowTrackingView (.view)
LeftAreaView
ContainerViewA

RightAreaView
ContainerViewB

So, when the window resized, WindowTrackingView::layout gets called with its “frame” property set to the new size. 

At this point there are two options:

1. WindowTrackingView::layout simply calls “setNeedsLayout” for each of its subviews and each subview sets their own frame based on the “frame" rectangle of the superview via their own “layout” method.

2.  WindowTrackingView::layout calculates and sets the frame of each of its subviews based on its own “frame” in this case and then calls “setNeedsDisplay” (unless this is already done if the “frame” changes?).

Is any of these methods preferred or is it up to the developer to choose based on the job in hand?


Any help on this greatly appreciated, I’ve written a test app and have almost got my head around manual layout, I just need to understand if this last bit.

Thanks in advance.

All the Best
Dave
















Quincey Morris
 

On Sep 21, 2018, at 05:36 , Dave <dave@...> wrote:

Using manual layout, does setting the “frame: of a view cause “layout” to be called or do I need to call “setNeedsLayout” specifically?

You seem to be way off base here. “Manual layout” is just the absence of auto-layout. The layout method is part of the auto-layout system, as the documentation says:

"Perform layout in concert with the constraint-based layout system.”


It’s a way of customizing auto-layout, not doing manual layout.

It sounds like you’re trying to auto-layout, but without any constraints. That’s certainly possible, but an auto-layout pass is still an auto-layout pass.

is there a particular way in which the NSView “layout” method is supposed to work or is it up to the developer to choose?

Yes, there’s a particular way. It needs to lay out its subviews (adjust the size and position of its subviews) by doing whatever isn’t being done by constraints. That means setting frames of its subviews. A view should not change *its own* frame in its "layout” method. That invalidates the layout of the superview, which is explicitly prohibited by the documentation you quoted.

At this point there are two options:

1. WindowTrackingView::layout simply calls “setNeedsLayout” for each of its subviews and each subview sets their own frame based on the “frame" rectangle of the superview via their own “layout” method.

No. Do *not* call “setNeedsLayout” from a “layout” method. That will trigger another layout pass, and layout will loop forever. As above, views are prohibited from setting their own frame in their own “layout” method.

2.  WindowTrackingView::layout calculates and sets the frame of each of its subviews based on its own “frame” in this case and then calls “setNeedsDisplay” (unless this is already done if the “frame” changes?).

Do not call “setNeedsDisplay” routinely here. The “layout” method is about layout, and you aren’t concerned with drawing here. (If changing the layout resizes some subviews, the resizing itself will trigger drawing as necessary. That’s the normal consequence of resizing views. You don’t have to do anything extra.)

But, yes, you need to set the frame of each subview that isn’t going to be (before "[super layout]”) or hasn’t been ("after [super layout]”) set by auto-layout constraints.


Dave
 



On 21 Sep 2018, at 16:49, Quincey Morris <quinceymorris@...> wrote:

On Sep 21, 2018, at 05:36 , Dave <dave@...> wrote:

Using manual layout, does setting the “frame: of a view cause “layout” to be called or do I need to call “setNeedsLayout” specifically?

You seem to be way off base here. “Manual layout” is just the absence of auto-layout. The layout method is part of the auto-layout system, as the documentation says:

I’m not sure what you mean, my storyboard file has Auto Layout and there are no constraints anywhere in the project. Also, all the old-type layout options are off in the Size Panel in IB.

"Perform layout in concert with the constraint-based layout system.”


It’s a way of customizing auto-layout, not doing manual layout.

It sounds like you’re trying to auto-layout, but without any constraints. That’s certainly possible, but an auto-layout pass is still an auto-layout pass.

is there a particular way in which the NSView “layout” method is supposed to work or is it up to the developer to choose?

Yes, there’s a particular way. It needs to lay out its subviews (adjust the size and position of its subviews) by doing whatever isn’t being done by constraints. That means setting frames of its subviews. A view should not change *its own* frame in its "layout” method. That invalidates the layout of the superview, which is explicitly prohibited by the documentation you quoted.


Typical! The way I chose to do it, layout sets in own frame rectangle and calls layout on its subviews, however, it seems to work wonderfully, so not sure what is going on! Any comments?


At this point there are two options:

1. WindowTrackingView::layout simply calls “setNeedsLayout” for each of its subviews and each subview sets their own frame based on the “frame" rectangle of the superview via their own “layout” method.

No. Do *not* call “setNeedsLayout” from a “layout” method. That will trigger another layout pass, and layout will loop forever. As above, views are prohibited from setting their own frame in their own “layout” method.

It only calls setNeedsLayout on its subviews, not on itself, is this still not a good idea (it seems to work ok).


2.  WindowTrackingView::layout calculates and sets the frame of each of its subviews based on its own “frame” in this case and then calls “setNeedsDisplay” (unless this is already done if the “frame” changes?).

Do not call “setNeedsDisplay” routinely here. The “layout” method is about layout, and you aren’t concerned with drawing here. (If changing the layout resizes some subviews, the resizing itself will trigger drawing as necessary. That’s the normal consequence of resizing views. You don’t have to do anything extra.)

Noted, thanks.

But, yes, you need to set the frame of each subview that isn’t going to be (before "[super layout]”) or hasn’t been ("after [super layout]”) set by auto-layout constraints.

Not sure what you mean by this? There are no constraints define in this Storyboard/NIB.

In the following hierarchy:

WindowTrackerView .view)
SubviewA
SubviewB
SubviewC

WindowTrackerView::layout sets the frame of SubviewA.
SubviewA::layout sets the frame of SubviewB
SubviewB::layout sets the frame of SubviewC



Thanks a lot for the help
All the Best
Dave






























Dave
 

I meant to say:

I’m not sure what you mean, my storyboard file has Auto Layout
DISABLED and there are no constraints anywhere in the project. Also, all the old-type layout options are off in the Size Panel in IB.




Quincey Morris
 

On Sep 21, 2018, at 08:07 , Dave <dave@...> wrote:

I’m not sure what you mean, my storyboard file has Auto Layout DISABLED and there are no constraints anywhere in the project. Also, all the old-type layout options are off in the Size Panel in IB.

I mean that if you have disabled auto-layout, then you shouldn’t be trying to use auto-layout. Implementing the “layout” method comes under the heading of “trying to use auto-layout”, as the documentation says.

It may be possible that “layout” gets called anyway (it would have no effect unless you overrode it to do something), but the behavior isn’t documented in that case. You can’t rely on it to behave in any meaningful way.


Dave
 

Hi,

I’m confused then! The “layout” method has been around a lot longer than Auto Layout and it worked in conjunction with the Auto-Resizing options (defined in the Size Pane in IB). I assumed that in order to customise my layout I would override “layout” as done in the past, is this not the case?

You seem to be saying that If I want to layout my views myself, I don’t override the layout method or use setNeedsLayout? 

I could change the name of the method from “layout" to (say) “layoutView” for instance and instead of calling “setNeedsLayout” call “layoutView” on the subviews. However, at the moment when the window is resized, it initially calls “layout” in the WindowTracker view, do I still override this and “layoutView” on its subviews?

The only other way of handling I can think of is to override setFrame and do the layout there, does that sound like a better approach?

I’m just trying to find a decent way of laying out my views that’s fits into the way Cocoa works these days…..

Any advice or suggestions greatly appreciated.

All the Best
Dave


On 21 Sep 2018, at 17:28, Quincey Morris <quinceymorris@...> wrote:

On Sep 21, 2018, at 08:07 , Dave <dave@...> wrote:

I’m not sure what you mean, my storyboard file has Auto Layout DISABLED and there are no constraints anywhere in the project. Also, all the old-type layout options are off in the Size Panel in IB.

I mean that if you have disabled auto-layout, then you shouldn’t be trying to use auto-layout. Implementing the “layout” method comes under the heading of “trying to use auto-layout”, as the documentation says.

It may be possible that “layout” gets called anyway (it would have no effect unless you overrode it to do something), but the behavior isn’t documented in that case. You can’t rely on it to behave in any meaningful way.



Keary Suska
 

On Sep 22, 2018, at 5:29 AM, Dave <dave@looktowindward.com> wrote:

Hi,

I’m confused then! The “layout” method has been around a lot longer than Auto Layout and it worked in conjunction with the Auto-Resizing options (defined in the Size Pane in IB). I assumed that in order to customise my layout I would override “layout” as done in the past, is this not the case?
This is incorrect: “layout” methods were added when autolayout was introduced in Lion (10.7). Before that, you used “frame” and “display” methods, and it was generally the responsibility of the superview to resize its subviews when its frame changes.

You seem to be saying that If I want to layout my views myself, I don’t override the layout method or use setNeedsLayout?
Yes.

I could change the name of the method from “layout" to (say) “layoutView” for instance and instead of calling “setNeedsLayout” call “layoutView” on the subviews. However, at the moment when the window is resized, it initially calls “layout” in the WindowTracker view, do I still override this and “layoutView” on its subviews?

The only other way of handling I can think of is to override setFrame and do the layout there, does that sound like a better approach?
Where you put resize logic probably best depends on who knows the most about how views should be laid out. For a super view that auto resizes subviews, its -resizeSubviewsWithOldSize: method is called anytime its frame changes. That is a better place to perform resizing on subviews, since it will be called by any method that changes the view's frame. If you want each subview to manage itself, you would override -resizeWithOldSuperviewSize: in the view. IMHO, it makes more sense for the superviews to manage their subviews so you avoid a bunch of subclasses that do nothing but self-resizing.

I’m just trying to find a decent way of laying out my views that’s fits into the way Cocoa works these days…..
Honestly, that means using autolayout because that “is thew way Cocoa works these days” but I sympathize with a strong desire to avoid it, as well as there being a number of use cases it simply can’t handle.

Keary Suska
Esoteritech, Inc.
"Demystifying technology for your home or business"

On 21 Sep 2018, at 17:28, Quincey Morris <quinceymorris@rivergatesoftware.com> wrote:

On Sep 21, 2018, at 08:07 , Dave <dave@looktowindward.com> wrote:

I’m not sure what you mean, my storyboard file has Auto Layout DISABLED and there are no constraints anywhere in the project. Also, all the old-type layout options are off in the Size Panel in IB.
I mean that if you have disabled auto-layout, then you shouldn’t be trying to use auto-layout. Implementing the “layout” method comes under the heading of “trying to use auto-layout”, as the documentation says.

It may be possible that “layout” gets called anyway (it would have no effect unless you overrode it to do something), but the behavior isn’t documented in that case. You can’t rely on it to behave in any meaningful way.


Gary L. Wade
 

When you find a use case it doesn’t handle, consider the various container views available, and if those don’t help, definitely write a radar.  Apple is always improving things, and if you report such a case, it helps everyone.

On Sep 22, 2018, at 7:54 AM, Keary Suska <cocoa-dev@...> wrote:

Honestly, that means using autolayout because that “is thew way Cocoa works these days” but I sympathize with a strong desire to avoid it, as well as there being a number of use cases it simply can’t handle.


Dave
 

HI,

Thanks for this, I did something like this a long while back and must have confused the “Display” path with the “Layout” path, thanks for putting me on the right track again. The its written it will be very easy to change what I have already to do it the way you describe below.

Honestly, that means using autolayout because that “is thew way Cocoa works these days” but I sympathize with a strong desire to avoid it, as well as there being a number of use cases it simply can’t handle.

The amount of time it was taking to make the board for the game I am working on to work was ridiculous for the return. The number of constraints was enormous and it was really slow when resizing. Add to this that as soon as you wanted to change something it sometimes meant re-doing *lots* of constraints and still it wasn’t 100%. It’s not Auto-Layout itself, the underlying technology is really good and I’d love to use it, but the tools we have for creating the Layouts in the first place are just horrible and unforgiving. I think a lot of it is due to having everything shoved into one giant all-purpose window/tab in Interface Builder. IMO its needs a specialist window that is designed specifically for Auto-Layout generation.

Where you put resize logic probably best depends on who knows the most about how views should be laid out. For a super view that auto resizes subviews, its -resizeSubviewsWithOldSize: method is called anytime its frame changes. That is a better place to perform resizing on subviews, since it will be called by any method that changes the view's frame. If you want each subview to manage itself, you would override -resizeWithOldSuperviewSize: in the view. IMHO, it makes more sense for the superviews to manage their subviews so you avoid a bunch of subclasses that do nothing but self-resizing.
I’m in two minds on this, a lot of the work can be done in a View Base Class that the subviews inherit from, this means that each subview has minimum extra code, but you can override it if you want to. I’m actually working on a number of games that use (mostly) a common board, each Game-App, and I can pull in Custom Views for the Games if I like, just by either adding or replacing a View in the Hierarchy and can be done in IB with no or minimal code changes.


Thanks again for your help.

All the Best
Dave

On 22 Sep 2018, at 16:54, Keary Suska <cocoa-dev@esoteritech.com> wrote:

On Sep 22, 2018, at 5:29 AM, Dave <dave@looktowindward.com> wrote:

Hi,

I’m confused then! The “layout” method has been around a lot longer than Auto Layout and it worked in conjunction with the Auto-Resizing options (defined in the Size Pane in IB). I assumed that in order to customise my layout I would override “layout” as done in the past, is this not the case?
This is incorrect: “layout” methods were added when autolayout was introduced in Lion (10.7). Before that, you used “frame” and “display” methods, and it was generally the responsibility of the superview to resize its subviews when its frame changes.

You seem to be saying that If I want to layout my views myself, I don’t override the layout method or use setNeedsLayout?
Yes.

I could change the name of the method from “layout" to (say) “layoutView” for instance and instead of calling “setNeedsLayout” call “layoutView” on the subviews. However, at the moment when the window is resized, it initially calls “layout” in the WindowTracker view, do I still override this and “layoutView” on its subviews?

The only other way of handling I can think of is to override setFrame and do the layout there, does that sound like a better approach?
Where you put resize logic probably best depends on who knows the most about how views should be laid out. For a super view that auto resizes subviews, its -resizeSubviewsWithOldSize: method is called anytime its frame changes. That is a better place to perform resizing on subviews, since it will be called by any method that changes the view's frame. If you want each subview to manage itself, you would override -resizeWithOldSuperviewSize: in the view. IMHO, it makes more sense for the superviews to manage their subviews so you avoid a bunch of subclasses that do nothing but self-resizing.

I’m just trying to find a decent way of laying out my views that’s fits into the way Cocoa works these days…..
Honestly, that means using autolayout because that “is thew way Cocoa works these days” but I sympathize with a strong desire to avoid it, as well as there being a number of use cases it simply can’t handle.

Keary Suska
Esoteritech, Inc.
"Demystifying technology for your home or business"

On 21 Sep 2018, at 17:28, Quincey Morris <quinceymorris@rivergatesoftware.com> wrote:

On Sep 21, 2018, at 08:07 , Dave <dave@looktowindward.com> wrote:

I’m not sure what you mean, my storyboard file has Auto Layout DISABLED and there are no constraints anywhere in the project. Also, all the old-type layout options are off in the Size Panel in IB.
I mean that if you have disabled auto-layout, then you shouldn’t be trying to use auto-layout. Implementing the “layout” method comes under the heading of “trying to use auto-layout”, as the documentation says.

It may be possible that “layout” gets called anyway (it would have no effect unless you overrode it to do something), but the behavior isn’t documented in that case. You can’t rely on it to behave in any meaningful way.


Dave
 

Hi,

It’s not so much it can’t handle it, its that it takes way to long to do the design in  Xcode/Interface Builder and what I am doing is not that difficult but there is a lot of it. Also their is a lot of documentation, I could never find anything actually helped me to do the design for a game. After 3 separate attempts that took around 12 days, I managed to get it working with a few glitches, then I came to modify it, and it was sheer hell. In the end I gave up and decided to do the layout myself.

Cheers
Dave

On 22 Sep 2018, at 17:54, Gary L. Wade <garywade@...> wrote:

When you find a use case it doesn’t handle, consider the various container views available, and if those don’t help, definitely write a radar.  Apple is always improving things, and if you report such a case, it helps everyone.

On Sep 22, 2018, at 7:54 AM, Keary Suska <cocoa-dev@...> wrote:

Honestly, that means using autolayout because that “is thew way Cocoa works these days” but I sympathize with a strong desire to avoid it, as well as there being a number of use cases it simply can’t handle.