Adding Haptic Feedback with Feedback Generators in Swift

Adding Haptic Feedback with Feedback Generators in Swift

Resources

Haptic feedback is great for providing the user with a tactile response. It adds that extra dimension to your application's user experience. Apple has made it very easy to integrate haptic feedback into your application thanks to the UIFeedbackGenerator class. You don't directly use UIFeedbackGenerator, though. UIKit defines three concrete subclasses you can use in your projects, UIImpactFeedbackGenerator, UISelectionFeedbackGenerator, and UINotificationFeedbackGenerator. Each of these classes has a clearly defined purpose. Let's take a look.

Which Devices Support Haptic Feedback?

Not every device has a taptic engine. No taptic engine means no haptic feedback. The system handles this gracefully, though. You, as a developer, don't need to worry about this. If the device doesn't have a taptic engine, nothing happens.

UIFeedbackGenerator and its concrete subclasses were introduced in iOS 10. Apple introduced Core Haptics in iOS 13. Core Haptics is a framework that takes haptic feedback to a whole new level. This post focuses UIFeedbackGenerator and its concrete subclasses.

As I mentioned earlier, you, as a developer, don't need to check whether the device supports haptic feedback. That is something the system handles. If the device doesn't support haptic feedback, the system ignores the request.

Sample Application

I have created a simple application that demonstrates every possible type of haptic feedback UIKit offers at the time of writing. Download the starter project of this post and open it in Xcode. As you might have guessed, the simulator doesn't support haptic feedback. I recommend running the application on a physical device. This is what you should see.

Adding Haptic Feedback in Swift

The application displays a table view with three sections, a section for each haptic feedback type. Let's start with the top section.

UISelectionFeedbackGenerator

The UIFeedbackGenerator subclasses are easy to use. Open HapticsTableViewController.swift and navigate to the generateHapticFeedback(for:) method. This is what the method looks like.

// MARK: - Helper Methods

private func generateHapticFeedback(for hapticFeedback: HapticFeedback) {
    switch hapticFeedback {
    case .selection:
        // Initialize Selection Feedback Generator
        let feedbackGenerator = UISelectionFeedbackGenerator()

        // Trigger Haptic Feedback
        feedbackGenerator.selectionChanged()
    case .impact(let feedbackStyle):
        // Initialize Impact Feedback Generator
        let feedbackGenerator = UIImpactFeedbackGenerator(style: feedbackStyle)

        // Trigger Haptic Feedback
        feedbackGenerator.impactOccurred()
    case .notification(let feedbackType):
        // Initialize Notification Feedback Generator
        let feedbackGenerator = UINotificationFeedbackGenerator()

        // Trigger Haptic Feedback
        feedbackGenerator.notificationOccurred(feedbackType)
    }
}

Don't worry about the HapticFeedback type. HapticFeedback is a private type the view controller uses to populate the table view. Let's take a look at the switch statement of generateHapticFeedback(for:).

If the user taps the only row of the Selection section, the view controller creates an instance of the UISelectionFeedbackGenerator class. To generate haptic feedback, the view controller calls selectionChanged() on the UISelectionFeedbackGenerator instance. That's it.

// Initialize Selection Feedback Generator
let feedbackGenerator = UISelectionFeedbackGenerator()

// Trigger Haptic Feedback
feedbackGenerator.selectionChanged()

As the name suggests, this type of haptic feedback should be used to communicate that a change in selection took place. Apple's Clock application is a fine example. To set a timer, you interact with a picker view. Every time the selection changes, the application generates haptic feedback using the UISelectionFeedbackGenerator class. Several UIKit components have built-in support for haptic feedback, including UISwitch and UIPickerView.

A picker view uses haptic feedback to indicate the selection changed.

UIImpactFeedbackGenerator

The Impact section has a number of options we can choose from. The haptic feedback is generated by an instance of the UIImpactFeedbackGenerator class. This class is used to simulate physical impacts, for example, objects colliding or a user interface element snapping into place.

The designated initializer accepts an object of type UIImpactFeedbackGenerator.FeedbackStyle. The FeedbackStyle object defines the type of haptic feedback the feedback generator produces. To trigger haptic feedback, the view controller calls impactOccurred() on the UIImpactFeedbackGenerator instance.

// Initialize Impact Feedback Generator
let feedbackGenerator = UIImpactFeedbackGenerator(style: feedbackStyle)

// Trigger Haptic Feedback
feedbackGenerator.impactOccurred()

The UIImpactFeedbackGenerator class also exposes the impactOccurred(intensity:) method to generate haptic feedback. This method accepts a single argument of type CGFloat to specify the intensity of the haptic feedback. Unfortunately, Apple's documentation doesn't specify which values are acceptable or recommended. If you create the impact feedback generator using init(feedbackStyle:), the value you pass to impactOccurred(intensity:) is ignored and the value of feedbackStyle is used to specify the intensity of the haptic feedback. I don't recommend using impactOccurred(intensity:) to generate haptic feedback due to its lack of documentation.

The light, medium, and heavy feedback styles are pretty descriptive. Apple introduced soft and rigid in iOS 13. Their meaning is less clear and Apple's documentation isn't a big help either.

UINotificationFeedbackGenerator

The third and last UIFeedbackGenerator subclass is UINotificationFeedbackGenerator. As the name suggests, the feedback generator can be used to notify the user of an event. The UINotificationFeedbackGenerator class defines a single method, notificationOccurred(_:), that accepts an argument of type UINotificationFeedbackGenerator.FeedbackType. The possible values are error, success, and warning.

Using the UINotificationFeedbackGenerator class is straightforward. The view controller creates an instance of the class and calls notificationOccurred(_:) on the feedback generator, passing in a UINotificationFeedbackGenerator.FeedbackType object.

// Initialize Notification Feedback Generator
let feedbackGenerator = UINotificationFeedbackGenerator()

// Trigger Haptic Feedback
feedbackGenerator.notificationOccurred(feedbackType)

When would you use the notification feedback generator? Your application displays a list of photos and the user can like a photo. If your application isn't able to process the user's request, then you might want to show an error to the user. The error feedback type is a good fit for this scenario. An application that implements pull-to-refresh can trigger the success haptic feedback when it successfully refreshed the user interface in response to the user pulling the table or collection view to refresh its data.

Preparing the Generator

Haptic feedback is generated by the taptic engine, a piece of hardware embedded in the device. To save power, the taptic engine is idle if it has nothing to do. It takes some time for the taptic engine to wake up and that can result in a slight delay. To reduce that delay, you can ask the feedback generator to prepare the taptic engine by invoking prepare() on the feedback generator. Calling prepare() is optional, though. As you can see in the starter project, you can generate haptic feedback without first calling prepare on the feedback generator.

I recently applied this pattern in a paging scroll view. After creating the feedback generator, I invoked the prepare() method in the scrollViewWillBeginDragging(_:) method of the UIScrollViewDelegate protocol. Every time the user scrolled to a different page, the feedback generator produced haptic feedback. Preparing the feedback generator is even more important in games where timing is of the essence for the haptic feedback to be effective.