Detecting swipes on iOS and tvOS is trivial thanks to the UISwipeGestureRecognizer class, a concrete subclass of the UIGestureRecognizer class. In this post, I show you how to use a swipe gesture recognizer in Swift. We create a simple application that detects swipe gestures by leveraging UIKit's UISwipeGestureRecognizer class.

Setting Up the Project in Xcode

Fire up Xcode and create a blank project by choosing the App template from the iOS > Application section.

Setting Up the Project in Xcode

Name the project Swipes, set Interface to Storyboard, and Language to Swift.

Setting Up the Project in Xcode

Open ViewController.swift and declare a private, constant property with name swipeableView of type UIView. We use a self-executing closure to create and configure the UIView instance. The view has a blue background color and is positioned in the top left of the view controller's view. We set translatesAutoresizingMaskIntoConstraints to false because we don't want to use Auto Layout to size and position the view. Why that is becomes clear in a moment.

import UIKit

class ViewController: UIViewController {

    // MARK: - Properties

    private let swipeableView: UIView = {
        // Initialize View
        let view = UIView(frame: CGRect(origin: .zero,
                                        size: CGSize(width: 200.0, height: 200.0)))

        // Configure View
        view.backgroundColor = .blue
        view.translatesAutoresizingMaskIntoConstraints = false

        return view
    }()

    // MARK: - View Life Cycle

    override func viewDidLoad() {
        super.viewDidLoad()
    }

}

In the view controller's viewDidLoad() method, we add swipeableView to the view hierarchy.

import UIKit

class ViewController: UIViewController {

    // MARK: - Properties

    private let swipeableView: UIView = {
        // Initialize View
        let view = UIView(frame: CGRect(origin: .zero,
                                        size: CGSize(width: 200.0, height: 200.0)))

        // Configure View
        view.backgroundColor = .blue
        view.translatesAutoresizingMaskIntoConstraints = false

        return view
    }()

    // MARK: - View Life Cycle

    override func viewDidLoad() {
        super.viewDidLoad()

        // Add to View Hierarchy
        view.addSubview(swipeableView)
    }

}

Adding a Swipe Gesture Recognizer to a View

The plan is simple. The user should be able to move the blue view by swiping it in any direction. This means we need to add four swipe gesture recognizers to the blue view, one for each swipe direction. We initialize a UISwipeGestureRecognizer instance by invoking the init(target:action:) initializer. The target is the view controller and the action is a method with name didSwipe(_:). We implement the didSwipe(_:) method in a moment.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    // Add to View Hierarchy
    view.addSubview(swipeableView)

    // Initialize Swipe Gesture Recognizer
    let swipeGestureRecognizerDown = UISwipeGestureRecognizer(target: self, action: #selector(didSwipe(_:)))
}

Before we add the swipe gesture recognizer to swipeableView, we need to set the direction property of the UISwipeGestureRecognizer instance. A swipe gesture recognizer detects swipes in one of four directions, up, down, left, and right. We set the direction property of the swipe gesture recognizer to down. If the user swipes from the top of the blue view to the bottom of the blue view, the swipe gesture recognizer invokes the didSwipe(_:) method on the view controller.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    // Add to View Hierarchy
    view.addSubview(swipeableView)

    // Initialize Swipe Gesture Recognizer
    let swipeGestureRecognizerDown = UISwipeGestureRecognizer(target: self, action: #selector(didSwipe(_:)))

    // Configure Swipe Gesture Recognizer
    swipeGestureRecognizerDown.direction = .down
}

Before we implement the didSwipe(_:) method, we add the swipe gesture recognizer to swipeableView. We invoke the addGestureRecognizer(_:) method on swipeableView, passing in the swipe gesture recognizer.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    // Add to View Hierarchy
    view.addSubview(swipeableView)

    // Initialize Swipe Gesture Recognizer
    let swipeGestureRecognizerDown = UISwipeGestureRecognizer(target: self, action: #selector(didSwipe(_:)))

    // Configure Swipe Gesture Recognizer
    swipeGestureRecognizerDown.direction = .down

    // Add Swipe Gesture Recognizer
    swipeableView.addGestureRecognizer(swipeGestureRecognizerDown)
}

The implementation of the didSwipe(_:) method isn't difficult. The method accepts a single argument of type UISwipeGestureRecognizer, the swipe gesture recognizer that detected the swipe gesture. We prefix the method with the objc attribute to exposes it to the Objective-C runtime. This is a more advanced topic that I won't cover in this post.

// MARK: - Actions

@objc private func didSwipe(_ sender: UISwipeGestureRecognizer) {

}

If we omit the objc attribute, the compiler throws an error.

We need to prefix the method with the objc attribute to expose it to the Objective-C runtime.

In the didSwipe(_:) method, we update the frame of swipeableView. We ask swipeableView for its current frame and update the frame's origin. We update the view with the new frame and animate the change.

// MARK: - Actions

@objc private func didSwipe(_ sender: UISwipeGestureRecognizer) {
    // Current Frame
    var frame = swipeableView.frame

    // New Frame
    frame.origin.y += 100.0

    UIView.animate(withDuration: 0.25) {
        self.swipeableView.frame = frame
    }
}

Build and run the application and give it a try. Swipe down on the blue view and watch it move.

Adding Multiple Swipe Gesture Recognizers to a View

It is perfectly fine for a view to have multiple swipe gesture recognizers. Let's add three more swipe gesture recognizers to enable the user to swipe the blue view in any direction. We first create a helper method to avoid code duplication.

// MARK: - Helper Methods

private func createSwipeGestureRecognizer(for direction: UISwipeGestureRecognizer.Direction) -> UISwipeGestureRecognizer {
    // Initialize Swipe Gesture Recognizer
    let swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(didSwipe(_:)))

    // Configure Swipe Gesture Recognizer
    swipeGestureRecognizer.direction = direction

    return swipeGestureRecognizer
}

The helper method doesn't contain anything new. It accepts a single argument of type UISwipeGestureRecognizer.Direction, the direction of the swipe gesture recognizer. The createSwipeGestureRecognizer(for:) method creates and configures a swipe gesture recognizer and returns it.

We can use the createSwipeGestureRecognizer(for:) method in the view controller's viewDidLoad() method to create a swipe gesture recognizer for each direction. We pass the result of createSwipeGestureRecognizer(for:) to the addGestureRecognizer(_:) method.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    // Add to View Hierarchy
    view.addSubview(swipeableView)

    // Create Swipe Gesture Recognizers
    swipeableView.addGestureRecognizer(createSwipeGestureRecognizer(for: .up))
    swipeableView.addGestureRecognizer(createSwipeGestureRecognizer(for: .down))
    swipeableView.addGestureRecognizer(createSwipeGestureRecognizer(for: .left))
    swipeableView.addGestureRecognizer(createSwipeGestureRecognizer(for: .right))
}

Each swipe gesture recognizer invokes the didSwipe(_:) method on the view controller when it detects a swipe gesture. This means we need to update the didSwipe(_:) method because it currently only supports swiping down. A switch statement is all we need.

// MARK: - Actions

@objc private func didSwipe(_ sender: UISwipeGestureRecognizer) {
    // Current Frame
    var frame = swipeableView.frame

    switch sender.direction {
    case .up:
        frame.origin.y -= 100.0
    case .down:
        frame.origin.y += 100.0
    case .left:
        frame.origin.x -= 100.0
    case .right:
        frame.origin.x += 100.0
    default:
        break
    }

    UIView.animate(withDuration: 0.25) {
        self.swipeableView.frame = frame
    }
}

We ask the swipe gesture recognizer for the value of its direction property and use it to determine the new frame of swipeableView. That isn't difficult. Right?

There is one problem we need to address, though. It is possible for the view to be swiped off-screen. That isn't what we want. We need to make sure the view stays within the bounds of its superview. This is what the updated implementation of the didSwipe(_:) method looks like.

// MARK: - Actions

@objc private func didSwipe(_ sender: UISwipeGestureRecognizer) {
    // Current Frame
    var frame = swipeableView.frame

    switch sender.direction {
    case .up:
        frame.origin.y -= 100.0
        frame.origin.y = max(0.0, frame.origin.y)
    case .down:
        frame.origin.y += 100.0

        if frame.maxY > view.bounds.maxY {
            frame.origin.y = view.bounds.height - frame.height
        }
    case .left:
        frame.origin.x -= 100.0
        frame.origin.x = max(0.0, frame.origin.x)
    case .right:
        frame.origin.x += 100.0

        if frame.maxX > view.bounds.maxX {
            frame.origin.x = view.bounds.width - frame.width
        }
    default:
        break
    }

    UIView.animate(withDuration: 0.25) {
        self.swipeableView.frame = frame
    }
}

Build and run the application to test the implementation. The user is able to swipe the blue view in any direction. The UISwipeGestureRecognizer class makes this very easy to implement.