UIView force redraw in real-time (drawing app) - iphone

I'm working an app for drawing.
I'm collecting the strokes as points and draw those points into the drawRect method of my UIView subclass.
I collect the point by overriding touchesBegan, touchesMoved and touchesEnded.
In all this those methods I call [self setNeedsDisplay] and i expect to see the strokes in realtime... it doens't happen, acutally the drawRect methods is called only after touchesEnded
I need a way to do not simply mark a view as discarded but i need acutally to force the system to perform the drawRect call.
Any idea ?

Is it possible you're calling setNeedsDisplay on your viewcontroller (self) and not the view (self.view)?
Also you should probably use setNeedsDisplayInRect to only update the part of the view that changes during the touch.

Related

Subclassed UIView shows black background if drawRect: is overridden

I have a custom class which I derived from the UIView class. What I want to accomplish is just drawing vertical black lines with a predefined distance between. When I override the drawRect method and write my code to draw vertical lines, the view just shows a complete black background. Then I noticed that even an empty overridden drawRect which only calls the superclass' drawRect results in a black background. When I comment out the drawRect, then the view shows through and becomes transparent as I expected.
This is the only code I am using in the drawRect which just calls the superclass method:
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
}
What can be reason of this behavior?
It's quite likely that there is some under-the-hood shenanigans going on with UIView, and that the base class doesn't actually implement drawRect, or some sneaky optimisation is happening.
The documentation does say not to call super if you are directly overriding UIView, and it also says you don't need to implement drawRect if all you do is set a background colour or you populate the content in other ways:
The default implementation of this method does nothing. Subclasses that use native drawing technologies (such as Core Graphics and UIKit) to draw their view’s content should override this method and implement their drawing code there. You do not need to override this method if your view sets its content in other ways. For example, you do not need to override this method if your view just displays a background color or if your view sets its content directly using the underlying layer object. Similarly, you should not override this method if your view uses OpenGL ES to do its drawing.
And:
If you subclass UIView directly, your implementation of this method does not need to call super. However, if you are subclassing a different view class, you should call super at some point in your implementation.
It's therefore quite likely that by calling super you are losing something. You don't show any of your actual drawing code so its difficult to see where you might be going wrong with that, but as a starting point, don't call super if you've directly overridden UIView.

drawRect causes slow scrolling even though it is not being called

In my app I have a scrollView with about 20 subviews in it. Each of these subviews has a drawRect method that at the moment looks like this:
- (void)drawRect:(CGRect)rect
{
NSLog(#"drawRect called");
}
When the subviews are added, drawRect is called, however when I scroll it is very slow even though drawRect is not called again.
If I remove the implementation for drawRect, then scrolling is completely normal. Even if I can't get rid of the slow scrolling, is there an alternative to drawRect which I could use instead?
Why are you calling drawRect if it is only logging that it was called? If that is it's only purpose for you, then just don't call it. In fact, I believe when you first create a class that inherits from UIView that has the drawRect method in it, it is commented out and above the commented out drawRect method, it says something along the lines of "Do not call this method if it does not do any drawing on screen as it takes up a significant amount of memory". Basically, don't call it in your case.
Hope this helps.
You could try assigning pre-drawn CGImages to the contents of each custom view's CALayer, before the view first appears. This may be faster than using a drawRect to customize a view's appearance.

Is drawrect more efficient for coregraphcs drawing than using core graphics in touches moved?

My whole app uses touchesMoved for drawing (Coregraphics). Should I be calling [self setNeedsDisplay] from within touchesMoved instead of performing the drawing code in there? Would that stop some lag that I am experiencing? The only reason I didn't try this before posting is because it is going to take me a few hours to convert all drawing to within drawRect and I wanted to know if it would be more would efficient than drawing in touchesMoved?
Yes. You should always keep your drawing in drawRect and use setNeedsDisplay to trigger the actual drawing. This is key to the rendering of views in Cocoa Touch.
For example, if you didn't use this method, you could potentially have drawing code scattered around your class in multiple methods. In addition, this also ensures that your drawing code executes only once during a render cycle. Essentially, calling setNeedsDisplay triggers an invalidation flag that lets your UIView know to redraw itself. Without this, you could be performing extra drawing operations that aren't actually needed due to the render cycle.
You should not do drawing inside a UI callback. It will slow down or freeze the UI responsiveness, and you won't have the proper drawing context if you want to draw directly into a UIView.
Instead save, schedule or queue the information required for drawing for later, either into your own bitmap drawing context, or during the drawRect callback for that view.
Depending on the frame rate required, you many not even need to do a setNeedsDisplay on every touchesMoved event, but can queue the data and set a flag for an animation rate timer or CADisplayLink callback to do the setNeedsDisplay at a more constant rate (say only 15 or 30 frames per second). This will also help eliminate extra drawing operations.

touchesEnded: not detecting end of touch properly?

I'm using the touchesEnded: method to do some work when I lift a finger off my UIScrollView, but my problem (and i've confirmed using NSLog) is that the touchesEnded: method seems to only get called when I tap on my scroll view and not when I touch and hold/slide my finger and then let go?
Is there another method I need to use? (btw i'm calling super as well)
I need a way to do stuff as soon as the user removes their fingers off the view
When you simply tap, the scroll view will pass touches through to its subviews. But if you start dragging, the scrollview will send a touchesCancelled message to the subview and process the touches itself. Check out the methods on UIScrollViewDelegate - there's probably something there you can use.
Alternatively, UIScrollView has a property canCancelContentTouches. If you turn that off, its subviews will always receive touches, but of course then the scroll view won't scroll.

Drawing a CGLayer outside the method drawRect: faster?

I'm not really sure, what am I asking...
...but if make layer composites in separate methods, and draw them to the view also outside of the drawRect: method... ...then could my app performance get raised?
I'm actually subclassed a puzzlePiece:UIView class, where the puzzlepiece gets rendered, but the redraw invoked now with setNeedsDisplay since the drawing implementation takes place in the drawRect: method. The app lags.
Can I improve performance if I get rid of the so called drawRect: method?
The thing wants to get implemented: http://gotoandplay.freeblog.hu/
I think what you want to do here is to have a very single, simple UIView that covers the entire play area. Then put each tile in a CALayer, and attach all the CALayers to the UIView and move them around and rotate them. Then there should not need to be a -drawRect: at all. You'll do all your drawing in your layers, and you should get the best performance that way. CALayer is similar to UIView in principle, but much lighter weight (faster and simpler). CALayer is basically a view that can only draw; it can't handle touches or events. It's parent view has to do that for it. But that let's CALayer be much faster.