NOTE that this code has been updated to support iOS6 and the 4 inch retina screen
The What And Why Of It All
This week I’m giving back some code that I developed as a learning exercise for moving an object along a bezier path under the iOS environment. This code also has its roots in the answers to some questions I posted on StackOverflow.com, hence part of my desire to give back a fully working solution. These questions being:
The PathMove program allows the user to set up a predetermined set of bezier paths and then trigger a UIImageView object to move from start to end along that path. The program incorporates several options to do things like:
- Select the ending quadrant of the bezier path.
- Mix and match three different predefined bezier path segments for the starting and ending segments of the overall path.
- Allow the object to grow, shrink or remaiin the same size as it moves along the path.
- Rotate the object to match the tangent of the bezier curve as the object moves.
- Pre-Rotate the object by 90 degrees to accommodate how iOS calculates a tangent.
- Annotate the complete bezier path with the size and location of all of the path’s the control points
You can download the source code for PathMove from GitHub at https://github.com/JoalahDesigns/PathMove
I have released this code under a BSD-3 license, so you are free to use however you like as long as you acknowledge where it originated from. And if you do use any part of it please drop me a line to let me know!
The program consists of two screens – a main screen where the path is drawn and the object moves, and a setup screen which contains all the options that the user can make. The following screen shots show the object moving along a path and the corresponding setup screen that created the path. The final screen shot shows how the bezier path can be annotated in order to show all the control points.
The PathMove program is built up from the following classes:
- JdViewController Forms the display for the main screen and animates the object to move along the bezier path.
- JdGraphicView Draws the graph axis, bezier path and assorted boxes.
- JdSetupViewController Forms the display for the setup screen.
- JdConfiguration Passes configuration information between the two screens.
- JdBezierPoint Contains the definitions of a single bezier point in the path.
- JdBezierPath Maintains a list of JdBezierPoint definitions and constructs a smooth bezier path from options passed into it.
There are several interesting points about PathMove.
Rather than directly construct a UIBezierPath object, PathMove constructs an array of JdBezierPoint objects, each of which describes a location along the desired bezier path, including the main and control points. This allows you to do nifty things like drawing the locations of the control points on the display, rather than just the actual bezier path. Before I did this I had a hell of a time trying to visualize the bezier path and how changes to the control points affected it. Once I could see the control points on the screen it became so much simpler to say “yep .. just move that control point by X degree and extend it by a little bit more. Short of having a drawing package that allows dragging and dropping control points, this is one of my favorite features of PathMove.
The animations are built up from a combination of CAAffineTransforms and CAKeyframeAnimation animation. Figuring out this combination of transforms and animations gave me the biggest headache and I only really nailed it after I got answers to my StackOverflow questions.
Within the animations, the biggest issue I ran into was the “Rotate the object with the path” option. In the code this is enabled by setting the rotationMode property on the CAKeyframeAnimation object. In Apples documentation for this property CAKeyframeAnimation Class Reference: rotationMode they helpfully say:
Determines whether objects animating along the path rotate to match the path tangent.
You’d think that setting this would mean that the object in question keep its orientation relative to the tangent of the path. Well it does .. sort of. What Apple should have said is:
Determines whether objects animating along the path rotate their horizontal axis to match the path tangent, and that once animation starts the object will be automatically rotated so that its horizontal axis is tangential to the path.
Thus if you want an axis other than the objects horizontal axis to be tangential to the path, you have to pre-rotate the object by the angle between the desired and horizontal axis. This is the reasoning behind the “Pre-Rotate object” option in the Setup screen.
Th’ Th’ That’s all folks
Hope you enjoy this program.