2727
2828// --------------------------------------------------------------------
2929
30- static inline BOOL TO_ROUNDED_BUTTON_FLOAT_IS_ZERO (CGFloat value) {
30+ static inline BOOL TORoundedButtonFloatIsZero (CGFloat value) {
3131 return (value > -FLT_EPSILON) && (value < FLT_EPSILON);
3232}
3333
34- static inline BOOL TO_ROUNDED_BUTTON_FLOATS_MATCH (CGFloat firstValue, CGFloat secondValue) {
34+ static inline BOOL TORoundedButtonFloatsMatch (CGFloat firstValue, CGFloat secondValue) {
3535 return fabs (firstValue - secondValue) > FLT_EPSILON;
3636}
3737
38+ static inline BOOL TORoundedButtonIsDynamicBackground (TORoundedButtonBackgroundStyle backgroundStyle) {
39+ return backgroundStyle != TORoundedButtonBackgroundStyleSolid;
40+ }
41+
3842// --------------------------------------------------------------------
3943
4044@implementation TORoundedButton {
@@ -50,6 +54,9 @@ @implementation TORoundedButton {
5054
5155 /* * A background view that displays the rounded box behind the button text. */
5256 UIView *_backgroundView;
57+
58+ /* * Manage an internal reference to the corner configuration so we can apply it to different backgrounds. */
59+ UICornerConfiguration *_cornerConfiguration API_AVAILABLE (ios (26.0 ));
5360}
5461
5562#pragma mark - View Creation -
@@ -102,13 +109,20 @@ - (void)_roundedButtonCommonInit TOROUNDEDBUTTON_OBJC_DIRECT {
102109 _tappedTextAlpha = (_tappedTextAlpha > FLT_EPSILON) ?: 1 .0f ;
103110 _tapAnimationDuration = (_tapAnimationDuration > FLT_EPSILON) ?: 0 .4f ;
104111 _tappedButtonScale = (_tappedButtonScale > FLT_EPSILON) ?: 0 .97f ;
105- _tappedTintColorBrightnessOffset = !TO_ROUNDED_BUTTON_FLOAT_IS_ZERO (_tappedTintColorBrightnessOffset) ?: -0 .15f ;
112+ _tappedTintColorBrightnessOffset = !TORoundedButtonFloatIsZero (_tappedTintColorBrightnessOffset) ?: -0 .15f ;
106113 _contentInset = (UIEdgeInsets){15.0 , 15.0 , 15.0 , 15.0 };
107114 _blurStyle = UIBlurEffectStyleDark;
108115#ifdef __IPHONE_13_0
109116 if (@available (iOS 13.0 , *)) { _blurStyle = UIBlurEffectStyleSystemThinMaterialDark; }
110117#endif
111118
119+ // Set the corner radius depending on system version
120+ if (@available (iOS 26.0 , *)) {
121+ _cornerConfiguration = [UICornerConfiguration capsuleConfiguration ];
122+ } else {
123+ _cornerRadius = (_cornerRadius > FLT_EPSILON) ?: 12 .0f ;
124+ }
125+
112126 // Set the tapped tint color if we've set to dynamically calculate it
113127 [self _updateTappedTintColorForTintColor ];
114128
@@ -117,11 +131,10 @@ - (void)_roundedButtonCommonInit TOROUNDEDBUTTON_OBJC_DIRECT {
117131 _containerView.backgroundColor = [UIColor clearColor ];
118132 _containerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
119133 _containerView.userInteractionEnabled = NO ;
120- _containerView.clipsToBounds = YES ;
121134 [self addSubview: _containerView];
122135
123136 // Create the image view which will show the button background
124- _backgroundView = [self _makeBackgroundViewWithBlur: _isTranslucent ];
137+ _backgroundView = [self _makeBackgroundViewWithStyle: _backgroundStyle ];
125138 [_containerView addSubview: _backgroundView];
126139
127140 // The foreground content view
@@ -132,13 +145,6 @@ - (void)_roundedButtonCommonInit TOROUNDEDBUTTON_OBJC_DIRECT {
132145 [self addTarget: self action: @selector (_didTouchUpInside ) forControlEvents: UIControlEventTouchUpInside];
133146 [self addTarget: self action: @selector (_didDragOutside ) forControlEvents: UIControlEventTouchDragExit|UIControlEventTouchCancel];
134147 [self addTarget: self action: @selector (_didDragInside ) forControlEvents: UIControlEventTouchDragEnter];
135-
136- // Set the corner radius depending on app version
137- if (@available (iOS 26.0 , *)) {
138- self.cornerConfiguration = [UICornerConfiguration capsuleConfiguration ];
139- } else {
140- _cornerRadius = (_cornerRadius > FLT_EPSILON) ?: 12 .0f ;
141- }
142148}
143149
144150- (void )_makeTitleLabelIfNeeded TOROUNDEDBUTTON_OBJC_DIRECT {
@@ -160,18 +166,36 @@ - (void)_makeTitleLabelIfNeeded TOROUNDEDBUTTON_OBJC_DIRECT {
160166 [_contentView addSubview: _titleLabel];
161167}
162168
163- - (UIView *)_makeBackgroundViewWithBlur : ( BOOL ) withBlur TOROUNDEDBUTTON_OBJC_DIRECT {
169+ - (UIView *)_makeBackgroundViewWithStyle : (TORoundedButtonBackgroundStyle) style TOROUNDEDBUTTON_OBJC_DIRECT {
164170 UIView *backgroundView = nil ;
165- if (withBlur) {
166- UIBlurEffect *const blurEffect = [UIBlurEffect effectWithStyle: _blurStyle];
167- backgroundView = [[UIVisualEffectView alloc ] initWithEffect: blurEffect];
168- backgroundView.clipsToBounds = YES ;
171+ if (TORoundedButtonIsDynamicBackground (style)) {
172+ // Create a glass or blur style based on the associated style
173+ UIVisualEffect *effect = nil ;
174+ if (@available (iOS 26.0 , *)) {
175+ if (style == TORoundedButtonBackgroundStyleGlass) {
176+ UIGlassEffect *const glassEffect = [UIGlassEffect effectWithStyle: _glassStyle];
177+ glassEffect.interactive = YES ;
178+ glassEffect.tintColor = self.tintColor ;
179+ effect = glassEffect;
180+ }
181+ }
182+ if (effect == nil ) {
183+ UIBlurEffect *const blurEffect = [UIBlurEffect effectWithStyle: _blurStyle];
184+ effect = blurEffect;
185+ }
186+ backgroundView = [[UIVisualEffectView alloc ] initWithEffect: effect];
169187 } else {
170188 backgroundView = [[UIView alloc ] initWithFrame: CGRectZero];
171189 backgroundView.backgroundColor = self.tintColor ;
172190 }
173191 backgroundView.frame = self.bounds ;
174192 backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
193+ if (@available (iOS 26.0 , *)) {
194+ backgroundView.cornerConfiguration = _cornerConfiguration;
195+ } else {
196+ backgroundView.clipsToBounds = TORoundedButtonIsDynamicBackground (style);
197+ backgroundView.layer .cornerRadius = _cornerRadius;
198+ }
175199#ifdef __IPHONE_13_0
176200 if (@available (iOS 13.0 , *)) { backgroundView.layer .cornerCurve = kCACornerCurveContinuous ; }
177201#endif
@@ -244,7 +268,7 @@ - (CGSize)sizeThatFits:(CGSize)size {
244268
245269- (void )tintColorDidChange {
246270 [super tintColorDidChange ];
247- if (_isTranslucent ) { return ; }
271+ if (TORoundedButtonIsDynamicBackground (_backgroundStyle) ) { return ; }
248272 _titleLabel.backgroundColor = [self _labelBackgroundColor ];
249273 _backgroundView.backgroundColor = self.tintColor ;
250274 [self setNeedsLayout ];
@@ -257,7 +281,7 @@ - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
257281}
258282
259283- (void )_updateTappedTintColorForTintColor TOROUNDEDBUTTON_OBJC_DIRECT {
260- if (TO_ROUNDED_BUTTON_FLOAT_IS_ZERO (_tappedTintColorBrightnessOffset)) {
284+ if (TORoundedButtonFloatIsZero (_tappedTintColorBrightnessOffset)) {
261285 return ;
262286 }
263287
@@ -272,7 +296,7 @@ - (void)_updateTappedTintColorForTintColor TOROUNDEDBUTTON_OBJC_DIRECT {
272296
273297- (UIColor *)_labelBackgroundColor TOROUNDEDBUTTON_OBJC_DIRECT {
274298 // Always return clear if tapped
275- if (_isTapped || _isTranslucent ) { return [UIColor clearColor ]; }
299+ if (_isTapped || TORoundedButtonIsDynamicBackground (_backgroundStyle) ) { return [UIColor clearColor ]; }
276300
277301 // Return clear if the tint color isn't opaque
278302 const BOOL isClear = CGColorGetAlpha (self.tintColor .CGColor ) < (1 .0f - FLT_EPSILON);
@@ -327,7 +351,7 @@ - (void)_didDragInside {
327351#pragma mark - Animation -
328352
329353- (void )_setBackgroundColorTappedAnimated : (BOOL )animated TOROUNDEDBUTTON_OBJC_DIRECT {
330- if (!_tappedTintColor || _isTranslucent ) { return ; }
354+ if (!_tappedTintColor || TORoundedButtonIsDynamicBackground (_backgroundStyle) ) { return ; }
331355
332356 // Toggle the background color of the title label
333357 void (^updateTitleOpacity)(void ) = ^{
@@ -489,7 +513,9 @@ - (void)setTextPointSize:(CGFloat)textPointSize {
489513- (void )setTintColor : (UIColor *)tintColor {
490514 [super setTintColor: tintColor];
491515 [self _updateTappedTintColorForTintColor ];
492- _backgroundView.backgroundColor = tintColor;
516+ if (!TORoundedButtonIsDynamicBackground (_backgroundStyle)) {
517+ _backgroundView.backgroundColor = tintColor;
518+ }
493519 _titleLabel.backgroundColor = [self _labelBackgroundColor ];
494520 [self setNeedsLayout ];
495521}
@@ -502,7 +528,7 @@ - (void)setTappedTintColor:(UIColor *)tappedTintColor {
502528}
503529
504530- (void )setTappedTintColorBrightnessOffset : (CGFloat)tappedTintColorBrightnessOffset {
505- if (TO_ROUNDED_BUTTON_FLOATS_MATCH (_tappedTintColorBrightnessOffset,
531+ if (TORoundedButtonFloatsMatch (_tappedTintColorBrightnessOffset,
506532 tappedTintColorBrightnessOffset)) { return ; }
507533
508534 _tappedTintColorBrightnessOffset = tappedTintColorBrightnessOffset;
@@ -520,29 +546,30 @@ - (void)setCornerRadius:(CGFloat)cornerRadius {
520546
521547 if (@available (iOS 26.0 , *)) {
522548 UICornerRadius *const radius = [UICornerRadius fixedRadius: _cornerRadius];
523- _backgroundView.cornerConfiguration = [UICornerConfiguration configurationWithUniformRadius: radius];
549+ _cornerConfiguration = [UICornerConfiguration configurationWithUniformRadius: radius];
550+ _backgroundView.cornerConfiguration = _cornerConfiguration;
524551 } else {
525552 _backgroundView.layer .cornerRadius = _cornerRadius;
553+ _backgroundView.layer .masksToBounds = TORoundedButtonIsDynamicBackground (_backgroundStyle);
526554 }
527555 [self setNeedsLayout ];
528556}
529557
530558- (void )setCornerConfiguration : (UICornerConfiguration *)cornerConfiguration {
531- _backgroundView.cornerConfiguration = cornerConfiguration;
559+ if (_cornerConfiguration == cornerConfiguration) { return ; }
560+ _cornerConfiguration = cornerConfiguration;
561+ _backgroundView.cornerConfiguration = _cornerConfiguration;
532562}
533563
534564- (UICornerConfiguration *)cornerConfiguration {
535- return _backgroundView. cornerConfiguration ;
565+ return _cornerConfiguration ;
536566}
537567
538- - (void )setIsTranslucent : (BOOL )isTranslucent {
539- if (_isTranslucent == isTranslucent) {
540- return ;
541- }
542-
543- _isTranslucent = isTranslucent;
568+ - (void )setBackgroundStyle : (TORoundedButtonBackgroundStyle)backgroundStyle {
569+ if (_backgroundStyle == backgroundStyle) { return ; }
570+ _backgroundStyle = backgroundStyle;
544571 [_backgroundView removeFromSuperview ];
545- _backgroundView = [self _makeBackgroundViewWithBlur: _isTranslucent ];
572+ _backgroundView = [self _makeBackgroundViewWithStyle: _backgroundStyle ];
546573 [_containerView insertSubview: _backgroundView atIndex: 0 ];
547574 _titleLabel.backgroundColor = [self _labelBackgroundColor ];
548575 [self setNeedsLayout ];
@@ -554,14 +581,30 @@ - (void)setBlurStyle:(UIBlurEffectStyle)blurStyle {
554581 }
555582
556583 _blurStyle = blurStyle;
557- if (!_isTranslucent || ![_backgroundView isKindOfClass: [UIVisualEffectView class ]]) {
584+ if (!TORoundedButtonIsDynamicBackground (_backgroundStyle) || ![_backgroundView isKindOfClass: [UIVisualEffectView class ]]) {
558585 return ;
559586 }
560587
561588 UIVisualEffectView *const blurView = (UIVisualEffectView *)_backgroundView;
562589 [blurView setEffect: [UIBlurEffect effectWithStyle: _blurStyle]];
563590}
564591
592+ - (void )setGlassStyle : (UIGlassEffectStyle)glassStyle {
593+ if (_glassStyle == glassStyle) { return ; }
594+ _glassStyle = glassStyle;
595+
596+ if (!TORoundedButtonIsDynamicBackground (_backgroundStyle) || ![_backgroundView isKindOfClass: [UIVisualEffectView class ]]) {
597+ return ;
598+ }
599+
600+ UIGlassEffect *const glassEffect = [UIGlassEffect effectWithStyle: _glassStyle];
601+ glassEffect.tintColor = self.tintColor ;
602+ glassEffect.interactive = YES ;
603+
604+ UIVisualEffectView *const effectView = (UIVisualEffectView *)_backgroundView;
605+ [effectView setEffect: glassEffect];
606+ }
607+
565608- (void )setEnabled : (BOOL )enabled {
566609 [super setEnabled: enabled];
567610 _containerView.alpha = enabled ? 1 : 0.4 ;
0 commit comments