Control iApp behaviors and properties with an XCode animation object
__________________
iApp developers have complete control over the properties and behaviors of both built-in XCode library objects and objects based on custom and derived classes. With CABasicAnimation objects, iApp developers can easily make an iApp come alive through animation of any CALayer property - color, position, opacity, rotation/orientation, etc. However, a direct combination of these tools - a CABasicAnimation of a CALayer that directly controls other class properties within that iApp - becomes more of a challenge. Fortunately, XCode can handle this. I built BSD, an iApp available for sale at iTunes that relies on this idea and to highlight the solution, I built a simple iApp called DV (for Dependent Variables), available as a GitHub repository. Using XCode 5.1 and targeting iOS 7.1, DV has a ViewController class with most of the machinery of the iApp. DV also has the AppDelegate class, and customView, a simple custom class. I did not use the Interface Builder to build DV; the ViewController viewDidLoad method dynamically builds all view controller objects DV needs.
iApp developers have complete control over the properties and behaviors of both built-in XCode library objects and objects based on custom and derived classes. With CABasicAnimation objects, iApp developers can easily make an iApp come alive through animation of any CALayer property - color, position, opacity, rotation/orientation, etc. However, a direct combination of these tools - a CABasicAnimation of a CALayer that directly controls other class properties within that iApp - becomes more of a challenge. Fortunately, XCode can handle this. I built BSD, an iApp available for sale at iTunes that relies on this idea and to highlight the solution, I built a simple iApp called DV (for Dependent Variables), available as a GitHub repository. Using XCode 5.1 and targeting iOS 7.1, DV has a ViewController class with most of the machinery of the iApp. DV also has the AppDelegate class, and customView, a simple custom class. I did not use the Interface Builder to build DV; the ViewController viewDidLoad method dynamically builds all view controller objects DV needs.
DV has a simple user experience. The first X Position label shows the states of the top two squares; the second X position label reflects the lower square. The Rotation Angle applies to all the squares.
Initialized DV animations |
The X Position label and Rotation Angle labels continuously update to show the latest instantaneous values. The PAUSE / RESUME button toggles, and when the animations finish, the RESET button enables.
DV animations completed |
The button interlocks make intuitive sense to the user.
In Objective C, a class declares its instance variables in the header file @interface section. All class methods can see and change instance variables inside that class. Additionally, properties of methods within that class can take the values of instance variables inside that class. Therefore, if a CABasicAnimation object can change an instance variable of an object, those changes could drive object behavior in other methods through direct property changes of those objects. A CABasicAnimation object cannot change these instance variable values directly, but through a CADisplayLink object - a native Objective C class - a CABasicAnimation object can update these instance variables indirectly. CADisplayLink works as a timer, synchronized to the screen refresh rate. Each time a CADisplayLink object fires, it calls a selector, or specified method. The selector can then update class instance variables. These instance variables then drive changes in objects with properties controlled by those variables. In this way, the animation indirectly controls those objects. Sample iApp DV relies on this engineering. In the XCode simulator, DV is optimized for iPhone Retina (3.5-inch). The main class, ViewController, has a CADisplayLink object which drives changes in objects both in ViewController and in a derived class instance. To show the flexibility of this approach, the changes in the derived class object happen through a direct call to a class method, and through direct updates to a derived class property.
Method viewDidLoad – the last method of class file ViewController.m - builds and configures the fourteen view controller objects of DV, and places each controller object in its own layer. All controls are instances of Xcode base classes, except for thirdView. Method viewDidLoad initializes three flags
at lines 533 to 535 that control the toggling, appearance, and enable/disable status of the control "buttons" at the bottom of the simulator screen; we won’t explore the code behind this button behavior because this article does not focus on that engineering. DV launches with the START enabled, and the squares initialized.
The thirdView object created in the viewDidLoad method of ViewController is an instance of the DV customView class. This class derives from the UIView base class. In customView.m, the drawRect method draws and fills a rectangle based on a CGRect parameter. In the customView class, method cGAffineTransforms uses CGAffineTransforms at lines 31 and 32 to translate and rotate the thirdView object, calling the transforms with CGAffineTransformConcat at line 33 to make them combine correctly.
To make the transforms occur instantaneously, the animation block sets both animateWithDuration and delay to 0.
(Code 1) DV application control flags |
The thirdView object created in the viewDidLoad method of ViewController is an instance of the DV customView class. This class derives from the UIView base class. In customView.m, the drawRect method draws and fills a rectangle based on a CGRect parameter. In the customView class, method cGAffineTransforms uses CGAffineTransforms at lines 31 and 32 to translate and rotate the thirdView object, calling the transforms with CGAffineTransformConcat at line 33 to make them combine correctly.
(Code 2) customView class transforms |
To make the transforms occur instantaneously, the animation block sets both animateWithDuration and delay to 0.
The animation objects in ViewController.m method prmrySquareLayerMainAnim only animate the upper square.
When the user taps START, the ViewController.m touchesBegan method launches; lines 216 and 217 in this method configure displayLink, the CADisplayLink variable.
A CADisplayLink variable is a timer, firing at the screen’s refresh rate. Each time it fires, it calls a selector callback - in this case, method squareLayerStateChange at line 216. Line 221 calls method prmrySquareLayerMainAnim, to start the primary square animations. Each time displayLink calls selector method squareLayerStateChange, this callback updates ViewController instance variables
uses the instance variable values
To show that a derived class method can use the xPosVal property as a transformed value, the customView class cGAffineTransforms method uses
–(self.xPosVal)
as a negative value in a translate transform (see Code 2 above) to move the object to the left.
(Code 3) When DV calls this method, the objects in this method only animate the upper square. |
(Code 4) Configure the CADisplayLink object in touchesBegan at lines 216 - 217 |
- curPos
- curXVal
- curRadAngle
(Code 5) Update the labels; change second and third square properties |
- curPos
- curXVal
- curRadAngle
To show that a derived class method can use the xPosVal property as a transformed value, the customView class cGAffineTransforms method uses
–(self.xPosVal)
as a negative value in a translate transform (see Code 2 above) to move the object to the left.
Although the examples in DV focused on simple animations, the CADisplayLink technique has huge potential. With this approach, a CABasicAnimation can drive changes to any object property that an XCode statement can see – layer color, sound generator frequency, or UILabel font size, for example. It could even control object creation based on animation object values. It has endless possibilities.