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:)
,touchesCancelled(_:with:)
,View
.
Replace them with the following method
touchesBegan(_:with:)
.View
to attract the little yellow
LittleView
to the point you touched.
See
Animating
Property Changes in a View.
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 ); }
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 ); }
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) { }
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) { }
A
block
is a group of statements surrounded by
^{
}
.
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 ]; }
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:
.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]; }
LittleView
’s
yellowColor
to
clearColor
.
center
.
UIView.setAnimationRepeatCount(2.0);Then go back to 1. Also see
UIViewAnimationOptions.Repeat
.
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 opaqueThen 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
.
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 );
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.
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 GLKMathDegreesToRadiansThen click on the white area and watch the
LittleView
tumble 90° clockwise.
Can you rotate counterclockwise?
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 GLKMathDegreesToRadiansOf 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))) );
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 ); } );
center
,
alpha
,
backgroundColor
,
and
transform
properties of a
UIView
.
See the
short
list
and
long
list
of animatable properties.