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 four methods touchesBegan(_:with:), touchesMoved(_:with:), touchesEnded(_:with:), and touchesCancelled(_:with:), from class View. Replace them with the following method touchesBegan(_:with:). Then click on the big white View to attract the little yellow LittleView to the point you touched. See Animating Property Changes in a View.

Animation in iOS 11.0.1 with Swift 4.0

	override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
		let touch: UITouch = touches.first!;
		let point: CGPoint = touch.location(in: self);
        
		UIView.animate(withDuration: 1.0,
			delay: 0.0,
			options:  UIViewAnimationOptions.curveEaseInOut,
			animations: {
				//This block describes what the animation should do.
				self.littleView.center = point;    //Move the LittleView to a new location.
			},
			completion: nil
		);
	}

Animation in iOS 9 with Swift 2.2

A closure is a block of statements surrounded by {curly braces}. It’s like an anonymous function. We saw a closure in the dispatch_after and temperature examples in Hello.

The last two arguments of animateWithDuration(_:delay:options:animations:completion:) can be blocks. Inside of a block, we have to say self.littleView instead of plain old littleView. Write nil if no block needs to be executed.

	override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
		let touch: UITouch = touches.first!;
		let point: CGPoint = touch.locationInView(self);

		UIView.animateWithDuration(1.0,
			delay: 0.0,
			options:  UIViewAnimationOptions.CurveEaseInOut,
			animations: {
				//This block describes what the animation should do.
				self.littleView.center = point;	//Move the LittleView to a new location.
			},
			completion: nil
		);
	}

Animation in iOS 8 with Swift 1.2

	override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
		let touch: UITouch = touches.first as! UITouch;
		let point: CGPoint = touch.locationInView(self);

		UIView.animateWithDuration(1.0,
			delay: 0.0,
			options:  UIViewAnimationOptions.CurveEaseInOut,
			animations: {
				//This block describes what the animation should do.
				self.littleView.center = point;	//Move the LittleView to a new location.
			},
			completion: nil
		);
	}

	override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
	}

	override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
	}

	override func touchesCancelled(touches: Set<NSObject>, withEvent event: UIEvent) {
	}

Animation in iOS 8 with Swift 1.1

	override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
		let touch: UITouch = touches.anyObject() as UITouch;
		let point: CGPoint = touch.locationInView(self);

		UIView.animateWithDuration(1.0,
			delay: 0.0,
			options:  UIViewAnimationOptions.CurveEaseInOut,
			animations: {
				//This block describes what the animation should do.
				self.littleView.center = point;	//Move the LittleView to a new location.
			},
			completion: nil
		);
	}

	override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
	}

	override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
	}

	override func touchesCancelled(touches: NSSet, withEvent event: UIEvent) {
	}

Animation in iOS 4, 5, 6, and 7 with Objective-C

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 with Objective-C

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 9 closure before the assignment to center.
    				UIView.setAnimationRepeatCount(2.0);
    
    Then go back to 1. Also see UIViewAnimationOptions.Repeat.

  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. If you touch while an animation is in progress in iOS 8 or 9, the new animation will begin at the current location of the old animation.

  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.
    				self.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 true in the init method of View, and flip its value in the touchesBegan(_:with:) method of class View. Then let the animation change the LittleView’s alpha level to 1 if the instance variable is true, and to 0 if the instance variable is false.


  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.
    				self.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, 7, 8 and 9. One way to make it animate gradually in 5, 6, 7, 8, and 9 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, 7, 8, and 9 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 init method of LittleView,

    		//backgroundColor = UIColor.yellowColor();
    		backgroundColor = UIColor.clearColor();
    
    		let colorspace: CGColorSpaceRef = CGColorSpaceCreateDeviceRGB()!;
    		let components: [CGFloat] = [1.0, 1.0, 0.0, 1.0];	//rgba
    		let layer: CALayer = self.layer;
    		layer.backgroundColor = CGColorCreate(colorspace, components);
    

    In the touchesBegan(_:with:) method of class View,

    		let touch: UITouch = touches.first!;
    		let point: CGPoint = touch.locationInView(self);
    
    		let colorspace: CGColorSpaceRef = CGColorSpaceCreateDeviceRGB()!;
    		let components: [CGFloat] = [0.0, 1.0, 0.0, 1.0];	//rgba
    
    		UIView.animateWithDuration(1.0,
    			delay: 0.0,
    			options: UIViewAnimationOptions.CurveEaseInOut,
    			animations: {
    				//This block describes what the animation should do.
    				self.littleView.center = point;	//Move the littleView to a new location.
    				self.littleView.layer.backgroundColor = CGColorCreate(colorspace, components);
    			},
    			completion: nil
    		);
    

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

  8. Animate a change to the LittleView’s orientation. Remove the code that animates the scale, and replace it by the following. GLKMathDegreesToRadians converts degrees to radians; its return value must then be converted from Float to CGFloat. We saw transformation objects in Japan.
    			//90 degrees clockwise
    			self.littleView.transform = CGAffineTransformMakeRotation(CGFloat(GLKMathDegreesToRadians(90)));
    
    import GLKit;	//needed for GLKMathDegreesToRadians
    
    Then click on the white area and watch the LittleView tumble 90° clockwise. Can you rotate counterclockwise?

  9. Animate the scale and orientation together.
    			let s: CGAffineTransform = CGAffineTransformMakeScale(1, 2);
    			let r: CGAffineTransform = CGAffineTransformMakeRotation(CGFloat(GLKMathDegreesToRadians(90)));
    			let c: CGAffineTransform = CGAffineTransformConcat(s, r);	//What happens if you say (r, s)?
    			self.littleView.transform = c;
    
    import GLKit;	//needed for GLKMathDegreesToRadians
    
    Of course, we can telescope the above statements to
    			let s: CGAffineTransform = CGAffineTransformMakeScale(1, 2);
    			let r: CGAffineTransform = CGAffineTransformMakeRotation(CGFloat(GLKMathDegreesToRadians(90)));
    			self.littleView.transform = CGAffineTransformConcat(s, r);
    
    or even to
    			self.littleView.transform = CGAffineTransformConcat(
    				CGAffineTransformMakeScale(1, 2),
    				CGAffineTransformMakeRotation(CGFloat(GLKMathDegreesToRadians(90)))
    			);
    

  10. The completion: parameter does not have to be nil. For example, we can launch a second animation when the first animation has completed.
    		//Move the little view to the point of the touch,
    		//and then move it back to where it originally was.
    		let center: CGPoint = littleView.center;
    
    		UIView.animateWithDuration(1.0,
    			delay: 0.0,
    			options: UIViewAnimationOptions.CurveEaseInOut,
    			animations: {
    				//This block describes what the animation should do.
    				self.littleView.center = point;	//Move the littleView to a new location.
    			},
    			completion: {(b: Bool) -> Void in
    				UIView.animateWithDuration(1.0,
    					delay: 0,
    					options: UIViewAnimationOptions.CurveEaseInOut,
    					animations: {
    						//This block describes what the animation should do.
    						self.littleView.center = center;
    					},
    					completion: nil
    				);
    			}
    		);
    

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