Hi Guys! Welcome back to the last part of this tutorial.
In the previous post I showed you how to draw an image using Core Graphics, in this post we’ll add some behavior to that image.
Handling touch
We can start by updating the circle position depending on the touch location.
Here I’ll briefly show you how to use the UIPanGestureRecognizer to get all the needed data.
[code lang=”obj-c” title=”ViewController.m – viewDidLoad”]
//Initialize Gesture recognizer —————-
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(screenPan:)];
[self.view addGestureRecognizer:panGestureRecognizer];
Essentially we initialize a Pan Gesture Recognizer and we attach it to the ViewController view at the end of the viewDidLoad function. We initialize it using the target-action pattern, using the ViewController itself as target and a function called screenPan as action. The method ScreenPan will be executed as soon as a Pan Gesture is recognized on the ViewController view.
Let’s see how the method screenPan is implemented:
//Manage the Pan Gesture
- (void)screenPan:(UIGestureRecognizer *)gesture{
//Get the location of the touch
CGPoint point = [gesture locationInView:self.view];
//Move the center of the gradient view if gesture state is equal to Began or Changed
if (gesture.state == UIGestureRecognizerStateBegan || gesture.state == UIGestureRecognizerStateChanged) {
self.gradientView.center = point;
}
}
This method is based on 2 steps. First we obtain the location of the touch thanks to the method locationInView: and then, if the state of the gesture is equal to “began” or “changed”, we assign it to the property center of gradientView.
As you can see this code works only while dragging but not at the first touch.
To make it work we need to implement some touch event handling methods, like touchesBegan and touchesEnded which will be fired automatically in response to user actions (you can find more information about these methods in the documentation).
The touchesBegan method does something really similar to what the screenPan method does. It just changes the current gradientView center to the touch location.
//Manage Touches Begin
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint drawPoint = [touch locationInView:self.view];
//Change gradientView center and launch fadeIn animation
self.gradientView.center = drawPoint;
}
Thanks to this function when the user touches the screen, the gradientView moves immediately to the new position.
Adding animations
To make this experiment more interesting we can animate the opacity of the red circle just to make it appear slowly when the user moves it and to make it fade out when the touch action ends.
The animations are generated through Core Animation using the class CABasicAnimation. This is probably the simplest way to animate a property, in fact it’s just going to take some simple steps. Let’s see the code needed to create the fadeIn animation:
[code lang=”obj-c” title=”GradientCircle.m”]
//Create the Fadein animation and assign it to the layer
-(void)fadeIn{
//Define the property through a Key
CABasicAnimation *fadein = [CABasicAnimation animationWithKeyPath:@"opacity"];
//Initial and the final values
fadein.fromValue = [NSNumber numberWithInt:0];
fadein.toValue = [NSNumber numberWithInt:1];
//Duration the the animation
fadein.duration = 0.3;
//Attach the animation to the layer for a custom key… let’s call it "fade"
[self.layer addAnimation:fadein forKey:@"fade"];
}
Firstly we define the CABasicAnimation object specifying the key path of the property to be animated, opacity.
The next step is to define how the animation takes place. So with the help of the properties fromValue, toValue and duration we say that in 0.3 seconds the opacity must change from 0 to 1. The animation between the initial and the final value is automatically created for us thanks to an interpolation calculated over the duration. So we don’t need to take care of that because it just works great producing a really smooth animation :).
We have now defined the animation and we can attach it to the layer of the current GradientCircle object using the function addAnimation:forKey:. This function adds the animation to the render tree specifying a unique key to refer to this animation (this is not required). When this function is called, the animation starts.
The fadeOut function is really similar to the fadeIn function. We just invert the fromValue and the toValue to make the object disappears.
[code lang=”obj-c” title=”GradientCircle.m”]
//Create the Fadeout animation, assign it to the layer
-(void)fadeOut{
CABasicAnimation *fadeout = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeout.delegate = self;
fadeout.fromValue = [NSNumber numberWithInt:1.0];
fadeout.toValue = [NSNumber numberWithInt:0];
fadeout.duration = 0.2;
[self.layer addAnimation:fadeout forKey:@"fade"];
}
Remember to add the function prototypes to the GradientCircle.h file.
We can now call the fadeOut function in relation to the touch actions that we have previously managed.
When the user touches the screen we make the object appear, while when the user stops touching the object has to disappear.
This is extremely simple, just call the animation functions (fadeIn/fadeOut) in the right places.
Add the fadeIn function at the end of the function touchesBegan:
[self.gradientView fadeIn];
And add the touchesEnded function to manage the end of the touch operations (this function is automatically called when a touch operation ends):
//Manage Touches End
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[self.gradientView fadeOut];
}
Last, we want to call the function fadeOut when the Pan Gesture ends. So add this code at the end of the screenPan function:
//Launch fadeOut animation if gesture state is equal to end
if (gesture.state == UIGestureRecognizerStateEnded) {
[self.gradientView fadeOut];
}
We are almost done…
If we execute the code now, though, we’ll encounter a problem. When the fadeOut animation ends the GradientCircle object appears again. To prevent it from happening we have to add 2 rows of code to the fadeOut function.
fadeout.fillMode = kCAFillModeForwards;
fadeout.removedOnCompletion = NO;
Setting fillMode to the kCAFillModeForwards constant we tell the animation to stick its value to the final value.
The removedOnCompletion tells the animation not to remove itself from the layer tree.
Compile and execute to see the final result!
Compile and execute to see the final result!
We’re done for this tutorial. If you haven’t done it yet, feel free to download the source code and play around with it.
Yari D'areglia
https://www.thinkandbuild.itSenior iOS developer @ Neato Robotics by day, game developer and wannabe artist @ Black Robot Games by night.