Skip to content

Commit 4d98ee5

Browse files
Nobodymaterial-automation
Nobody
authored andcommitted
Resized the image to a square format with UIImageSymbolConfiguration
PiperOrigin-RevId: 720248847
1 parent ae3d2df commit 4d98ee5

File tree

4 files changed

+149
-30
lines changed

4 files changed

+149
-30
lines changed

components/BottomNavigation/src/MDCBottomNavigationBar.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ typedef NS_ENUM(NSInteger, MDCBottomNavigationBarAlignment) {
184184
*/
185185
@property(nonatomic, assign) BOOL showsSelectionIndicator;
186186

187+
/**
188+
If true, the tab icons will be placed in a square container for layout.
189+
*/
190+
@property(nonatomic, assign) BOOL enableSquareImages;
191+
187192
/**
188193
Size of the active indicator.
189194

components/BottomNavigation/src/MDCBottomNavigationBar.m

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,6 @@ - (void)setItems:(NSArray<UITabBarItem *> *)items {
913913
for (NSUInteger i = 0; i < items.count; i++) {
914914
MDCBottomNavigationItemView *itemView =
915915
[[MDCBottomNavigationItemView alloc] initWithFrame:CGRectZero];
916-
917916
itemView.rippleTouchController.delegate = self;
918917
itemView.selected = NO;
919918
itemView.displayTitleInVerticalLayout = self.displayItemTitlesInVerticalLayout;
@@ -1342,6 +1341,13 @@ - (void)configureTitleStateForItemView:(MDCBottomNavigationItemView *)itemView {
13421341
}
13431342
}
13441343

1344+
- (void)setEnableSquareImages:(BOOL)enableSquareImages {
1345+
_enableSquareImages = enableSquareImages;
1346+
for (MDCBottomNavigationItemView *itemView in self.itemViews) {
1347+
itemView.enableSquareImages = enableSquareImages;
1348+
}
1349+
}
1350+
13451351
@end
13461352

13471353
NS_ASSUME_NONNULL_END

components/BottomNavigation/src/private/MDCBottomNavigationItemView.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ __attribute__((objc_subclassing_restricted))
4545
@property(nonatomic, strong, nullable) UIImage *image;
4646
@property(nonatomic, strong, nullable) UIImage *selectedImage;
4747
@property(nonatomic, strong) UIImageView *iconImageView;
48+
@property(nonatomic, strong) UIView *iconContainerView;
4849

4950
@property(nonatomic, strong, nullable) UIColor *selectedItemTintColor;
5051
@property(nonatomic, strong, nullable) UIColor *unselectedItemTintColor;
@@ -63,6 +64,11 @@ __attribute__((objc_subclassing_restricted))
6364
/** The flag to enable displaying titles vertical layout mode. */
6465
@property(nonatomic) BOOL displayTitleInVerticalLayout;
6566

67+
/**
68+
If true, the tab icon will be placed in a square container for layout.
69+
*/
70+
@property(nonatomic, assign) BOOL enableSquareImages;
71+
6672
/**
6773
Returns a rect that is the union of all visible content views, inset by
6874
kMDCButtonNavigationItemViewPointerEffectHoverRectInset. This rect will never be larger than the

components/BottomNavigation/src/private/MDCBottomNavigationItemView.m

Lines changed: 131 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
#import <CoreGraphics/CoreGraphics.h>
16+
#import <UIKit/UIKit.h>
1617

1718
#import "MDCBottomNavigationItemView.h"
1819

@@ -28,7 +29,6 @@
2829
// CGFloat doesn't lose precision.
2930
static const CGFloat kMaxSizeDimension = 1000000;
3031
static const CGFloat MDCBottomNavigationItemViewRippleOpacity = (CGFloat)0.150;
31-
static const CGFloat MDCBottomNavigationItemViewTitleFontSize = 12;
3232

3333
// Selection indicator animation details.
3434
static const CGFloat kSelectionIndicatorTransformAnimationDuration = 0.17;
@@ -115,7 +115,7 @@ - (instancetype)initWithFrame:(CGRect)frame {
115115

116116
_label = [[UILabel alloc] initWithFrame:CGRectZero];
117117
_label.text = _title;
118-
_label.font = [UIFont systemFontOfSize:MDCBottomNavigationItemViewTitleFontSize];
118+
_label.font = [UIFont systemFontOfSize:12];
119119
_label.textAlignment = NSTextAlignmentCenter;
120120
_label.textColor = _selectedItemTitleColor;
121121
_label.lineBreakMode = NSLineBreakByTruncatingTail;
@@ -128,12 +128,11 @@ - (instancetype)initWithFrame:(CGRect)frame {
128128

129129
_badge = [[MDCBadgeView alloc] initWithFrame:CGRectZero];
130130
_badge.isAccessibilityElement = NO;
131-
132131
[_button addSubview:_iconImageView];
133132
[_button addSubview:_label];
134133
[_button addSubview:_badge];
135134
_badge.hidden = YES;
136-
135+
_iconContainerView = [[UIView alloc] initWithFrame:CGRectZero];
137136
_rippleTouchController = [[MDCRippleTouchController alloc] initWithView:self];
138137
_rippleTouchController.rippleView.rippleStyle = MDCRippleStyleUnbounded;
139138
}
@@ -170,7 +169,12 @@ - (CGSize)sizeThatFitsForVerticalLayout {
170169
}
171170

172171
CGSize maxSize = CGSizeMake(kMaxSizeDimension, kMaxSizeDimension);
173-
CGSize iconSize = [self.iconImageView sizeThatFits:maxSize];
172+
CGSize iconSize;
173+
if (_enableSquareImages) {
174+
iconSize = CGSizeMake(24, 24);
175+
} else {
176+
iconSize = [self.iconImageView sizeThatFits:maxSize];
177+
}
174178
CGRect iconFrame = CGRectMake(0, 0, iconSize.width, iconSize.height);
175179
CGSize badgeSize = [_badge sizeThatFits:maxSize];
176180
CGPoint badgeCenter = [self badgeCenterFromIconFrame:iconFrame isRTL:NO];
@@ -194,7 +198,12 @@ - (CGSize)sizeThatFitsForHorizontalLayout {
194198
}
195199

196200
CGSize maxSize = CGSizeMake(kMaxSizeDimension, kMaxSizeDimension);
197-
CGSize iconSize = [self.iconImageView sizeThatFits:maxSize];
201+
CGSize iconSize;
202+
if (_enableSquareImages) {
203+
iconSize = CGSizeMake(24, 24);
204+
} else {
205+
iconSize = [self.iconImageView sizeThatFits:maxSize];
206+
}
198207
CGRect iconFrame = CGRectMake(0, 0, iconSize.width, iconSize.height);
199208
CGSize badgeSize = [_badge sizeThatFits:maxSize];
200209
CGPoint badgeCenter = [self badgeCenterFromIconFrame:iconFrame isRTL:NO];
@@ -234,7 +243,12 @@ - (void)calculateVerticalLayoutInBounds:(CGRect)contentBounds
234243

235244
// Determine the intrinsic size of the label, icon, and combined content
236245
CGRect contentBoundingRect = CGRectStandardize(contentBounds);
237-
CGSize iconImageViewSize = [self.iconImageView sizeThatFits:contentBoundingRect.size];
246+
CGSize iconImageViewSize;
247+
if (_enableSquareImages) {
248+
iconImageViewSize = CGSizeMake(24, 24);
249+
} else {
250+
iconImageViewSize = [self.iconImageView sizeThatFits:contentBoundingRect.size];
251+
}
238252
CGSize labelSize = [self.label sizeThatFits:contentBoundingRect.size];
239253
CGFloat iconHeight = iconImageViewSize.height;
240254
CGFloat labelHeight = labelSize.height;
@@ -245,12 +259,11 @@ - (void)calculateVerticalLayoutInBounds:(CGRect)contentBounds
245259

246260
// Determine the position of the label and icon
247261
CGFloat centerX = CGRectGetMidX(contentBoundingRect);
248-
CGFloat iconImageViewCenterY =
249-
MAX(floor(CGRectGetMidY(contentBoundingRect) - totalContentHeight / 2 +
250-
iconHeight / 2), // Content centered
251-
floor(CGRectGetMinY(contentBoundingRect) +
252-
iconHeight / 2) // Pinned to top of bounding rect.
253-
);
262+
CGFloat iconImageViewCenterY = MAX(
263+
floor(CGRectGetMidY(contentBoundingRect) - totalContentHeight / 2 +
264+
iconHeight / 2), // Content centered
265+
floor(CGRectGetMinY(contentBoundingRect) + iconHeight / 2) // Pinned to top of bounding rect.
266+
);
254267
CGPoint iconImageViewCenter = CGPointMake(centerX, iconImageViewCenterY);
255268
// Ignore the horizontal titlePositionAdjustment in a vertical layout to match UITabBar behavior.
256269
CGFloat centerY;
@@ -290,7 +303,12 @@ - (void)calculateHorizontalLayoutInBounds:(CGRect)contentBounds
290303
}
291304
// Determine the intrinsic size of the label and icon
292305
CGRect contentBoundingRect = CGRectStandardize(contentBounds);
293-
CGSize iconImageViewSize = [self.iconImageView sizeThatFits:contentBoundingRect.size];
306+
CGSize iconImageViewSize;
307+
if (_enableSquareImages) {
308+
iconImageViewSize = CGSizeMake(24, 24);
309+
} else {
310+
iconImageViewSize = [self.iconImageView sizeThatFits:contentBoundingRect.size];
311+
}
294312
CGSize maxLabelSize = CGSizeMake(
295313
contentBoundingRect.size.width - self.contentHorizontalMargin - iconImageViewSize.width,
296314
contentBoundingRect.size.height);
@@ -373,13 +391,21 @@ - (void)centerLayoutAnimated:(BOOL)animated {
373391
if (animated) {
374392
[UIView animateWithDuration:kMDCBottomNavigationItemViewSelectionAnimationDuration
375393
animations:^(void) {
376-
self.iconImageView.center = iconImageViewCenter;
394+
if (_enableSquareImages) {
395+
self.iconContainerView.center = iconImageViewCenter;
396+
} else {
397+
self.iconImageView.center = iconImageViewCenter;
398+
}
377399
_badge.center =
378400
[self badgeCenterFromIconFrame:CGRectStandardize(iconImageViewFrame)
379401
isRTL:isRTL];
380402
}];
381403
} else {
382-
self.iconImageView.center = iconImageViewCenter;
404+
if (_enableSquareImages) {
405+
self.iconContainerView.center = iconImageViewCenter;
406+
} else {
407+
self.iconImageView.center = iconImageViewCenter;
408+
}
383409
_badge.center = [self badgeCenterFromIconFrame:CGRectStandardize(iconImageViewFrame)
384410
isRTL:isRTL];
385411
}
@@ -390,7 +416,12 @@ - (void)centerLayoutAnimated:(BOOL)animated {
390416
} else {
391417
self.label.textAlignment = NSTextAlignmentRight;
392418
}
393-
self.iconImageView.center = iconImageViewCenter;
419+
if (_enableSquareImages) {
420+
self.iconContainerView.center = iconImageViewCenter;
421+
} else {
422+
self.iconImageView.center = iconImageViewCenter;
423+
}
424+
self.iconContainerView.center = iconImageViewCenter;
394425
_badge.center = [self badgeCenterFromIconFrame:CGRectStandardize(iconImageViewFrame)
395426
isRTL:isRTL];
396427
}
@@ -621,9 +652,15 @@ - (void)setSelectedItemTitleColor:(nullable UIColor *)selectedItemTitleColor {
621652

622653
- (void)setImage:(nullable UIImage *)image {
623654
_image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
624-
625655
// _image updates unselected state
626656
// _image updates selected state IF there is no selectedImage
657+
if (image == nil) {
658+
self.iconContainerView.frame = CGRectZero;
659+
return;
660+
}
661+
if (_enableSquareImages) {
662+
[self setupIconContainerView];
663+
}
627664
if (!self.selected || (self.selected && !self.selectedImage)) {
628665
self.iconImageView.image = _image;
629666
self.iconImageView.tintColor =
@@ -737,7 +774,11 @@ - (void)setShowsSelectionIndicator:(BOOL)showsSelectionIndicator {
737774
_selectionIndicator.backgroundColor = _selectionIndicatorColor;
738775
_selectionIndicator.hidden = !_selected;
739776
[self commitSelectionIndicatorState];
740-
[self.button insertSubview:_selectionIndicator belowSubview:_iconImageView];
777+
if (_enableSquareImages) {
778+
[self.button insertSubview:_selectionIndicator belowSubview:_iconContainerView];
779+
} else {
780+
[self.button insertSubview:_selectionIndicator belowSubview:_iconImageView];
781+
}
741782
} else {
742783
[_selectionIndicator removeFromSuperview];
743784
_selectionIndicator = nil;
@@ -973,16 +1014,27 @@ - (CGPoint)iconPosition {
9731014
CGPoint midPoint = [self midPoint];
9741015
CGFloat indicatorMidX = CGRectGetMidX(_selectionIndicator.frame);
9751016

976-
CGFloat iconX = indicatorMidX - CGRectGetMidX(_iconImageView.bounds);
977-
CGFloat iconY = midPoint.y + (_selectionIndicatorSize.height * 0.5) -
978-
CGRectGetMidY(_iconImageView.bounds) + kIconVerticalOffset;
979-
1017+
CGFloat iconX;
1018+
CGFloat iconY;
1019+
if (_enableSquareImages) {
1020+
iconX = indicatorMidX - CGRectGetMidX(_iconContainerView.bounds);
1021+
iconY = midPoint.y + (_selectionIndicatorSize.height * 0.5) -
1022+
CGRectGetMidY(_iconContainerView.bounds) + kIconVerticalOffset;
1023+
} else {
1024+
iconX = indicatorMidX - CGRectGetMidX(_iconImageView.bounds);
1025+
iconY = midPoint.y + (_selectionIndicatorSize.height * 0.5) -
1026+
CGRectGetMidY(_iconImageView.bounds) + kIconVerticalOffset;
1027+
}
9801028
return CGPointMake(iconX, iconY);
9811029
}
9821030

9831031
- (CGSize)iconSize {
984-
CGSize maxSize = CGSizeMake(kMaxSizeDimension, kMaxSizeDimension);
985-
return [_iconImageView sizeThatFits:maxSize];
1032+
if (_enableSquareImages) {
1033+
return _iconContainerView.frame.size;
1034+
} else {
1035+
CGSize maxSize = CGSizeMake(kMaxSizeDimension, kMaxSizeDimension);
1036+
return [_iconImageView sizeThatFits:maxSize];
1037+
}
9861038
}
9871039

9881040
#pragma mark - Anchored Label
@@ -1045,7 +1097,11 @@ - (CGFloat)labelXForHorizontalLayoutWithRTLState:(BOOL)isRTL {
10451097

10461098
- (CGFloat)labelYForHorizontalLayout {
10471099
CGPoint midPoint = [self midPoint];
1048-
return midPoint.y + CGRectGetMidY(_iconImageView.bounds);
1100+
if (_enableSquareImages) {
1101+
return midPoint.y + CGRectGetMidY(_iconContainerView.bounds);
1102+
} else {
1103+
return midPoint.y + CGRectGetMidY(_iconImageView.bounds);
1104+
}
10491105
}
10501106

10511107
#pragma mark - Branched anchored layout methods
@@ -1105,7 +1161,11 @@ - (void)centerAnchoredLayoutVertical {
11051161
CGFloat iconY = iconPosition.y;
11061162
CGSize iconSize = [self iconSize];
11071163
CGRect iconFrame = (CGRectMake(iconX, iconY, iconSize.width, iconSize.height));
1108-
_iconImageView.frame = iconFrame;
1164+
if (_enableSquareImages) {
1165+
_iconContainerView.frame = iconFrame;
1166+
} else {
1167+
_iconImageView.frame = iconFrame;
1168+
}
11091169

11101170
CGSize labelSize = [self labelSize];
11111171
CGRect adjustedLabelBounds = CGRectMake(0, 0, labelSize.width, labelSize.height);
@@ -1139,8 +1199,11 @@ - (void)centerAnchoredLayoutHorizontal {
11391199
CGFloat iconY = iconPosition.y;
11401200
CGSize iconSize = [self iconSize];
11411201
CGRect iconFrame = CGRectIntegral(CGRectMake(iconX, iconY, iconSize.width, iconSize.height));
1142-
_iconImageView.frame = iconFrame;
1143-
1202+
if (_enableSquareImages) {
1203+
_iconContainerView.frame = iconFrame;
1204+
} else {
1205+
_iconImageView.frame = iconFrame;
1206+
}
11441207
CGFloat labelX = [self labelXForHorizontalLayoutWithRTLState:isRTL];
11451208
CGFloat labelY = [self labelYForHorizontalLayout];
11461209
if (self.enableVerticalLayout) {
@@ -1190,6 +1253,45 @@ - (BOOL)isTitleHiddenInAnchoredLayout {
11901253
_titleVisibility == MDCBottomNavigationBarTitleVisibilityNever);
11911254
}
11921255

1256+
- (void)setEnableSquareImages:(BOOL)enableSquareImages {
1257+
if (_enableSquareImages == enableSquareImages) {
1258+
return;
1259+
}
1260+
_enableSquareImages = enableSquareImages;
1261+
if (_enableSquareImages) {
1262+
[self setupIconContainerView];
1263+
} else {
1264+
_iconImageView.translatesAutoresizingMaskIntoConstraints = YES;
1265+
[_iconContainerView removeFromSuperview];
1266+
[_button addSubview:_iconImageView];
1267+
}
1268+
[_button bringSubviewToFront:_badge];
1269+
}
1270+
1271+
- (void)setupIconContainerView {
1272+
if (!_enableSquareImages) {
1273+
return;
1274+
}
1275+
_iconContainerView.userInteractionEnabled = NO;
1276+
[_iconContainerView addSubview:_iconImageView];
1277+
[_button addSubview:_iconContainerView];
1278+
_iconImageView.translatesAutoresizingMaskIntoConstraints = NO;
1279+
if (_image != nil) {
1280+
_iconContainerView.frame = CGRectMake(0, 0, 24, 24);
1281+
UIImageSymbolConfiguration *symbolConfiguration =
1282+
[UIImageSymbolConfiguration configurationWithPointSize:15.5
1283+
weight:UIImageSymbolWeightMedium];
1284+
_iconImageView.preferredSymbolConfiguration = symbolConfiguration;
1285+
[_iconImageView sizeToFit];
1286+
[NSLayoutConstraint activateConstraints:@[
1287+
[_iconImageView.centerXAnchor constraintEqualToAnchor:_iconContainerView.centerXAnchor],
1288+
[_iconImageView.centerYAnchor constraintEqualToAnchor:_iconContainerView.centerYAnchor],
1289+
]];
1290+
} else {
1291+
self.iconContainerView.frame = CGRectZero;
1292+
}
1293+
}
1294+
11931295
@end
11941296

11951297
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)