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.
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
.
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.