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.
Name the project Swipes, set Interface to Storyboard, and Language to Swift.
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.
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.