Working With Gesture Recognizers in iOS Apps Using Swift

Published Aug 30, 2017Last updated Sep 13, 2017
Working With Gesture Recognizers in iOS Apps Using Swift

If you've ever used an iOS device, then, whether you know it or not, you've used gesture recognition on many occasions. From double-tapping or pinching to zoom in on a picture, to swiping left or right to change scenes on Snapchat and Instagram, all of these actions are done by gesture recognizers. In this tutorial, we'll be exploring a few gesture recongnizers in iOS to give you a feel of what they are and how to use them.

To get started, let's make a new Single View Application Xcode project.
Screen Shot 2017-08-26 at 1.28.45 PM.png

I will name the app 'Gesturify' ( because I can 😄). You can name it whatever you want. You can put anything in the Organization Name and Organization Identifier fields for the sake of this tutorial.

Make sure the Language is set to Swift.
Screen Shot 2017-08-26 at 1.38.40 PM.png

Click 'Next,' and go ahead and save the application anywhere. I'll save mine on my Desktop.
Screen Shot 2017-08-26 at 1.46.15 PM.png

Now we're ready to go!

Click on Main.storyboard. This is where we'll lay our very simple user interface out. On the bottom right, click on the 'Object Library' icon. We need an Image View.
Screen Shot 2017-08-26 at 1.55.21 PM.png

Click and drag the Image View onto the view controller. Place it in the middle of the view controller and drag its edges until they touch the edges of the view controller.

Screen Shot 2017-08-26 at 2.03.02 PM.png

Now, we want to add an actual image to our Image View. To do that, we need to add an image to our Assets catalog. You can use any image you want. I'm going to grab one from unsplash.com. Feel free to do the same.
Screen Shot 2017-08-26 at 2.28.01 PM.png

Next, go back to Main.storyboard and click on the Image View. Click on the Attributes Inspector, then click on the dropdown icon on the Image field and select the name of the photo you added.

Your picture should automatically appear in the Image View. To enable the Image View to respond to touches and gestures, enable Interaction.

(Human interactions are disabled by default for viewers like Image View that do not normally accept touches.)
Screen Shot 2017-08-26 at 8.40.50 PM.png

Next, let's add some gesture recognizers to our app. Go to the Object Library and search for gesture. You'll see there are quite a number of gesture recognizers. We'll only be working with four of them in this tutorial, though 😦.

So, go ahead, drag Tap Gesture Recognizer and drop it on the Image View. It should appear just under 'Exit' in the Document Outline. (Check the picture after the next one for how to hide/show Document Outline.) Do the same for Swipe Gesture Recognizer, Rotate Gesture Recognizer, and Pinch Gesture Recognizer.

Screen Shot 2017-08-26 at 3.46.40 PM.png

If you build and run your app using CMD + R, you'll have an app with a picture sitting in its middle. Pretty boring if you ask me. Type CMD + .(dot) to stop the app from running. Let's add some life to it.

Click on the Assistant Editor icon to show the ViewController.swift file. You can hide the Document Outline to give more room.

Screen Shot 2017-08-26 at 3.11.39 PM.png

It's time to connect our Image View and gesture recognizers to the code. Do this by holding the control key and clicking and dragging from the Image View to the code right above, viewDidLoad(). Name it image and click connect.
Screen Shot 2017-08-26 at 3.21.50 PM.png

Tap Gesture Recognizer

For the gesture recognizers, things are a little different. First, we're going to connect the Tap Gesture Recognizer to our code.

From the Document Outline, hold control, click, and drag from the Tap Gesture Recognizer to the code, just under or over the IBOutlet for the Image View. Name it tapGesture. Click connect.

You should have something like this:
Screen Shot 2017-08-26 at 11.01.09 PM.png

By doing this, you've created two variables that are directly linked to UI elements. For our gesture, this is not enough. We need behavior.

To do this, control + drag again from the Tap Gesture Recognizer (whose name has changed after connecting it to the code). This time, don't click 'connect' yet. We want to make an IBAction, not an outlet. Change 'Connection' to Action. Click 'connect' and you should have a new IBAction function in ViewController.swift.
Screen Shot 2017-08-26 at 11.38.45 PM.png

What we want to do, going forward, is increase the picture size 1.5 times by double tapping and back to its normal size by double tapping again.

First, we want to make sure it takes two taps for our Tap Gesture Recognizer to respond (the default is one). To do this, go to ViewDidLoad and input this line of code:
tapGesture.numberOfTapsRequired = 2

Next, we will create two functions to handle the zooming in and out of the picture on double tap. Go ahead and copy the following two functions and paste them under viewDidLoad. You can delete the didReceiveMemoryWarning function — we won't be needing it.

func scaleImageUp() {
    image.frame = CGRect(x: 0,
                         y: view.bounds.size.height / 2 - (image.frame.size.height * 1.5) / 2,
                         width: image.frame.size.width * 1.5,
                         height: image.frame.size.height * 1.5)
}
func normalizeImage() {
    let imageHeight = image.frame.size.height / 1.5
    let imageWidth = image.frame.size.width / 1.5

    image.frame = CGRect(x: 0,
                         y: view.bounds.size.height / 2 - imageHeight / 2,
                         width: imageWidth,
                         height: imageHeight)
}

CGRect, according to Apple's documentation is

A structure that contains the location and dimensions of a rectangle.

Image Views have the frame property which is, well, the frame of the image. We can use CGRect to alter the size of our image by altering its x and y positons and its width and height. Altering the width, height, and x properties is pretty straightforward. The only thing that may look complex is the way the y property is set.

In normalizeImage, we want to make sure the image sits right at the middle of the view. We take the height of the whole view in view.bounds.size.height (say it is 100) and divide it into two (that gives us 50).

If the image's height is 50 and we have it start from 50, that means it will only occupy the bottom half of the view. We don't want that. What we want is for it to sit at the middle, so it leaves a space of 25 above and under it. We do this by substracting half of the image's height from half of the view's height (50 - 25). Now, our image starts at 25.

A similar thing is done in scaleImageUp. The only difference is that we have to take into consideration the fact that our image will now be 1.5 times its height.

Since we'll be scaling up or down by double tapping, we need a way to know when to scale up or down, or we'll just be scaling up every time we double tap. To do this, create a new variable just above the IBAction:
var imageZoomed = false

Copy and paste the following code into the IBAction function:

if (!imageZoomed) {
    scaleImageUp()
    imageZoomed = true
} else {
    normalizeImage()
    imageZoomed = false
}

Another way to handle zooming in and out is to use the image's transform property. This property allows you to transform the image by manipulating its size, position and rotation. Using transform, our functions become:

func scaleImageUp() {
    image.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
    imageZoomed = true
}
func normalizeImage() {
    image.transform = CGAffineTransform.identity
    imageZoomed = false
}

CGAffineTransform.identity is the default transform and resets the image to its orignal position.

Run the app. You should be able to double tap to scale up or normalize the image.

Pinch Gesture Recognizer

It is time to add some Pinch Gesture Recognizer magic.

Make an IBAction with the Pinch Gesture Recognizer the way you did for the Tap Gesture Recognizer. Name it pinchImage. This time, make sure 'Type' is set to UIPinchGestureRecognizer before clicking 'connect'.
Copy and paste the code below into the newly created IBAction:

if let imageView = sender.view {
    imageView.transform = imageView.transform.scaledBy(x: sender.scale, y: sender.scale)
    sender.scale = 1
}

Here, you access the image by targeting the Pinch Gesture Recognizer's view (remember, we dropped it on the image) and use the imageView's transform property to scale it depending on the movement of your fingers.

Say the image's size is 20 — to determine how to increase or decrease the size of the image, we target the scale property of the pinch gesture.

The default is one. If you start pinching with your fingers close and begin to separate them, the scale increases. Let's say it goes to 1.2, our image is now 20 * 1.2 (24). If you start on 24 and scale 1.5, you have 24 * 1.5 (36). The picture keeps increasing in size with each pinch.

The opposite happens if you start your fingers wider and begin to bring them close. If you start at 20, you'll have something like 20 * 0.9 (18) etc. Since the default is one, we reset back to it every time the pinch stops, so we don't continuously decrease or increase the image. You can test it out by changing 1 to 1.2 or 0.9. Once you start, you don't stop. Crazy!

If you're running the app in the simulator, you can use the pinch gesture by holding down alt/option, clicking on the image, and moving up or down.

Rotate Gesture Recognizer.

Create an IBAction the same way you created one for Pinch Gesture Recognizer. Don't forget to drag from Rotate Gesture Recognizer this time 😉.
Name the IBAction rotateImage.

Copy and paste the following into the IBAction function's body:

if let imageView = sender.view {
    imageView.transform = imageView.transform.rotated(by: sender.rotation)
    sender.rotation = 0
}

The explanation for this is similar to that of Pinch Gesture Recognizer. The default rotation is 0. It changes continuously as you rotate the picture (+ (positive) in the clockwise direction and - (negative) in the anticlockwise direction). Because we don't want the picture to keep rotating every time we rotate it, we reset the rotation to its default value.

Use alt/option the way you did with the pinch gesture.

Swipe Gesture Recognizer

What we want to do now is make the image shift some points to the right on swiping right and back to its position by swiping left.
Each Swipe Gesture Recognizer you add can be configured with a direction from the Attributes Inspector.
Screen Shot 2017-08-27 at 1.06.52 PM.png
Make an IBAction from the Swipe Gesture Recognizer. Name it swipeRight.
You can rename this Gesture Recognizer to 'Right Swipe Gesture Recognizer' to distinguish it. Do this by clicking on it and hitting 'enter'.

Because each Swipe Gesture Recognizer bears a direction, we have to add another since we need to be able to swipe left as well. From the Object Library, drag and drop a Swipe Gesture Recognizer onto the image. From the Attributes inspector, change its swipe direction from Right to Left. You can rename this one as well. Create an IBAction and name it swipeLeft. Let's shift stuff.

Put the following code in swipeRight:

image.transform = CGAffineTransform(translationX: 30, y: 0)

swipeLeft:

image.transform = CGAffineTransform(translationX: 0, y: 0)

We move 30 points to the right by swiping right and back to 0 by swiping left.

Okay, Can I Rotate and Pinch at the Same Time?

With what we have now, only one gesture can happen at a time. Even if you want to pinch and rotate at the same time, you can't. To make this possible, we'll have to override a function in the UIGestureRecognizerDelegate protocol.

At the point of declaration of the ViewController class, you have
class ViewController: UIViewController { — replace it with
class ViewController: UIViewController, UIGestureRecognizerDelegate {.
To peek inside this protocol, hold CMD and click on it.

The function we're implementing is this:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

Copy and paste that into the body of the class. The next thing is to connect the delegate outlet of each of the gesture recognizers to the View Controller. Do this by holding control, clicking, and dragging from the gesture recognizer to the View Controller this way:
Screen Shot 2017-08-27 at 3.45.20 PM.png

Click on delegate.
Screen Shot 2017-08-27 at 3.46.06 PM.png
You should be able to perform more than one gesture now.

Conclusion

I hope this was enough to get you comfortable with using gesture recognizers in iOS apps. Gestures are a very integral part of iOS apps and you should be able to implement them in your own apps now. This tutorial is not exhaustive — it's only meant to introduce the topic. There are a few other things that are not covered: You could implement your own custom gesture recognizer, for example.

You may also be wondering why you can't scroll in or out of the picture after zooming in or out on it. This can be done using scroll views. I will cover scroll views in my next tutorial. Stay tuned. Until then, gesturify away! 😉

If you like this post, please drop a like ❤️.

Discover and read more posts from Taiwo Adedotun
get started
Enjoy this post?

Leave a like and comment for Taiwo

2
Be the first to share your opinion

Get curated posts in your inbox

Read more posts to become a better developer