Re: needsDisplay and subviews
> 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
> 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
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
> 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
> On 9 Mar 2017, at 01:49, Quincey Morris > 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
On Mar 8, 2017, at 15:25 , Jeremy Hughes 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
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 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 wrote: >> >> >>> 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
Re: needsDisplay and subviews
> 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
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