Skip to content

Commit e614d5d

Browse files
authored
Merge pull request #57 from TimOliver/taptic
Added haptic APIs
2 parents 11b3b60 + 7c373b0 commit e614d5d

2 files changed

Lines changed: 52 additions & 2 deletions

File tree

TORoundedButton/TORoundedButton.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ typedef NS_ENUM(NSInteger, TORoundedButtonBackgroundStyle) {
3333
TORoundedButtonBackgroundStyleGlass
3434
};
3535

36+
/// The types of impacts that the button can generate when tapped by the user.
37+
typedef NS_ENUM(NSInteger, TORoundedButtonImpactStyle) {
38+
TORoundedButtonImpactStyleNone = -1,
39+
TORoundedButtonImpactStyleLight = UIImpactFeedbackStyleLight,
40+
TORoundedButtonImpactStyleMedium = UIImpactFeedbackStyleMedium,
41+
TORoundedButtonImpactStyleHeavy = UIImpactFeedbackStyleHeavy
42+
};
43+
3644
NS_SWIFT_NAME(RoundedButtonDelegate)
3745
@protocol TORoundedButtonDelegate <NSObject>
3846

@@ -75,6 +83,9 @@ IB_DESIGNABLE @interface TORoundedButton : UIControl
7583
/// The style, whether static or dynamic of the button's background view.
7684
@property (nonatomic, assign) TORoundedButtonBackgroundStyle backgroundStyle;
7785

86+
/// The haptic impact style that this button will generate when tapped by the user. (Default is medium)
87+
@property (nonatomic, assign) TORoundedButtonImpactStyle impactStyle;
88+
7889
/// When `backgroundStyle` is set to `.blur`, the specific blur style to apply.
7990
@property (nonatomic, assign) UIBlurEffectStyle blurStyle;
8091

TORoundedButton/TORoundedButton.m

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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
@@ -150,7 +154,7 @@ - (void)_roundedButtonCommonInit TOROUNDEDBUTTON_OBJC_DIRECT {
150154

151155
// Create action events for all possible interactions with this control
152156
[self addTarget:self action:@selector(_didTouchDownInside) forControlEvents:UIControlEventTouchDown|UIControlEventTouchDownRepeat];
153-
[self addTarget:self action:@selector(_didTouchUpInside) forControlEvents:UIControlEventTouchUpInside];
157+
[self addTarget:self action:@selector(_didTouchUpInside:event:) forControlEvents:UIControlEventTouchUpInside];
154158
[self addTarget:self action:@selector(_didDragOutside) forControlEvents:UIControlEventTouchDragExit|UIControlEventTouchCancel];
155159
[self addTarget:self action:@selector(_didDragInside) forControlEvents:UIControlEventTouchDragEnter];
156160
}
@@ -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,15 +330,26 @@ - (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];
314339
[self _setButtonScaledTappedAnimated:YES];
315340
}
316341

317-
- (void)_didTouchUpInside {
342+
- (void)_didTouchUpInside:(id)sender event:(UIEvent *)event {
318343
_isTapped = NO;
319344

345+
// Play the impact to lock in that the user committed to this action.
346+
if (@available(iOS 17.5, *)) {
347+
const CGPoint touchPoint = [event.allTouches.anyObject locationInView:self];
348+
[_impactGenerator impactOccurredAtLocation:touchPoint];
349+
} else {
350+
[_impactGenerator impactOccurred];
351+
}
352+
320353
// The user lifted their finger up from inside the button bounds
321354
[self _setLabelAlphaTappedAnimated:YES];
322355
[self _setBackgroundColorTappedAnimated:YES];
@@ -585,6 +618,12 @@ - (void)setEnabled:(BOOL)enabled {
585618
_containerView.alpha = enabled ? 1 : 0.4;
586619
}
587620

621+
- (void)setImpactStyle:(TORoundedButtonImpactStyle)impactStyle {
622+
if (_impactStyle == impactStyle) { return; }
623+
_impactStyle = impactStyle;
624+
_impactGenerator = [self _makeImpactGeneratorWithStyle:impactStyle];
625+
}
626+
588627
#pragma mark - Private -
589628

590629
- (void)_setBackgroundTintColor:(UIColor *)tintColor TOROUNDEDBUTTON_OBJC_DIRECT {

0 commit comments

Comments
 (0)