From 8eb898db4d5f67dbefd35527fbdd52da303bf434 Mon Sep 17 00:00:00 2001 From: Frank Wang Date: Fri, 1 Nov 2024 11:53:20 -0700 Subject: [PATCH] [MDCBadge] Allow MDCBadgeView to take on a dot badge style. PiperOrigin-RevId: 692247823 --- .../src/Appearance/MDCBadgeAppearance.h | 20 ++++++++ .../src/Appearance/MDCBadgeAppearance.m | 6 +++ components/Badges/src/MDCBadgeView.m | 50 ++++++++++++++++++- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/components/Badges/src/Appearance/MDCBadgeAppearance.h b/components/Badges/src/Appearance/MDCBadgeAppearance.h index 95749162307..e09e6085cba 100644 --- a/components/Badges/src/Appearance/MDCBadgeAppearance.h +++ b/components/Badges/src/Appearance/MDCBadgeAppearance.h @@ -16,6 +16,8 @@ #import "MDCMinimumOS.h" // IWYU pragma: keep +NS_ASSUME_NONNULL_BEGIN + /** An object for customizing the appearance of a badge. */ __attribute__((objc_subclassing_restricted)) @interface MDCBadgeAppearance : NSObject @@ -38,6 +40,22 @@ __attribute__((objc_subclassing_restricted)) /** The font that will be used to display the value. */ @property(nonatomic, strong, nullable) UIFont *font; +/** + The radius of the filled portion of the dot badge. Only used if `dotBadgeEnabled` is YES. + + The width and height of the dot badge's frame will be equal to + (dotBadgeInnerRadius + borderWidth) * 2. + */ +@property(nonatomic) CGFloat dotBadgeInnerRadius; + +/** + Whether the badge should be a dot badge. + + Dot badges do not display any text. Enabling this flag will cause the badge to ignore any text + set on the badge. + */ +@property(nonatomic) BOOL dotBadgeEnabled; + /** The color of the border surrounding the badge. @@ -55,3 +73,5 @@ __attribute__((objc_subclassing_restricted)) @property(nonatomic) CGFloat borderWidth; @end + +NS_ASSUME_NONNULL_END diff --git a/components/Badges/src/Appearance/MDCBadgeAppearance.m b/components/Badges/src/Appearance/MDCBadgeAppearance.m index 2e9d127966b..2fd12634961 100644 --- a/components/Badges/src/Appearance/MDCBadgeAppearance.m +++ b/components/Badges/src/Appearance/MDCBadgeAppearance.m @@ -1,5 +1,7 @@ #import "MDCBadgeAppearance.h" +NS_ASSUME_NONNULL_BEGIN + @implementation MDCBadgeAppearance - (nonnull instancetype)init { @@ -10,6 +12,8 @@ - (nonnull instancetype)init { - (nonnull id)copyWithZone:(nullable __unused NSZone *)zone { MDCBadgeAppearance *config = [[MDCBadgeAppearance alloc] init]; + config.dotBadgeEnabled = self.dotBadgeEnabled; + config.dotBadgeInnerRadius = self.dotBadgeInnerRadius; config.backgroundColor = self.backgroundColor; config.textColor = self.textColor; config.font = self.font; @@ -19,3 +23,5 @@ - (nonnull id)copyWithZone:(nullable __unused NSZone *)zone { } @end + +NS_ASSUME_NONNULL_END diff --git a/components/Badges/src/MDCBadgeView.m b/components/Badges/src/MDCBadgeView.m index 31593c552ba..760efc06f21 100644 --- a/components/Badges/src/MDCBadgeView.m +++ b/components/Badges/src/MDCBadgeView.m @@ -20,6 +20,8 @@ // TODO(featherless): Remove the dependency on MDCPalette. #import "MDCPalettes.h" +NS_ASSUME_NONNULL_BEGIN + static const CGFloat kBadgeFontSize = 8; static const CGFloat kBadgeYPadding = 2; static const CGFloat kMinDiameter = 9; @@ -47,6 +49,8 @@ @implementation MDCBadgeView { UIColor *_Nullable _borderColor; UILabel *_Nonnull _label; + CGFloat _dotBadgeInnerRadius; + BOOL _dotBadgeEnabled; } @synthesize appearance = _appearance; @@ -63,6 +67,7 @@ - (nonnull instancetype)initWithFrame:(CGRect)frame { _label = [[UILabel alloc] initWithFrame:self.bounds]; _label.textAlignment = NSTextAlignmentCenter; _label.isAccessibilityElement = NO; + _label.hidden = _appearance.dotBadgeEnabled; [self addSubview:_label]; } return self; @@ -80,6 +85,18 @@ - (CGFloat)badgeXPaddingForRadius:(CGFloat)radius { - (void)layoutSubviews { [super layoutSubviews]; + if (_appearance.dotBadgeEnabled) { + [self layoutDotBadge]; + } else { + [self layoutTextBadge]; + } +} + +- (void)layoutDotBadge { + self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2; +} + +- (void)layoutTextBadge { CGFloat badgeRadius = CGRectGetHeight(self.bounds) / 2; CGRect availableContentRect = CGRectStandardize( CGRectInset(self.bounds, [self badgeXPaddingForRadius:badgeRadius], kBadgeYPadding)); @@ -91,6 +108,19 @@ - (void)layoutSubviews { } - (CGSize)sizeThatFits:(CGSize)size { + if (_appearance.dotBadgeEnabled) { + return [self sizeThatFitsDotBadge:size]; + } else { + return [self sizeThatFitsTextBadge:size]; + } +} + +- (CGSize)sizeThatFitsDotBadge:(CGSize)size { + const CGFloat squareDimension = (_dotBadgeInnerRadius + self.layer.borderWidth) * 2; + return CGSizeMake(squareDimension, squareDimension); +} + +- (CGSize)sizeThatFitsTextBadge:(CGSize)size { if (_label.text == nil) { return CGSizeZero; } @@ -128,8 +158,10 @@ - (void)didMoveToWindow { - (void)traitCollectionDidChange:(nullable UITraitCollection *)previousTraitCollection { [super traitCollectionDidChange:previousTraitCollection]; - [self updateFont]; - [self updateBorderColor]; + if (!_appearance.dotBadgeEnabled) { + [self updateFont]; + [self updateBorderColor]; + } } - (void)updateFont { @@ -152,6 +184,18 @@ - (void)applyAppearance { _label.textColor = _appearance.textColor; if (![_label.font isEqual:_appearance.font]) { [self updateFont]; + intrinsicSizeAffected = !_appearance.dotBadgeEnabled; + } + + // Layout + if (_dotBadgeInnerRadius != _appearance.dotBadgeInnerRadius) { + _dotBadgeInnerRadius = _appearance.dotBadgeInnerRadius; + intrinsicSizeAffected = intrinsicSizeAffected || _appearance.dotBadgeEnabled; + } + + if (_dotBadgeEnabled != _appearance.dotBadgeEnabled) { + _dotBadgeEnabled = _appearance.dotBadgeEnabled; + _label.hidden = _appearance.dotBadgeEnabled; intrinsicSizeAffected = YES; } @@ -203,3 +247,5 @@ - (nonnull MDCBadgeAppearance *)appearance { } @end + +NS_ASSUME_NONNULL_END