Haptic feedback provides tactile responses that enhance user experience. Learn to implement haptics in iOS apps.
UIKit Haptics
Basic Haptic Feedback
swift
import UIKit
class HapticManager {
static let shared = HapticManager()
private init() {}
func notification(_ type: UINotificationFeedbackGenerator.FeedbackType) {
let generator = UINotificationFeedbackGenerator()
generator.prepare()
generator.notificationOccurred(type)
}
func impact(_ style: UIImpactFeedbackGenerator.FeedbackStyle) {
let generator = UIImpactFeedbackGenerator(style: style)
generator.prepare()
generator.impactOccurred()
}
func selection() {
let generator = UISelectionFeedbackGenerator()
generator.prepare()
generator.selectionChanged()
}
}Feedback Types
swift
struct HapticExamplesView: View {
var body: some View {
VStack(spacing: 20) {
Button("Success") {
HapticManager.shared.notification(.success)
}
Button("Warning") {
HapticManager.shared.notification(.warning)
}
Button("Error") {
HapticManager.shared.notification(.error)
}
Divider()
Button("Light Impact") {
HapticManager.shared.impact(.light)
}
Button("Medium Impact") {
HapticManager.shared.impact(.medium)
}
Button("Heavy Impact") {
HapticManager.shared.impact(.heavy)
}
Divider()
Button("Selection") {
HapticManager.shared.selection()
}
}
.padding()
}
}Core Haptics
Custom Haptic Patterns
swift
import CoreHaptics
class AdvancedHapticManager: ObservableObject {
private var hapticEngine: CHHapticEngine?
func setupHaptics() {
guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else {
print("Haptics not supported")
return
}
do {
hapticEngine = try CHHapticEngine()
try hapticEngine?.start()
} catch {
print("Haptic engine creation failed: \(error)")
}
}
func playCustomHaptic() {
guard let engine = hapticEngine else {
setupHaptics()
return
}
// Create haptic pattern
let sharpness = CHHapticEventParameter(
parameterID: .hapticSharpness,
value: 1.0
)
let intensity = CHHapticEventParameter(
parameterID: .hapticIntensity,
value: 1.0
)
let event = CHHapticEvent(
eventType: .hapticTransient,
parameters: [sharpness, intensity],
relativeTime: 0,
duration: 0.1
)
do {
let pattern = try CHHapticPattern(events: [event], parameters: [])
let player = try engine.makePlayer(with: pattern)
try player.start(atTime: 0)
} catch {
print("Failed to play haptic: \(error)")
}
}
}Continuous Haptics
swift
extension AdvancedHapticManager {
func playContinuousHaptic(duration: TimeInterval) {
guard let engine = hapticEngine else { return }
let sharpness = CHHapticEventParameter(
parameterID: .hapticSharpness,
value: 0.5
)
let intensity = CHHapticEventParameter(
parameterID: .hapticIntensity,
value: 0.8
)
let event = CHHapticEvent(
eventType: .hapticContinuous,
parameters: [sharpness, intensity],
relativeTime: 0,
duration: duration
)
do {
let pattern = try CHHapticPattern(events: [event], parameters: [])
let player = try engine.makePlayer(with: pattern)
try player.start(atTime: 0)
} catch {
print("Failed to play continuous haptic: \(error)")
}
}
}Haptic Patterns
Predefined Patterns
swift
struct HapticPatternManager {
func playHeartbeat() {
let pattern = [
(delay: 0.0, intensity: 1.0, duration: 0.1),
(delay: 0.15, intensity: 0.8, duration: 0.1),
(delay: 0.4, intensity: 1.0, duration: 0.1),
(delay: 0.15, intensity: 0.8, duration: 0.1)
]
for (delay, intensity, duration) in pattern {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
let generator = UIImpactFeedbackGenerator(style: .medium)
generator.impactOccurred()
}
}
}
func playNotificationPattern() {
// Double tap pattern
HapticManager.shared.impact(.light)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
HapticManager.shared.impact(.light)
}
}
}SwiftUI Integration
Haptic Button
swift
struct HapticButton: View {
let title: String
let action: () -> Void
var hapticStyle: UIImpactFeedbackGenerator.FeedbackStyle = .medium
var body: some View {
Button(action: {
HapticManager.shared.impact(hapticStyle)
action()
}) {
Text(title)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
}
struct HapticButtonsView: View {
var body: some View {
VStack(spacing: 20) {
HapticButton(title: "Like", hapticStyle: .light) {
print("Liked")
}
HapticButton(title: "Share", hapticStyle: .medium) {
print("Shared")
}
HapticButton(title: "Delete", hapticStyle: .heavy) {
print("Deleted")
}
}
}
}Haptic Toggle
swift
struct HapticToggle: View {
@Binding var isOn: Bool
var body: some View {
Toggle("Enable", isOn: $isOn)
.onChange(of: isOn) { _, newValue in
if newValue {
HapticManager.shared.notification(.success)
} else {
HapticManager.shared.notification(.warning)
}
}
}
}Context-Aware Haptics
Smart Haptic Feedback
swift
class ContextAwareHaptics {
func provideFeedback(for action: UserAction) {
switch action {
case .success:
HapticManager.shared.notification(.success)
case .failure:
HapticManager.shared.notification(.error)
case .warning:
HapticManager.shared.notification(.warning)
case .tap:
HapticManager.shared.selection()
case .scroll:
HapticManager.shared.impact(.light)
}
}
enum UserAction {
case success
case failure
case warning
case tap
case scroll
}
}Haptic Customization
Intensity Control
swift
class CustomHaptics {
func playScaledHaptic(intensity: Float) {
let generator = UIImpactFeedbackGenerator(style: .medium)
generator.prepare()
// Scale impact based on intensity
switch intensity {
case 0.0..<0.3:
generator.impactOccurred(intensity: 0.3)
case 0.3..<0.7:
generator.impactOccurred(intensity: intensity)
default:
generator.impactOccurred(intensity: 1.0)
}
}
}Best Practices
- 1Restraint: Use haptics sparingly
- 2Context: Provide context-relevant feedback
- 3Intensity: Match intensity to action importance
- 4Duration: Keep haptics brief
- 5Testing: Test on real devices
- 6Preferences: Respect user preferences
- 7Battery: Consider battery impact
- 8Accessibility: Provide visual alternatives
Haptic feedback creates engaging, tactile experiences!