Re: needsDisplay and subviews

2017-03-10 Thread Jeremy Hughes
> On 9 Mar 2017, at 18:32, corbin dunn  wrote:
> 
>> 
>> On Mar 8, 2017, at 8:46 AM, Jeremy Hughes  
>> wrote:
>> 
>> If needsDisplay is set to true for an NSView, does that also cause subviews 
>> to be redrawn?
>> 
>> I’ve seen conflicting statements about this, but haven’t found anything in 
>> Apple’s documentation.
> 
> Just to be clear: if you want a view to be redrawn you should call 
> setNeedsDisplay on that view. That’s really the bottom line. There are some 
> cases where AppKit will redraw subviews when a parent view is invalidated, 
> but you should not depend on this!

Thanks for being clear!

> I also don’t recommend things like this:
> 
>> override var needsDisplay: Bool
>> {
>>  willSet
>>  {
>>  for view in subviews
>>  {
>>  view.needsDisplay = newValue
>>  }
>>  }
> 
> 
> Instead, it is better if your subviews invalidate themselves when their state 
> changes.

In this case I have a view that is broken up into subviews but should really be 
treated as a single view as far as redraws are concerned. When the model object 
for the view changes, the entire view needs to be redrawn. The controller for 
this view is not aware of each individual subview or what kind of view it is 
dealing with - it’s actually dealing with a more general view that is a 
superclass of different kinds of specific views. Also, the views aren’t aware 
of model states, so they can’t invalidate themselves.

The willSet override isn’t a general override that will cause all views to 
redraw whenever a superview is redrawn. It’s a specific override for a 
particular view that is really a cluster of subviews and which needs all of its 
subviews to be redrawn whenever it is invalidated.

Jeremy
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: needsDisplay and subviews

2017-03-09 Thread corbin dunn

> On Mar 8, 2017, at 8:46 AM, Jeremy Hughes  wrote:
> 
> If needsDisplay is set to true for an NSView, does that also cause subviews 
> to be redrawn?
> 
> I’ve seen conflicting statements about this, but haven’t found anything in 
> Apple’s documentation.

Just to be clear: if you want a view to be redrawn you should call 
setNeedsDisplay on that view. That’s really the bottom line. There are some 
cases where AppKit will redraw subviews when a parent view is invalidated, but 
you should not depend on this!

I also don’t recommend things like this:

> override var needsDisplay: Bool
> {
>   willSet
>   {
>   for view in subviews
>   {
>   view.needsDisplay = newValue
>   }
>   }


Instead, it is better if your subviews invalidate themselves when their state 
changes.

corbin




___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: needsDisplay and subviews

2017-03-09 Thread Quincey Morris
On Mar 9, 2017, at 02:24 , Jeremy Hughes  wrote:
> 
> that disagrees with Ben’s conclusion:
> 
> "Thus, it seems to follow that so long a custom view's display() calls super, 
> then all of its subviews should also be drawn when its needsDisplay is true.”

I don’t see it as a contradiction. The problem is that “drawing” is ambiguous.

On the one hand, “drawing” means the rendering of model data to image data. 
This is done via “draw(_:)” for a custom view. Originally this was (I think) 
the only way to generate view content, but currently there are other ways, such 
as custom layer-backed views that use the layer-update mechanism, or views with 
content supplied directly by a CGImage.

On the other hand, “drawing” means transferring content to the display. This 
involves compositing the backing stores of a view and all of its subviews into 
a single content “image”, which can then be copied to the display. Note that 
the actual process might be more complicated, because some views might not have 
a backing store. That might cause the use of “draw(_:)” for subviews, simply 
for the purpose of compositing, not because anything has changed in the 
subview’s view content.

In the case where a view needs to be “redrawn” because (say) it has been 
revealed by moving something above it, then the only drawing that’s sure to be 
needed is in the second sense. If all the content is cached in backing stores, 
no drawing in the first sense is needed. However, uncached content will require 
drawing in the first sense, too, simply to regenerate the content.

Setting “needsDisplay”, for historical reasons, does not distinguish between 
the two kinds of drawing. Therefore, it has to be understood in the first sense 
of drawing — it’s usually used to indicate that the view needs to show 
different content than it previously did. But it’s also implied that the view 
will be redrawn in the second sense, too, and *that* sometimes causes subview 
“draw(_:)” invocations for uncached content, which looks a bit like the 
recursive application of “needsDisplay”, though it really isn’t.

That’s why I said what I said: Without changes to the model, you only need to 
set “needsDisplay” at the top level view. With data model changes, you need to 
set “needsDisplay” on every view whose content is affected by the data change. 
Beyond those specific views, it’s an implementation detail whether “draw(_:)” 
is invoked for a subview.
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: needsDisplay and subviews

2017-03-09 Thread Jeremy Hughes
> On 9 Mar 2017, at 10:24, Jeremy Hughes  wrote:
> 
> So it seems that for layer-backed views, setting needsDisplay does cause the 
> view to be redrawn - but the value is never actually set. If I needed to work 
> with layer-backed views, it might be possible to get around this by 
> overriding willSet instead of didSet.

I’ve now tried this, and it works.

To ensure that a subview will be redrawn when needsDisplay is (attempted to be) 
set on its superview we can add the following code:

override var needsDisplay: Bool
{
willSet
{
for view in subviews
{
view.needsDisplay = newValue
}
}
}

Jeremy


___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: needsDisplay and subviews

2017-03-09 Thread Jeremy Hughes
> On 9 Mar 2017, at 01:49, Quincey Morris <quinceymor...@rivergatesoftware.com> 
> wrote:
> 
> So, the correct answer to your original question, I think, is that if your 
> model data has changed in such a way that the representation in custom views 
> no longer reflects the data, then you should set “needsDisplay” on *every* 
> custom subview that uses “draw(_:)”.
> 
> If you just need your changed view to be re-composited with the same subview 
> contents as previously, then you don’t need to set “needsDisplay” on the 
> subviews, just the parent view.

OK - that disagrees with Ben’s conclusion:

"Thus, it seems to follow that so long a custom view's display() calls super, 
then all of its subviews should also be drawn when its needsDisplay is true.”

But maybe it is covered by the “as necessary” clause in Apple’s documentation:

display(): "Displays the view and all its subviews if possible, invoking each 
of the NSView methods lockFocus(), draw(_:), and unlockFocus() as necessary.”

I have a custom view that has two custom subviews, and is a subclass of a more 
general custom view. The code that tells the general (superclass) view that it 
needs to be redrawn shouldn’t have to know which kind of (subclass) view it is 
dealing with. So it would be necessary for the subclass view to override 
needsDisplay with an observer that sets needDisplay to be true for its subviews:

override var needsDisplay: Bool
{
didSet
{
for view in subviews
    {
    view.needsDisplay = needsDisplay
}
}
}

Currently, I don’t actually need to do this because I’m not trying to use 
layer-backed views (that happened accidentally through a bug or unexpected 
behaviour in Interface Builder), but the fact that Apple is unclear about how 
needsDisplay affects subviews means that I probably should do this in case they 
introduce new drawing optimisations in future versions of macOS.

The other puzzle in all this is why it should be the case that setting 
needsDisplay to be true on a layer-backed view actually doesn’t set it to be 
true:

> One other strange behaviour with the problem xib occurred when I added some 
> debugging code to try and find out why the subview wasn’t being redrawn:
> 
>   view.needsDisplay = true
>   print("view.needsDisplay: \(view.needsDisplay)”)
> 
> and this consistently printed “ view.needsDisplay: false” after 
> view.needsDisplay had been set to true. Despite this, “draw” (= drawRect in 
> ObjC) was being called for “view” - but not for view’s subviews.

Before I discovered that IB had changed one of my views to be layer-backed, I 
tried using the didSet observer to pass on needsDisplay to subviews and found 
that it was setting the value to be false for subviews when I had set it to be 
true for the superview (that’s why I added the debugging code above).

So it seems that for layer-backed views, setting needsDisplay does cause the 
view to be redrawn - but the value is never actually set. If I needed to work 
with layer-backed views, it might be possible to get around this by overriding 
willSet instead of didSet.

I think NSView needs a “Redraws subviews” property (similar to “Autoresizes 
subviews”) that will take away the guesswork and hackery that we currently need 
to use in order to get some kind of consistent behaviour.

Jeremy


___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: needsDisplay and subviews

2017-03-08 Thread Quincey Morris
On Mar 8, 2017, at 15:25 , Jeremy Hughes <moon.rab...@virginmedia.com> wrote:
> 
> My conclusion (for now) is that “needsDisplay” causes subviews to be redrawn 
> - except when “wantsLayer” has been set to true.
> 
> Does that make sense?

Sure. In some cases, such as when using layers, the result of your “draw(_:)” 
methods for custom subviews can be skipped as an optimization, since their 
output might have been cached earlier in a backing store.

“needDisplay” applies to the view(s) for which it is set. That means the 
“draw(_:)” method is called for that view (or those views). However, in a later 
part of the screen update process, this new drawing output must be composited 
with subview drawing output.

For subviews that didn’t have “needsDisplay” set, this compositing can be done 
with the existing backing store, if available. Otherwise, the subview 
“draw(_:)” methods have to be called too, simply to (re)create the subview 
contents. It can be difficult to predict which drawing paths will be taken.

One situation where subviews may already be cached is when you are using 
layers, since layers *are* drawing caches.

So, the correct answer to your original question, I think, is that if your 
model data has changed in such a way that the representation in custom views no 
longer reflects the data, then you should set “needsDisplay” on *every* custom 
subview that uses “draw(_:)”.

If you just need your changed view to be re-composited with the same subview 
contents as previously, then you don’t need to set “needsDisplay” on the 
subviews, just the parent view.
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: needsDisplay and subviews

2017-03-08 Thread Jeremy Hughes
The situation in which subviews were not being redrawn seems to have been a xib 
problem. After spending some time getting nowhere I decided to recreate the xib 
by taking a different xib (which used the same custom view) and cutting it 
down. This worked without any problems. Then I compared the two xibs in 
TextWrangler and found that the one which didn’t work had a view where 
“wantsLayer” was set to YES - and this corresponded to a “Core Animation Layer” 
checkbox in Interface Builder.

I didn’t intentionally select this checkbox, and I didn’t originally look at 
this pane (View Effects) when I was trying to spot differences between the view 
that worked and the view that didn’t. But in my first attempt at creating this 
xib (where the problem occurred) I copied and pasted a view from another xib, 
whereas in my second attempt I duplicated the entire xib and modified it - so 
maybe the checkbox property was set as a side effect of copying and pasting.

One other strange behaviour with the problem xib occurred when I added some 
debugging code to try and find out why the subview wasn’t being redrawn:

view.needsDisplay = true
print("view.needsDisplay: \(view.needsDisplay)”)

and this consistently printed “ view.needsDisplay: false” after 
view.needsDisplay had been set to true. Despite this, “draw” (= drawRect in 
ObjC) was being called for “view” - but not for view’s subviews.

My conclusion (for now) is that “needsDisplay” causes subviews to be redrawn - 
except when “wantsLayer” has been set to true.

Does that make sense?

Jeremy

--

> On 8 Mar 2017, at 17:12, Jeremy Hughes <moon.rab...@virginmedia.com> wrote:
> 
> Thanks - that seems reasonably clear, and it agrees with what I previously 
> thought, but I’ve got a situation where it doesn’t seem to be happening.
> 
> There are contradictory opinions on Stack Overflow: 
> 
> http://stackoverflow.com/questions/8718290/setneedsdisplay-and-subviews
> http://stackoverflow.com/questions/11480341/setneedsdisplay-does-not-trigger-drawrect-in-subviews-as-expected
> 
> Jeremy
> 
> --
> 
>> On 8 Mar 2017, at 17:01, Ben Kennedy <b...@zygoat.ca> wrote:
>> 
>> 
>>> On 08 Mar 2017, at 8:46 am, Jeremy Hughes <moon.rab...@virginmedia.com> 
>>> wrote:
>>> 
>>> If needsDisplay is set to true for an NSView, does that also cause subviews 
>>> to be redrawn?
>> 
>> Admittedly I've been mostly doing iOS development for the last several years 
>> and barely any Mac lately, but, according to the current docs:
>> 
>> needsDisplay: "The displayIfNeeded methods check the value of this property 
>> to avoid unnecessary drawing, and all display methods set the value back to 
>> false when the view is up to date."
>> 
>> display(): "Displays the view and all its subviews if possible, invoking 
>> each of the NSView methods lockFocus(), draw(_:), and unlockFocus() as 
>> necessary."
>> 
>> Thus, it seems to follow that so long a custom view's display() calls super, 
>> then all of its subviews should also be drawn when its needsDisplay is true.
>> 
>> What sort of contradictions are out there?
>> 
>> b
>> 
> 


___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: needsDisplay and subviews

2017-03-08 Thread Ben Kennedy

> On 08 Mar 2017, at 8:46 am, Jeremy Hughes  wrote:
> 
> If needsDisplay is set to true for an NSView, does that also cause subviews 
> to be redrawn?

Admittedly I've been mostly doing iOS development for the last several years 
and barely any Mac lately, but, according to the current docs:

needsDisplay: "The displayIfNeeded methods check the value of this property to 
avoid unnecessary drawing, and all display methods set the value back to false 
when the view is up to date."

display(): "Displays the view and all its subviews if possible, invoking each 
of the NSView methods lockFocus(), draw(_:), and unlockFocus() as necessary."

Thus, it seems to follow that so long a custom view's display() calls super, 
then all of its subviews should also be drawn when its needsDisplay is true.

What sort of contradictions are out there?

b


___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


needsDisplay and subviews

2017-03-08 Thread Jeremy Hughes
If needsDisplay is set to true for an NSView, does that also cause subviews to 
be redrawn?

I’ve seen conflicting statements about this, but haven’t found anything in 
Apple’s documentation.

Jeremy


___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com