@@ -63,6 +63,9 @@ @implementation TORoundedButton {
6363 /* * Maintain a reference to the corner configuration in case we swap out the background view */
6464 UICornerConfiguration *_cornerConfiguration API_AVAILABLE (ios (26.0 ));
6565#endif
66+
67+ /* * The current haptic feedback generator that will play vibrations when the button is tapped. */
68+ UIImpactFeedbackGenerator *_impactGenerator;
6669}
6770
6871#pragma mark - View Creation -
@@ -118,6 +121,7 @@ - (void)_roundedButtonCommonInit TOROUNDEDBUTTON_OBJC_DIRECT {
118121 _tappedTintColorBrightnessOffset = !TORoundedButtonFloatIsZero (_tappedTintColorBrightnessOffset) ?: -0 .15f ;
119122 _contentInset = (UIEdgeInsets){15.0 , 15.0 , 15.0 , 15.0 };
120123 _blurStyle = UIBlurEffectStyleDark;
124+ _impactStyle = TORoundedButtonImpactStyleMedium;
121125
122126 // Set the corner radius depending on system version
123127#ifdef __IPHONE_26_0
@@ -211,6 +215,19 @@ - (UIView *)_makeBackgroundViewWithStyle:(TORoundedButtonBackgroundStyle)style T
211215 return backgroundView;
212216}
213217
218+ - (UIImpactFeedbackGenerator *)_makeImpactGeneratorWithStyle : (TORoundedButtonImpactStyle)style TOROUNDEDBUTTON_OBJC_DIRECT {
219+ if (style == TORoundedButtonImpactStyleNone) {
220+ return nil ;
221+ }
222+
223+ const UIImpactFeedbackStyle impactStyle = (UIImpactFeedbackStyle)style;
224+ if (@available (iOS 17.5 , *)) {
225+ return [UIImpactFeedbackGenerator feedbackGeneratorWithStyle: impactStyle forView: self ];
226+ }
227+
228+ return [[UIImpactFeedbackGenerator alloc ] initWithStyle: impactStyle];
229+ }
230+
214231#pragma mark - View Lifecycle -
215232
216233- (void )didMoveToSuperview {
@@ -222,6 +239,11 @@ - (void)didMoveToSuperview {
222239 // Defer making the background until we're added to the subview in case the user changes it
223240 _backgroundView = [self _makeBackgroundViewWithStyle: _backgroundStyle];
224241 [_containerView insertSubview: _backgroundView atIndex: 0 ];
242+
243+ // If the button has been configured to set up an impact generator, create it now.
244+ if (_impactStyle != TORoundedButtonImpactStyleNone && !_impactGenerator) {
245+ _impactGenerator = [self _makeImpactGeneratorWithStyle: _impactStyle];
246+ }
225247}
226248
227249- (void )tintColorDidChange {
@@ -308,6 +330,9 @@ - (CGSize)sizeThatFits:(CGSize)size {
308330- (void )_didTouchDownInside {
309331 _isTapped = YES ;
310332
333+ // Preheat the impact generator to prepare playing it when we tap up.
334+ [_impactGenerator prepare ];
335+
311336 // The user touched their finger down into the button bounds
312337 [self _setLabelAlphaTappedAnimated: NO ];
313338 [self _setBackgroundColorTappedAnimated: YES ];
@@ -317,6 +342,9 @@ - (void)_didTouchDownInside {
317342- (void )_didTouchUpInside {
318343 _isTapped = NO ;
319344
345+ // Play the impact.
346+ [_impactGenerator impactOccurred ];
347+
320348 // The user lifted their finger up from inside the button bounds
321349 [self _setLabelAlphaTappedAnimated: YES ];
322350 [self _setBackgroundColorTappedAnimated: YES ];
@@ -585,6 +613,12 @@ - (void)setEnabled:(BOOL)enabled {
585613 _containerView.alpha = enabled ? 1 : 0.4 ;
586614}
587615
616+ - (void )setImpactStyle : (TORoundedButtonImpactStyle)impactStyle {
617+ if (_impactStyle == impactStyle) { return ; }
618+ _impactStyle = impactStyle;
619+ _impactGenerator = [self _makeImpactGeneratorWithStyle: impactStyle];
620+ }
621+
588622#pragma mark - Private -
589623
590624- (void )_setBackgroundTintColor : (UIColor *)tintColor TOROUNDEDBUTTON_OBJC_DIRECT {
0 commit comments