Hold-to-confirm with rising tension
A "hold 1.5 s to confirm" button: vibration ramps up while holding, success fires at completion.
import SwiftUI
import CoreHaptics
struct HoldToConfirmButton: View {
let duration: TimeInterval = 1.5
let onConfirm: () -> Void
@State private var engine: CHHapticEngine?
@State private var player: CHHapticAdvancedPatternPlayer?
@State private var progress: CGFloat = 0
@State private var holding = false
var body: some View {
Text("Hold to confirm")
.font(.headline).foregroundStyle(.white)
.frame(maxWidth: .infinity).padding()
.background(
GeometryReader { geo in
ZStack(alignment: .leading) {
Color.red.opacity(0.4)
Color.red.frame(width: geo.size.width * progress)
}
}, in: .rect(cornerRadius: 14)
)
.onLongPressGesture(
minimumDuration: duration,
perform: confirm,
onPressingChanged: pressingChanged
)
.onAppear(perform: setupEngine)
}
private func setupEngine() {
guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return }
engine = try? CHHapticEngine()
try? engine?.start()
}
private func pressingChanged(_ pressing: Bool) {
holding = pressing
if pressing {
startRamp()
withAnimation(.linear(duration: duration)) { progress = 1 }
} else {
try? player?.stop(atTime: CHHapticTimeImmediate) // cancel = cut
withAnimation(.easeOut(duration: 0.2)) { progress = 0 }
}
}
private func startRamp() {
guard let engine else { return }
// Continuous event whose intensity climbs 0.1 → 1.0 over the hold
let event = CHHapticEvent(
eventType: .hapticContinuous,
parameters: [
CHHapticEventParameter(parameterID: .hapticIntensity, value: 1.0),
CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.4),
],
relativeTime: 0,
duration: duration
)
let curve = CHHapticParameterCurve(
parameterID: .hapticIntensityControl,
controlPoints: [
.init(relativeTime: 0, value: 0.1),
.init(relativeTime: duration, value: 1.0),
],
relativeTime: 0
)
if let pattern = try? CHHapticPattern(events: [event],
parameterCurves: [curve]) {
player = try? engine.makeAdvancedPlayer(with: pattern)
try? player?.start(atTime: CHHapticTimeImmediate)
}
}
private func confirm() {
UINotificationFeedbackGenerator().notificationOccurred(.success)
onConfirm()
}
}