Background: I have a custom scrollview (subclassed) that has uiimageviews on it that are draggable, based on the drags I need to draw some lines dynamically in a subview of the uiscrollview. (Note I need them in a subview as at a later point i need to change the opacity of the view.)
So before I spend ages developing the code (i'm a newbie so it will take me a while) I looked into what i need to do and found some possible ways. Just wondering what the right way to do this.
- Create a subclass of UIView and use the drawRect method to draw the line i need (but unsure how to make it dynamically read in the values)
- On the subview use CALayers and draw on there
- Create a draw line method using CGContext functions
- Something else?
Cheers for the help
First, for drawing on iOS you need a context and when drawing on the screen you cannot get the context outside of
drawRect:
(UIView) ordrawLayer:inContext:
(CALayer). This means option 3 is out (if you meant to do it outside adrawRect:
method).You could go for a CALayer, but I'd go for a UIView here. As far as I have understood your setup, you have this:
So LineView is a sibling of ViewA and ViewB, would need be big enough to cover both ViewA and ViewB and is arranged to be in front of both (and has
setOpaque:NO
set).The implementation of LineView would be pretty straight forward: give it two properties
point1
andpoint2
of type CGPoint. Optionally, implement thesetPoint1:
/setPoint2:
methods yourself so it always calls[self setNeedsDisplay];
so it redraws itself once a point has been changed.In LineView's
drawRect:
, all you need to is draw the line either with CoreGraphics or with UIBezierPath. Which one to use is more or less a matter of taste. When you like to use CoreGraphics, you do it like this:Using NSBezierPath, it'd look quite similar:
The magic is now getting the correct coordinates for point1 and point2. I assume you have a controller that can see all the views. UIView has two nice utility methods,
convertPoint:toView:
andconvertPoint:fromView:
that you'll need here. Here's dummy code for the controller that would cause the LineView to draw a line between the centers of ViewA and ViewB:Since I don't know how you've implemented the dragging I can't tell you how to trigger calling this method on the controller. If it's done entirely encapsulated in your views and the controller is not involved, I'd go for a NSNotification that you post every time the view is dragged to a new coordinate. The controller would listen for the notification and call the aforementioned method to update the LineView.
One last note: you might want to call
setUserInteractionEnabled:NO
on your LineView in itsinitWithFrame:
method so that a touch on the line will go through to the view under the line.Happy coding !
Conceptually all your propositions are similar. All of them would lead to the following steps (some of them done invisibly by UIKit):
The expensive part of the above steps are the first three points. They lead to repeated memory allocation, memory copying, and CPU/GPU communication. On the other hand, what you really want to do is lightweight: Draw a line, probably animating start/end points, width, color, alpha, ...
There's an easy way to do this, completely avoiding the described overhead: Use a CALayer for your line, but instead of redrawing the contents on the CPU just fill it completely with the line's color (setting its
backgroundColor
property to the line's color. Then modify the layer's properties for position, bounds, transform, to make the CALayer cover the exact area of your line.Of course, this approach can only draw straight lines. But it can also be modified to draw complex visual effects by setting the
contents
property to an image. You could, for example have fuzzy edges of a glow effect on the line, using this technique.Though this technique has its limitations, I used it quite often in different apps on the iPhone as well as on the Mac. It always had dramatically superior performance than the core graphics based drawing.
Edit: Code to calculate layer properties:
2nd Edit: Here's a simple test project which shows the dramatical difference in performance between Core Graphics and Core Animation based rendering.
3rd Edit: The results are quite impressive: Rendering 30 draggable views, each connected to each other (resulting in 435 lines) renders smoothly at 60Hz on an iPad 2 using Core Animation. When using the classic approach, the framerate drops to 5 Hz and memory warnings eventually appear.