When do I need to call setNeedsDisplay in iOS? - ios

When creating an iOS app, I'm confused as to when exactly I need to call setNeedsDisplay? I know that it has something to do with updating/redrawing the UI; however, do I need to call this every time I change any of my views?
For example, do I need to call it:
After programatically changing the text in a text field
When changing the background of a view?
When I make changes in viewDidLoad?
How about in viewDidAppear?
Could someone give me some general guidelines regarding when to use this method?

You should only be calling setNeedsDisplay if you override drawRect in a subclass of UIView which is basically a custom view drawing something on the screen, like lines, images, or shapes like a rectangle.
So you should call setNeedsDisplay when you make changes to few variables on which this drawing depends and for view to represent that change , you need to call this method which internally will give a call to drawRect and redraw the components.
When you add an imageView or a UIButton as a subview or make changes to any subview, you need not call this method.
Example:
You have a view that shows a moving circle, either you touch and move it, or may be timer based animation.
Now for this, you will need a custom view that draws a circle at given center and with given radius.
These are kept as instance variables which are modified to move the circle by changing its center or make it bigger by increasing radius of it.
Now in this case either you will modify these variables(centre or radius) in a loop and timer Or may be by your fingers in touchesEnded and touchesMoved methods.
To reflect the change in this property you need to redraw this view for which you will call setNeedsDisplay.

You only really need to call -setNeedsDisplay on UIView subclasses that draw their contents with -drawRect:.
For labels and other standard controls, changing the text will automatically cause the label to redraw so you don't need to do this yourself.

setNeedsDisplay: should be called when you want to refresh your view explicitly. It just sets an internal flag, and the iOS UI system will call drawRect: at an appropriate time later.
It sounds like it should be always called when you updating any property which may change the presentation. But it's not. Almost all the standard UI controls already handled that. I believe whenever you modify the properties of standard UI components (views), setNeedsDisplay: would be triggered internally, and the affected region will be redrawn. (In all the situations you listed)
However, if you create your own view, implement its own drawRect:, and want to update that when something has been changed, you must call setNeedsDisplay: explicitly.

I think #Amogh Talpallikar make it clear. And I just wanna discuss one thing more.
In the fact that, you should avoid override drawRectunless you really need it because it can cause bad performance. You can refer this https://yalantis.com/blog/mastering-uikit-performance/
If you only wanna change frame, position of buttons, labels, ... you can call setNeedLayout or layoutIfNeeded

You will call setNeedDisplay when you are changing the property on which your view custom drawing depends. It will explicitly call drawRect: method forcefully.

Related

How to make UIViews transition to visible state in a fade-in fashion?

The question might seem confusing so let me clarify.
I am working on a very heavily content driven app. Content is obtained asynchronously and there are often dozen or more elements on screen that represent content. Images, text...etc...all these are implemented via UIView subclasses.
I'd like to be able to set per app level (via appearance proxy or some other method), that once the view is ready for being rendered visible, it should fade in instead of just show up suddenly after the next render pass.
Anybody any ideas? Is there some CALayer property that would allow this easily?
Drawrect? Override hidden? A convenience public wrapper function using UIView animations internally?
Once again, I know how to do this on individual level using UIView animations methods. What I want is a global change of behaviour for all UIViews.
You can create a UIView subclass with a property readyForRendering and do the fade-in animation (which you already know how) when it is set to true. Make your existing UIView subclasses subclass of this new UIView subclass and set readyForRendering as needed.
This isn't something that can be done via appearance proxies, but you can subclass UIView and override its method (or create an extension with a new method) addSubview() method to perform the animation.

How can I get a callback when an arbitrary UIView redraws itself?

I would like to write a class that applies an effect to a UIView, similar to UIVisualEffectView. I would also like this view to update live -- if the "target" view redraws itself, my view should as well. Obviously I can just redraw on every frame using a CADisplayLink or NSTimer, but I would prefer not to waste the user's battery unnecessarily.
I see that CALayer/UIView objects have a needsDisplay boolean object, but this seems to be something that the client sets, rather than the system exposing it, so it doesn't seem to be any help.
Is it possible to do this?

+[UIView transitionFromView:toView:...] with multiple views

So here's the situation:
I have let's say 20 views who are all subviews of the same view. Now I want to remove and add new views in an animated matter.
I previously did this by using regular UIView animations and fading them out or in respectively. The problem is though that the animation isn't flawless when the new subview who is to be inserted overlaps with the subviews who are fading out. So I tried using the +[UIView transitionFromView:toView:...] animation block to make the animation cross dissolve. This works well, animation looks good.
This basically solves my issue. The only problem is that it might be that I want to replace 2 subviews with one bigger subview. This doesn't really work out as I can't just pas nil to the transition method.
So my question basically is how I can simultaneously cross dissolve multiple subviews no matter how many views are animated?
Thanks for your help!
Have you tried putting the views to remove inside a transparent bigger view? So you just call transitionFromViewToView using the container with the 2 little ones inside and the big one that is entering?
You can use + [UIView transitionWithView:duration:options:animations:completion:], passing the common superview (usually self.view of your view controller) to perform all animations at once.
Excerpt from the doc:
This method applies a transition to the specified view so that you can
make state changes to it. The block you specify in the animations
parameter contains whatever state changes you want to make. You can
use this block to add, remove, show, or hide subviews of the specified
view. If you want to incorporate other animatable changes, you must
include the UIViewAnimationOptionAllowAnimatedContent key in the
options parameter.

iOS UIView derived view instance drawRect being called but not updating on screen?

Was hoping someone could help with this please? I've scanned through the forum and apple docs and can't find anything that matches my problem exactly.
I'm trying to animate a custom 'Composite' UIView derived class (with overidden drawRect) by changing a custom colour property (not one of the standard view properties like centre, alpha, backgroundColour etc), calling setNeedsDisplay and redrawing the view using drawRect over X seconds duration to produce a slow colour change from say, red to blue.
At the moment, all Composite::drawRect does internally is clear the context (the view has a default background colour set to clear) and fill the view using the colour provided. This works fine the first time it's called - the view appears on screen in the correct red colour.
My problem is that when I try to animate the view by updating the colour property, even though drawRect is being called everytime I call setNeedsDisplay and the colour being fed into the view instance is correct the view doesn't update on screen until I stop calling setNeedsDisplay (or rather, stop updating the custom view colour property). It's like every call to setNeedsDisplay pushes the view to the back of a list so it doesn't get updated on screen until it stops getting called (even though drawRect is being called).
For example - trying to change the colour from red to blue over 10 seconds, the view stays red for 10 seconds, then turns to blue in a single frame at the end of the property changing (and presumably when I stop calling setNeedsDisplay) rather than a slow fade between the two over the time duration.
Any ideas what's going on? I'm guessing I need to animate this using apple's own animation property stuff but I'd really prefer not to - I'm trying to make this cross platform if possible and I don't see why my own method shouldn't work.
The basics:
In case it matters, my composite view instance is a subview of an UIImageView. (I know imageview drawRect doesn't get called but I can't imagine this is a problem for it's subviews - after all, my Composite::drawRect is definitely being called).
I'm setting the right colour on my view instance - I've debug printed this to be sure - so the colour provided to the view goes from red to blue over however many seconds / frames.
I'm setting setNeedsDispay and drawRect is being called every time - again, debug printing confirms this.
This is happening over multiple frames and the app goes through the drawing update at the end of every frame - I.e I'm not blocking the render thread or anything - the app runs fine, it just doesn't update the view on screen even though its drawRect is being called, until I stop manipulating the property. Someone mentioned a similar problem where they were blocking the render thread in a single frame but I'm definitely not doing anything like that.
I've tried messing around with the following but they all produced the same results (i.e. view changed from red to blue in a single frame rather than a slow change over).
Updating the colour every frame but only calling setNeedsDisplay on my view every Xth frame (5, 10, 20, 30 etc)
Updating the colour every frame but not calling setNeedsDisplay unless the corresponding drawRect call from the last setNeedsDisplay has happened (i.e. set a flag to false in SetColour func, set same flag to true in drawRect, test against it when decided to call setNeedsDisplay) - same results.
setting the view bounds with ContentMode set to redraw to force an update - same results
tried calling setNeedsDisplay on the parent image view too - no change.
I'm guessing that this just isn't possible - can anyone explain why its not updating the v view onscreen even though drawRect is being called? Any ideas how I should do this? I don't think I would be able to do what I want to do if I tried animated one of the default view properties using apple's internal animation system but if I've got to use that....
Thanks!
:-)

When is the right time to animate and add sublayers in an UIView subclass

If I create a subclassed UIView and want to use a bunch of custom made content (animations via CABasicAnimation using CAShapeLayers, custom made drawings using CGContextRef in drawRect) when is the right time to create, add and animate sublayers?
I know that I should perform custom drawings in the drawRect method (and since there is the only place when I can actually get UIGraphicsGetCurrentContext() that kinda narrows my choice down). Now I have been creating sublayers, animations and all that other non-drawing related stuff in the drawRect as well. But I'm not sure that drawRect is the best place to do those kind of activities.
I am familiar with layoutSubviews method and some other UIView methods but haven't actually implemented any of them except drawRect.
So if I repeat myself one more time - the question goes: Where to add sublayers, where to animate them and are there any tricks or catches I should be aware of?
You can create and add the layers at init, if they are permanent. If they are dynamic, layoutSubviews is better. This obviously means you need to setNeedsLayout whenever a new layer/item is required. You can mix and match as much as you want between -init and -layoutSubviews, but if I had to pick, I'd say lean toward using layoutSubviews. Don't use drawRect.
You can set properties (strokeWidth, lineColor, path, etc) to a CAShapeLayer at either the time of creation or during normal execution. This includes setting a path to CAShapeLayer. Again, don't set properties in drawRect.
If you want to do custom drawing on the layer you can subclass a layer and use drawRect on that layer. This can be good if the CALAyers need to be reused and extended. You can also supply a CALayerDelegate, as long as its not the UIView. See these questions: Using CALayer Delegate AND
iOS: Using UIView's 'drawRect:' vs. its layer's delagate 'drawLayer:inContext:'
Animation is easy, and it follows the same principles as creating and setting properties. Make sure you understand when automatic animations will be invoked and how to disable them: Disabling implicit animations in -[CALayer setNeedsDisplayInRect:]
Again don't try to animate from drawRect: How to make my UIBezierPath animated with CAShapeLayer?
Drawing is expensive. Animation is cheap. Using drawRect too much will cause a big performance hit to your applications. Use drawRect/setNeedsDisplay sparingly, for instance on a state change (selected/unselected). Whenever possible modify views with animations or top level properties, and don't redraw the view. Making drawRect do anything OTHER than draw could result in you calling setNeedsDisplay unnecessarily. An easy upfront optimization is to call drawRect as little as possible, or not at all.

Resources