Animate the response to the touch.

Let’s make the yellow LittleView in the previous project travel under its own power, without being dragged.

Remove the three methods touchesBegan:withEvent:, touchesMoved:withEvent:, and touchesEnded:withEvent: from class View. Replace them with the following touchesBegan:withEvent. Then click on the big white View to attract the little yellow LittleView.

Animation in iOS 4, 5, 6, and 7

A block is a group of statements surrounded by ^{ and }. It’s like an anonymous function. The last two arguments of animateWithDuration:delay:options:animations:completion: can be blocks. Write NULL if there’s no block that needs to be executed.

//Animation in iOS 4, 5, 6, and 7

- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event {

	[UIView animateWithDuration: 1.0
		delay: 0.0
		options: UIViewAnimationOptionCurveEaseInOut
		animations: ^{
			//This block describes what the animation should do.
			littleView.center = [[touches anyObject] locationInView: self];
		}
		completion: NULL
	];
}

Animation in iOS 3

The group of statements between the calls to beginAnimations:context: and commitAnimations is called the animation block. Please indent the animation block to make it more conspicuous, and to ensure that the calls to beginAnimations:context: and commitAnimations are paired up correctly.

Look at the four possible arguments for setAnimationCurve:. The one we chose, UIViewAnimationCurveEaseInOut, starts and ends slowly but is fast in the middle.

See an example here.

//Animation in iOS 3.

- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event {

	[UIView beginAnimations: nil context: NULL];

		//Describe the animation itself.
		[UIView setAnimationDuration: 1.0];	//in seconds; default is 0.2
		[UIView setAnimationCurve: UIViewAnimationCurveEaseInOut];
		[UIView setAnimationRepeatCount: 1.0];	//The default is 1.0.

		//Describe what the animation should do.
		littleView.center = [[touches anyObject] locationInView: self];

	[UIView commitAnimations];
}

Things to try

  1. Change the LittleView’s yellowColor to clearColor.

  2. Try an animation duration of 0.2 seconds. Then go back to one second.

  3. Try an animation repeat count of 2. Insert the following statement into the iOS 7 block before the assignment to center.
    				[UIView setAnimationRepeatCount: 2];
    
    Then go back to 1.

  4. If you touch while an animation is in progress in iOS 4, the touch will be ignored. If you touch while an animation is in progress in iOS 5, 6, or 7, a new animation will begin immediately, starting at the ending location of the old animation. To make the new animation begin at the current location of the old animation, change the options from
    	UIViewAnimationOptionCurveEaseInOut
    
    to the following. Use the “bitwise or” operator | to combine the options together.
    	UIViewAnimationOptionCurveEaseInOut
    		| UIViewAnimationOptionAllowUserInteraction
    		| UIViewAnimationOptionBeginFromCurrentState
    

  5. We animated a change to the LittleView’s position. Now animate a change to the LittleView’s opacity. Insert the following statement after we assign a value to littleView.center. This demonstrates that we can animate the position and the opactity simultaneously.
    		littleView.alpha = 0.0;	//0.0 is transparent, 1.0 is opaque
    
    Then click on the white area and watch the LittleView fade out. You can now give the cloaking device to your Romulan warship.

    Give class View an instance variable of type BOOL. Initalize it to YES in the initWithFrame: method of View, and flip its value in the touchesBegan:withEvent: method of class View. Then let the animation change the LittleView’s alpha level to 1 if the instance variable is YES, and to 0 if the instance variable is NO.


  6. Animate a change to the LittleView’s backgroundColor. Remove the code that animates the opacity change. I wish we could replace it by the following.
    		littleView.backgroundColor = [UIColor greenColor];
    
    Then click on the white area. The LittleView animates gradually to green in iOS 4, but turns green instantaneously in iOS 5, 6, and 7. One way to make it animate gradually in 5, 6, and 7 is to remove the drawRect: method from class LittleView. But no one wants to do that.

    A better way to make it animate in iOS 5, 6, and 7 is to keep the drawRect: method in LittleView and add the following chunks of code to the project. A UIView is built on top of a Core Animation CALayer. We’ll make the LittleView transparent so we can see the layer underneath it. In the initWithFrame: method of LittleView,

    		//self.backgroundColor = [UIColor yellowColor];
    		self.backgroundColor = [UIColor clearColor];
    
    		CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    		const CGFloat components[] = {1.0, 1.0, 0.0, 1.0};	//rgba
    		CALayer *layer = self.layer;
    		layer.backgroundColor = CGColorCreate(colorspace, components);
    

    In the touchesBegan:withEvent: method of class View,

    	[UIView animateWithDuration: 1.0
    		delay: 0.0
    		options: UIViewAnimationOptionCurveEaseInOut
    		animations: ^{
    			//This block describes what the animation should do.
    			littleView.center = [[touches anyObject] locationInView: self];
    
    			CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    			const CGFloat components[] = {0.0, 1.0, 0.0, 1.0};
    			littleView.layer.backgroundColor =
    				CGColorCreate(colorspace, components);
    		}
    		completion: NULL
    	];
    


  7. The completion: argument does not have to be NULL. You can launch a second animation when the first animation has completed.
    		[UIView animateWithDuration: 1.0
    			delay: 0.0
    			options: UIViewAnimationOptionCurveEaseInOut
    			animations: ^{
    				//This block describes what the animation should do.
    				littleView.center = [[touches anyObject] locationInView: self];
    			}
    			completion: ^(BOOL b) {
    				[UIView animateWithDuration: 1.0
    					delay: 0.0
    					options: UIViewAnimationOptionCurveEaseInOut
    					animations: ^{
    						//This block describes what the animation should do.
    						littleView.center = //another CGPoint;
    					}
    					completion: NULL
    				];
    			}
    		];
    

  8. Animate a change to the LittleView’s scale (size). Insert the following into the block.
    			littleView.transform = CGAffineTransformMakeScale(1, 2);
    
    Then click on the white area and watch the height of the LittleView double.

  9. Animate a change to the LittleView’s orientation. Remove the code that animates the scale, and replace it by the following. Multiplication by π/180 converts degrees to radians.
    			littleView.transform = CGAffineTransformMakeRotation(90 * M_PI / 180);
    
    Then click on the white area and watch the LittleView tumble 90° clockwise. Can you rotate counterclockwise?

  10. Animate the scale and orientation together. A CGAffineTransform is a structure, not an object, so it can be stored in a variable.
    			CGAffineTransform s = CGAffineTransformMakeScale(1, 2);
    			CGAffineTransform r = CGAffineTransformMakeRotation(90 * M_PI / 180);
    			CGAffineTransform c = CGAffineTransformConcat(r, s);
    			littleView.transform = c;
    
    Of course, you can telescope the above lines to
    			CGAffineTransform s = CGAffineTransformMakeScale(1, 2);
    			CGAffineTransform r = CGAffineTransformMakeRotation(90 * M_PI / 180);
    			littleView.transform = CGAffineTransformConcat(r, s);
    
    or even to
    			littleView.transform = CGAffineTransformConcat(
    				CGAffineTransformMakeRotation(90 * M_PI / 180),
    				CGAffineTransformMakeScale(1, 2)
    			);
    

  11. We have animated the center, alpha, backgroundColor, and transform properties of a UIView. See the short list and long list of animatable properties.