UIView setNeedsDisplay calling the CALayer's setNeedsDisplay? - objective-c

It seems that calling [self setNeedsDisplay] on a UIView does not result in a
[self.layer setNeedsDisplay]. Is it alright if I override setNeedsDisplay as follows:
- (void)setNeedsDisplay {
[super setNeedsDisplay];
[self.layer setNeedsDisplay];
}
Will this get me into trouble? Perhaps there is a better way?
Thank You

I posted this as a comment to another answer, but this may be helpful to other people.
A UIView only calls setNeedsDisplay on its underlying CALayer when drawRect: is implemented (even if the method is empty and does nothing). This tells UIView's setNeedsDisplay that its layer also has some drawing to be done.
Another way to get the layer's setNeedsDisplay to be called automatically is to set the layer's flag needsDisplayOnBoundsChange to true, which will trigger a setNeedsDisplay whenever the view's bounds change (which automatically changes the layer's bounds). But to answer your questions, what you're doing is fine if you didn't override drawRect:, otherwise it's redundant.

I don't see why it would get you into trouble. setNeedsDisplay / setNeedsLayout just flag that the view/layer needs to be displayed / laid out. It'll do the drawing next time around the run loop.
I'm fairly surprised though that calling it on the view doesn't cause it to happen on the layer, because after all a UIView and its associated CALayer are quite tightly integrated.
So overall, yeh if you're finding it's not working for you by just calling setNeedsDisplay on the view then go for your solution. I don't see how it can harm anything.

Related

Why isn't drawRect: getting called?

I have an NSView subclass foo which overrides drawRect:
- (void)drawRect:(NSRect)dirtyRect
{
[self doStuff];
}
I would have expected that whenever I added an instance bar of my class as a subview of another view, bar would be redrawn. However, when I add bar as the contentView of the main window in my application, I'm not seeing this to be the case.
I tried adding another override:
- (void)viewDidMoveToSuperview
{
self.needsDisplay = YES;
}
...in hopes that when bar gained a superview, it would invalidate its display and force a redraw. No such luck (viewDidMoveToSuperview is being called, but not drawRect:).
I think I may be misunderstanding how drawRect: gets called, and I admit that I don't have the best understanding of the drawing event loop in general. I couldn't find a lot of documentation online about this.
So my question is two-fold: 1) What am I doing wrong in regards to getting drawRect: called? 2) Is there any relevant online documentation regarding the event loop which draws UI in cocoa?
EDIT:
In my UI chain, the instance of foo which isn't getting drawRect: called on it has two subviews: one which is essentially a subclass of NSTextView which sizes to its content, and another which is an NSScrollView. By sheer force of luck, I happened to comment out the code which added the NSScrollView, and suddenly the instance of foo had drawRect called on it again.
So somehow my NSScrollview is keeping my foo from receiving drawRect: . From a quick Google search, I found that other people have had reported problems related to drawing and NSScrollView. However, most of those posts seemed related to subviews within NSScrollView, rather than superviews of NSScrollView.
So here's my newest question: what about my instance of NSScrollView is preventing its parent from receiving drawRect:?
Did you try to invoke [view display]; instead of needsDiplay ?

NSView ordering not maintain

I have two NSView subclass objects. One i have already added to window content view. Other I have added as a subview below the first subview as below.
[window.contenView addSubview:secondSubview positioned:NSWindowBelow relativeTo:firstView];
[secondSubView setWantsLayer:YES];
When I change the secondSubview to frame using animator, it animates above firstSubview.
As the secondSUbview lies below the firstSubview, should it not animate below the firstSubview.
you misunderstood the point of my comment so I'll post this here as an answer.
You need to put a layer on the superview doing so will put a layer on all the subviews and should fix the problem.
I even found this other SO question and answer that verifies what I was trying to tell you.

setNeedsDisplay does not trigger drawRect in subviews as expected

I'm struggling with setNeedsDisplay. I thought it was supposed to trigger calls of drawRect: for the view for which it is called and the hierarchy below that if it's within the view's bounds, but I'm not finding that to be the case. Here is my setup:
From the application delegate, I create a view whose size is a square that covers essentially the whole screen real estate. This view is called TrollCalendarView. There is not much that happens with TrollCalendarView except for a rotation triggered by the compass.
There are 7 subviews of TrollCalendarView called PlatformView intended to contain 2D draw objects arranged around the center of TrollCalendarView in a 7-sided arrangement. So when the iPad is rotated, these 7 views rotate such that they are always oriented with the cardinal directions.
Each of the PlatformView subviews contains 3 subviews called Tower. Each tower contains 2D draw objects implemented in drawRect:.
So, in summary, I have TrollCalendarView with empty drawRect:, and subviews PlatformView and Platformview -> Tower that each have drawRect implementations. Additionally, Tower lies within the bounds of Platform, and Platform lies within the bounds of TrollCalendarView.
In TrollCalendarView I've added a swipe recognizer. When I swipe happens, a property is updated, and I call [self setNeedsDisplay] but nothing seems to happen. I added NSLog entries to drawRect: method in each of these views, and only the TrollCalendarView drawRect: method is called. Ironically, that is the one view whose drawRect method will be empty.
There is no xib file.
What do I need to do to ensure the drawRect method in the other subviews is called? Is there documentation somewhere that describes all the nuances that could affect this?
I'm struggling with setNeedsDisplay. I thought it was supposed to trigger calls of drawRect for the view for which it is called and the hierarchy below that if it's within the view's bounds
No, that is not the case. Where did you get that idea?
-setNeedsDisplay: applies only to the view to which it is sent. If you need to invalidate other views, you need to add some code to send -setNeedsDisplay: to them, too. That's all there is to it.
I think this is an optimization in the framework; if your subviews don't need to draw again, then this is a major performance improvement. Realize that almost anything animatable does not require drawrect (moving, scaling, etc).
If you know that all of your subviews should be redrawn (and not simply moved), then override setNeedsDisplay in your main view and do like this:
-(void) setNeedsDisplay {
[self.subviews makeObjectsPerformSelector:#selector(setNeedsDisplay)];
[super setNeedsDisplay];
}
I have tested this, and it causes all subviews to be redrawn as well. Please note that you will earn efficiency karma points if you somehow filter your subviews and make sure you only send that to subviews which actually need redrawn... and even more if you can figure out how not to need to redraw them. :-)

drawRect and addSubview: custom drawing affects which views?

If I have a custom subclass of UIView that implements drawRect and controller methods use addSubview to create a view hierarchy in this custom view, how does drawRect interact with these subviews? Does it recreate the entire subclass's view hierarchy from scratch and remove any existing subviews? Or does it ignore subviews and only redraw a particular view/subview?
Would it be acceptable to programmatically add and remove subviews within drawRect?
drawRect is meant to be only for drawing your content in the view.
Whether it draws the entire view or part of it: It depends on your implementation. If you want to do any optimization is a good idea to check when your view calls drawRect and adjust the code accordingly (maybe you want to update only one part of the view, maybe you don't want to draw all the times, etc). It depends on your needs
I don't think is a good idea to add/remove subviews within drawRect because this method will be called in several situations and I dare to say that is NOT what you want :)
Instead, you could try something like this:
[myView addSubview:aSubview];
[myView setNeedsDisplay];
//or calculate the needed display rect by yourself and then
[myView setNeedsDisplayInRect:aRect];
-drawRect: doesn't interact with subviews. It draws whatever the view it's sent to wants to draw in the rect it's given.
Would it be acceptable to programmatically add and remove subviews within drawRect?
NO. -drawRect: is for drawing, not for manipulating the view hierarchy.

UIView. How do I redisplay a "data driven" UIView?

I am doing a scientific visualization app for iPhone. Periodically a UIView subclass will receive some data from the network that it must render via its overridden drawRect: method.
My challenge is that there is no way to indicate to Cocoa-Touch that this data has arrived - or changed - other then [myView setNeedsDisplay] which is ignored except for changes to the bounds of the UIView. I have tried hiding and unhiding. Nuthin'.
Maddeningly, since all I did was alter some internal state of the UIView [myView setNeedsDisplay]. This change is completely invisible to Cocoa-Touch. This change is not one of the criteria that warrents a redraw - according to Cocoa-Touch - thus my UIView sits there, unchanged.
This is very, very, very frustrating. I have hit a wall here.
Could someone please suggest a technique, a trick, a hack, that will prompt my UIView to re-render.
Cheers,
Doug
[myView setNeedsDisplay] should cause drawRect: to be sent to myView the next time you're back in the run loop and myView is visible. If changing the bounds is causing the view to redraw (myView.contentMode is UIViewContentModeRedraw), then setNeedsDisplay must be working, since that's how the redraw is signalled by the bounds change. See the UIView class reference for details.
Is your drawRect: being invoked the first time the view is shown? If not, it may be misspelled, overridden in a subclass, or even on the wrong object.
Is the view visible and on screen? It won't be drawn if it is off screen.
Is control returning to the event loop? drawRect: won't be invoked if your application is busy.