iPhone: draw the objects along the curve - iphone

Good day, friends.
There is a task: to draw repeating objects (UIImageView and UILabel) along the curve (if more exactly, it's an arc).
What classes should be used for it?

As an alternative to Android's Path, you can use either UIBezierPath or CGPath. The former is Objective-C, the later pure C. I recommend going with UIBezierPath as it's easier to use.
To rotate a view, use the transform property of UIView (see this question for an example on how to use it). But note that you then need to ignore the frame (it's undefined) and need to move the view by modifying the center instead.

Related

How to change the shape of a UIButton in Swift?

I am trying to make an arrow shape. I understand that it will be done with CGRect method but can you give a complete example of how it will be done in SWIFT?
This can be done programmatically by first subclassing UIButton and overriding drawRect within it to form the shape of an arrow via bezier paths. Then simply set the title of the button to be whatever you want your text to be and position it accordingly within the shape you have drawn.
The benefit of doing it this way is that you can easily change the color of, scale and dimensions of the arrow since you control how the object is actually being drawn.
A good tool for drawing complex bezier paths is paintCode. I'm using the trial version and can pretty easily make shapes like this now.

How to detect if the touch point is contained by draw content of CALayer?

There are three layers added to UIView. One layer draws a rectangle. One draws a circle. One draws a polygon. The layer's opacity is no. When I touched the polygon, I want to get the correct layer which draws the polygon. And the three layers are full filled to the view. I have implemented this. But I don't know if we have better solution to solve it .My way is like this:
1.Drawing the content using -drawLayer:inContext. store the CGPath that you used.
2.In the UIView's -touchedEnded:withEvent method. using CGPathContainsPoint() to detect if the touch point is contained by the CGPath.
Maybe this is the stupid way to solve this. Anyone who can tell me how to solve it better?
If you need an accurate hit test for path's I'm afraid you have to check/iterate the layer hierarchy yourself if the point is inside your path using CGPathContainsPoint as you suggested.
While iterating you could optimize it by skipping layers where the point is outside their frame.
For less fine grained control you can get the touched layer by using CALayers
- (CALayer *)hitTest:(CGPoint)thePoint
method.
If you have a layer hierarchy with a nesting level < 1000 (which is almost always true) I would not worry too much.

Most efficient way for partially drawing an UIView in drawRect:

I'm using setNeedsDisplayInRect: as much as possible in my UIView subclass, but I don't know what to do in drawRect:. What is the best way to detect which parts of the UIView have to be drawn? Right now I've divided my UIView into several CGRect's. For each CGRect I'm calling CGRectContainsRect() to check whether that CGRect needs to be drawn or not. Is that the correct way, or is there a better way?
You'll want to draw any rect that is even partially in the invalidated rect, so you'll want to use CGRectIntersectsRect(). Having said that, I've seen few cases where it makes sense to partially draw UIViews -- unless you have a very large single view inside of a scroll view, you may as well draw the whole thing.
You're looking at a classic spatial partitioning task. Depending on your view complexity, you can use different strategies to find out what needs to be drawn. If your scene is very simple, then drawing everything or partitioning the scene into discrete areas and doing your bounding rectangle check is fine. For very complex scenes, there are several good data structures (such as octrees) for representing your scene as a tree, and performing spacial queries for objects within a given region.
Check out the topic of Spacial Partitioning on Wikipedia: http://en.wikipedia.org/wiki/Space_partitioning

The best way to implement drawing features like Keynote

I'm trying to make a little iPad tool's for drawing simple geometrical objects (rect, rounded rect, ellipse, star, ...).
My goal is to make something very close to Keynote (drawing feature), i.e. let the user add a rect (for instance), resizing it and moving it. I want too the user can select many objects and move them together.
I've thought about at least 3 differents ways to do that :
Extends UIView for each object type, a class for Rect, another for Ellipse, ... With custom drawing method. Then add this view as subview of the global view.
Extends CALayer for each object type, a class for Rect, another for Ellipse, ... With custom drawing method. Then add this layer as sublayer of the global view layer's.
Extends NSObject for each object type, a class for Rect, another for Ellipse, ... With just a drawing method which will get as argument a CGContext and a Rect and draw directly the form in it. Those methods will be called by the drawing method of the global view.
I'm aware that the two first ways come with functions to detect touch on each object, to add easily shadows,... but I'm afraid that they are a little too heavy ? That's why I thought about the last way, which it seems to be straight forward.
Which way will be the more efficient ??? Or maybe I didn't thought another way ?
Any help will be appreciated ;-)
Thanks.
I'd use the UIKit classes to do your drawing, then profile and optimise your code from there.
Apple/iPad info: link text
My first feeling was to make the 3rd way, but to be convinced, just after I've posted my message, I did some tests with just a global view and over 200 geometrical forms (Rect, Rounded Rect and Ellipse) on it and I move only a half with touchMoved event. I did this test with the way 1 (Subclassing UIView) and the way 3 (Subclassing NSObject), the way 2 seems to me too restrictive and not help me at all.
The resuslt is that the way 1 seems to be more efficient... There is no lag when I move 60 objects together ! Moreover using this way would probably help me because using view comes which some interesting functions like touch detection on complex path (see UIBezierPath), object hierarchy handled by the UIView classe...
So I will use that way and come back here to share my results ;-)
Regards
It's better to use CGLayer objects. The benefits are:
It's much faster and more memory efficient. For simple objects, adding a view is much more expensive and complicates the view hierarchy. For complicated objects, the caching done on CGLayers can boost performance.
It's easy to group objects together. you just put everything in a new layer, and voila! There's almost no overhead.
Using CGLayer and other Quartz objects gives you a lot more flexibility.
The only drawback is that you have to directly use Quartz 2D. It's not really difficult, but needs some learning if you haven't used it before.
CAShapeLayer pretty much handles your option 2. It does rect and rounded rect (see cornerRadius) by default, or you can give it a path for any arbitrary shape. For your option 1, you can use a CAShapeLayer with a UIView instead of implementing drawRect and it may be faster.

How to animate a polygon mask with performance?

I'm having a performance issue.
I've created an UIView and overwrited it's drawRect function. At this function, I was drawing an image (big one), and over that, an white square at the entire screen with a polygon inside it, with CGContextEOFillPath. The result is an white screen with portion of the image (defined by the polygon) displayed.
After that, I created a function to animate the transition of that polygon to another one. Besides the polygon animation, the image should also be scaled and moved to fix the diplayed at the screen. I did that with an NSTimer. The animation of the polygon consists in calculating the distance between each vertex and moving then to a position according to elapsedTime. It worked just fine at the simulator, but got really stucked at device.
Reading about performance, here at stackoverflow, I found the alternative to use beginAnimations and commitAnimations. I'm changing everything to use that approach with the image. But what can I do with the polygon. The polygon is drawn with CGContextMoveToPoint and CGContextAddLineToPoint, so I believe it can't be animated with beginAnimations. An I correct? Is there a better approach to do that?
The desired result is something like this comic reader app: http://www.comixology.com/iphoneapp (click on guided tour. at the middle of the video they show the "automatic masking" feature)
My suggestion would be to use a CAShapeLayer overlaid on your main image view, with the CAShapeLayer being the size of the view you want to mask and having a polygon path for a hole in the center of it. CAShapeLayers let you animate from one CGPathRef to another smoothly, as long as the two paths have the same number of control points. You will need to use a CABasicAnimation here to do that animating, rather than a UIView begin / commitAnimations block, but it's not too difficult.
Joe Ricioppo has a nice example of animating CAShapeLayer paths in his post here.
With Core Animation you can animate "animatable" (sic) properties. Apple's documentation enumerates animatable properties in Mac OS X:
http://url.akosma.com/55
In the case of the iPhone, the UIView documentation explicitly says "animatable" when a given property is, hum, animatable. The most powerful of these are (IMHO) UIView's "transform" property, which takes CGAffineTransform structs as inputs, or CALayer's "transform" property (which takes CATransform3D structs). Both are animatable and give you tremendous power to create any kind of transition you want.
Now, in your case, indeed, you can't animate the polygon in an "easy" way. My bet would be in your case to try to map CGAffineTransforms that fit your needs (scale, translation) and apply that to a fixed view, non-animated, created using your Quartz code.
I hope I'm clear enough :)