diff --git a/CHANGELOG.md b/CHANGELOG.md
index 474947603ff..5833a6427e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,79 @@
+# 109.8.0
+
+In this minor release we added indeterminate mode to `MDCProgressView`. As well as various bug fixes to ActionSheet, Banner, Dialog, and TextFields components.
+
+## New features
+
+`MDCProgressView` now supports indeterminate mode.
+
+```objc
+MDCProgressView *progressView = [[MDCProgressView alloc] init];
+progressView.mode = MDCProgressViewModeIndeterminate;
+[progressView startAnimating];
+```
+
+## API changes
+
+### Buttons
+
+*new* property: `defaultContentEdgeInsets` in `MDCButton`
+
+### ProgressView
+
+*new* enum: `MDCProgressViewMode`
+
+*new* property: `mode` in `MDCProgressView`
+
+*new* property: `animating` in `MDCProgressView`
+
+*new* method: `setMode:animated:completion:` in `MDCProgressView`
+
+*new* method: `startAnimating` in `MDCProgressView`
+
+*new* method: `stopAnimating` in `MDCProgressView`
+
+## Component changes
+
+### ActionSheet
+
+* [Mark title as accessibility `.header`](https://github.com/material-components/material-components-ios/commit/386ba003220db035244c9d66cfe1468e1110ee37) (Rob Moore)
+
+### Banner
+
+* [Support two buttons on a single row style banner.](https://github.com/material-components/material-components-ios/commit/c13125fb26e64aea98392855ec3bfcda2a89ff36) (Wenyu Zhang)
+
+### Buttons
+
+* [Expose `defaultContentEdgeInsets` readonly property on MDCButton in regular header for the class, rather than only in the subclassing header, as clients may need to reset content insets back to their defaults after using a themer, even if they do not subclass MDCButton.](https://github.com/material-components/material-components-ios/commit/df6658771a83f2e8d3637f7ff0549437fb9c1d5f) (Jake Rockland)
+
+### Dialogs
+
+* [Voice over reads the message twice](https://github.com/material-components/material-components-ios/commit/8b4e6058db8a5235ef0ca145427795850c6b676f) (Nobody)
+
+### ProgressView
+
+* [Add and indeterminate state to the progress bar.](https://github.com/material-components/material-components-ios/commit/1a75f9dbbb5e5297834c1deb2c604d63175f4ed7) (Nobody)
+
+### Tabs
+
+* [Add explicit cast in MDCTabBarViewTests.](https://github.com/material-components/material-components-ios/commit/7c4dab62cf371ee9f89a9bff26c0d2ba1a0eb015) (Wenyu Zhang)
+* [Internal change](https://github.com/material-components/material-components-ios/commit/a66e645d94f6d59e5ba8f97e75447ee57b3b7144) (Jeff Verkoeyen)
+
+### TextFields
+
+* [Return the empty string instead of nil in MDCTextField's accessibilityValue method to avoid VoiceOver defaulting to [super accessibilityValue].](https://github.com/material-components/material-components-ios/commit/d4880b46008f1636b39a50f939b746811994e37e) (Bryan Oltman)
+
+### private/TextControlsPrivate
+
+* [Rename + small refactor of horizontal positioning stuff](https://github.com/material-components/material-components-ios/commit/765676cca8d7705fe56c1d796c920b96fd45f5d0) (Andrew Overton)
+
+## Multi-component changes
+
+* [Add a convenience math method to calculate the correct visibleAreaInsets given a frame and a minimum touch target.](https://github.com/material-components/material-components-ios/commit/6bd4757079daa8a85828303d4826d46cb6e05583) (Yarden Eitan)
+* [Change side view positioning for MDCUnderlinedTextField](https://github.com/material-components/material-components-ios/commit/c302339943de305772db2809fe864a53cbe5ab0f) (Andrew Overton)
+
+---
+
# 109.7.0
This minor release makes accessibility improvements related to high contrast colors, adds new functionality to Buttons, Chips, Dialogs, Snackbars, and Tabs, and adds a new TextControls text field, `MDCUnderlinedTextField`, which is intended to replace `MDCTextInputControllerLegacyDefault` and `MDCTextInputControllerUnderline`.
diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec
index c14bbaa8576..2f0f71c53b6 100644
--- a/MaterialComponents.podspec
+++ b/MaterialComponents.podspec
@@ -2,7 +2,7 @@ load 'scripts/generated/icons.rb'
Pod::Spec.new do |mdc|
mdc.name = "MaterialComponents"
- mdc.version = "109.7.0"
+ mdc.version = "109.8.0"
mdc.authors = "The Material Components authors."
mdc.summary = "A collection of stand-alone production-ready UI libraries focused on design details."
mdc.homepage = "https://github.com/material-components/material-components-ios"
diff --git a/MaterialComponentsBeta.podspec b/MaterialComponentsBeta.podspec
index 36aa8b318bf..b3186aeb6dd 100644
--- a/MaterialComponentsBeta.podspec
+++ b/MaterialComponentsBeta.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |mdc|
mdc.name = "MaterialComponentsBeta"
- mdc.version = "109.7.0"
+ mdc.version = "109.8.0"
mdc.authors = "The Material Components authors."
mdc.summary = "A collection of stand-alone alpha UI libraries that are not yet guaranteed to be ready for general production use. Use with caution."
mdc.homepage = "https://github.com/material-components/material-components-ios"
diff --git a/MaterialComponentsEarlGreyTests.podspec b/MaterialComponentsEarlGreyTests.podspec
index 35498dd62eb..111dbece18e 100644
--- a/MaterialComponentsEarlGreyTests.podspec
+++ b/MaterialComponentsEarlGreyTests.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "MaterialComponentsEarlGreyTests"
- s.version = "109.7.0"
+ s.version = "109.8.0"
s.authors = "The Material Components authors."
s.summary = "This spec is an aggregate of all the Material Components EarlGrey tests."
s.description = "This spec is made for use in the MDC Catalog."
diff --git a/MaterialComponentsExamples.podspec b/MaterialComponentsExamples.podspec
index 8ead2eadbca..cfdeacbf5e1 100644
--- a/MaterialComponentsExamples.podspec
+++ b/MaterialComponentsExamples.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "MaterialComponentsExamples"
- s.version = "109.7.0"
+ s.version = "109.8.0"
s.authors = "The Material Components authors."
s.summary = "This spec is an aggregate of all the Material Components examples."
s.description = "This spec is made for use in the MDC Catalog. Used in conjunction with CatalogByConvention we create our Material Catalog."
diff --git a/MaterialComponentsSnapshotTests.podspec b/MaterialComponentsSnapshotTests.podspec
index a80c37a60b9..428f87cf92d 100644
--- a/MaterialComponentsSnapshotTests.podspec
+++ b/MaterialComponentsSnapshotTests.podspec
@@ -53,7 +53,7 @@ end
Pod::Spec.new do |s|
s.name = "MaterialComponentsSnapshotTests"
- s.version = "109.7.0"
+ s.version = "109.8.0"
s.authors = "The Material Components authors."
s.summary = "This spec is an aggregate of all the Material Components snapshot tests."
s.homepage = "https://github.com/material-components/material-components-ios"
diff --git a/VERSION b/VERSION
index 12d7b15d921..ff07bce069d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-109.7.0
+109.8.0
diff --git a/catalog/MDCCatalog/Info.plist b/catalog/MDCCatalog/Info.plist
index fb5557d8586..f16dc91450e 100644
--- a/catalog/MDCCatalog/Info.plist
+++ b/catalog/MDCCatalog/Info.plist
@@ -15,11 +15,11 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 109.7.0
+ 109.8.0
CFBundleSignature
????
CFBundleVersion
- 109.7.0
+ 109.8.0
LSRequiresIPhoneOS
UIAppFonts
diff --git a/catalog/MDCDragons/Info.plist b/catalog/MDCDragons/Info.plist
index 28c799ee578..9aa9c80df20 100644
--- a/catalog/MDCDragons/Info.plist
+++ b/catalog/MDCDragons/Info.plist
@@ -15,9 +15,9 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 109.7.0
+ 109.8.0
CFBundleVersion
- 109.7.0
+ 109.8.0
LSRequiresIPhoneOS
UILaunchStoryboardName
diff --git a/catalog/MaterialCatalog/MaterialCatalog.podspec b/catalog/MaterialCatalog/MaterialCatalog.podspec
index 27b68f9f2cb..4d5977d85f2 100644
--- a/catalog/MaterialCatalog/MaterialCatalog.podspec
+++ b/catalog/MaterialCatalog/MaterialCatalog.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "MaterialCatalog"
- s.version = "109.7.0"
+ s.version = "109.8.0"
s.summary = "Helper Objective-C classes for the MDC catalog."
s.description = "This spec is made for use in the MDC Catalog."
s.homepage = "https://github.com/material-components/material-components-ios"
diff --git a/components/ActionSheet/src/private/MDCActionSheetHeaderView.m b/components/ActionSheet/src/private/MDCActionSheetHeaderView.m
index 75e8a9dfbf7..f812a1f7d2b 100644
--- a/components/ActionSheet/src/private/MDCActionSheetHeaderView.m
+++ b/components/ActionSheet/src/private/MDCActionSheetHeaderView.m
@@ -47,6 +47,7 @@ - (instancetype)initWithFrame:(CGRect)frame {
_titleLabel.font = [UIFont mdc_standardFontForMaterialTextStyle:MDCFontTextStyleSubheadline];
_titleLabel.numberOfLines = 0;
_titleLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
+ _titleLabel.accessibilityTraits |= UIAccessibilityTraitHeader;
[self addSubview:_messageLabel];
_messageLabel.font = [UIFont mdc_standardFontForMaterialTextStyle:MDCFontTextStyleBody1];
diff --git a/components/Banner/examples/BannerTypicalUseExampleViewController.m b/components/Banner/examples/BannerTypicalUseExampleViewController.m
index 2c7a87a3734..f6b885f275e 100644
--- a/components/Banner/examples/BannerTypicalUseExampleViewController.m
+++ b/components/Banner/examples/BannerTypicalUseExampleViewController.m
@@ -389,6 +389,7 @@ - (void)showSingleLineLongTextStyleBanner {
MDCButton *button = bannerView.leadingButton;
[button setTitle:@"Dismiss" forState:UIControlStateNormal];
+ bannerView.trailingButton.hidden = YES;
bannerView.imageView.hidden = YES;
bannerView.showsDivider = YES;
diff --git a/components/Banner/src/MDCBannerView.h b/components/Banner/src/MDCBannerView.h
index 532c783cc5b..6bf72d377e3 100644
--- a/components/Banner/src/MDCBannerView.h
+++ b/components/Banner/src/MDCBannerView.h
@@ -23,8 +23,7 @@ typedef NS_ENUM(NSInteger, MDCBannerViewLayoutStyle) {
MDCBannerViewLayoutStyleAutomatic, // Layout is set automatically based on how elements are
// configured on banner view. One of three other layouts will
// be used internally.
- MDCBannerViewLayoutStyleSingleRow, // All elements on the same row, only supports one button.
- // trailingButton is hidden under this layout style.
+ MDCBannerViewLayoutStyleSingleRow, // All elements on the same row.
MDCBannerViewLayoutStyleMultiRowStackedButton, // Multilple rows with stacked button layout
MDCBannerViewLayoutStyleMultiRowAlignedButton, // Multiple rows with aligned buttons horizontally
};
diff --git a/components/Banner/src/MDCBannerView.m b/components/Banner/src/MDCBannerView.m
index 83255b01a83..389d44c72be 100644
--- a/components/Banner/src/MDCBannerView.m
+++ b/components/Banner/src/MDCBannerView.m
@@ -180,14 +180,6 @@ - (void)commonBannerViewInit {
_mdc_overrideBaseElevation = -1;
}
-- (void)setBannerViewLayoutStyle:(MDCBannerViewLayoutStyle)bannerViewLayoutStyle {
- _bannerViewLayoutStyle = bannerViewLayoutStyle;
- if (bannerViewLayoutStyle == MDCBannerViewLayoutStyleSingleRow) {
- // Only leadingButton is supported in MDCBannerViewLayoutStyleSingleRow.
- self.trailingButton.hidden = YES;
- }
-}
-
- (void)setDividerColor:(UIColor *)dividerColor {
self.divider.backgroundColor = dividerColor;
}
@@ -442,7 +434,9 @@ - (void)updateConstraintsWithLayoutStyle:(MDCBannerViewLayoutStyle)layoutStyle {
if (layoutStyle == MDCBannerViewLayoutStyleSingleRow) {
self.imageViewConstraintCenterY.active = YES;
self.textViewConstraintCenterY.active = YES;
- self.buttonContainerConstraintWidthWithLeadingButton.active = YES;
+ if (self.trailingButton.hidden) {
+ self.buttonContainerConstraintWidthWithLeadingButton.active = YES;
+ }
self.buttonContainerConstraintTopWithMargin.active = YES;
if (self.leadingButton.hidden) {
self.textViewConstraintTrailing.active = YES;
diff --git a/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m b/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m
index 31b0efa9abf..51a65f398e4 100644
--- a/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m
+++ b/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m
@@ -330,6 +330,7 @@ - (void)testSingleRowStyleLongTextWithSingleActionLTR {
[button1 setTitle:@"Action1" forState:UIControlStateNormal];
[button1 setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
button1.uppercaseTitle = YES;
+ self.bannerView.trailingButton.hidden = YES;
self.bannerView.bannerViewLayoutStyle = MDCBannerViewLayoutStyleSingleRow;
self.bannerView.imageView.hidden = YES;
@@ -344,6 +345,7 @@ - (void)testSingleRowStyleLongTextWithSingleActionRTLInArabic {
[button1 setTitle:@"Action1" forState:UIControlStateNormal];
[button1 setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
button1.uppercaseTitle = YES;
+ self.bannerView.trailingButton.hidden = YES;
self.bannerView.bannerViewLayoutStyle = MDCBannerViewLayoutStyleSingleRow;
[self changeViewToRTL:self.bannerView];
self.bannerView.imageView.hidden = YES;
@@ -352,6 +354,43 @@ - (void)testSingleRowStyleLongTextWithSingleActionRTLInArabic {
[self generateSnapshotAndVerifyForView:self.bannerView];
}
+- (void)testSingleRowStyleShortTextWithTwoActionsLTR {
+ // When
+ self.bannerView.textView.text = kBannerShortText;
+ MDCButton *button1 = self.bannerView.leadingButton;
+ [button1 setTitle:@"Action1" forState:UIControlStateNormal];
+ [button1 setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
+ button1.uppercaseTitle = YES;
+ MDCButton *button2 = self.bannerView.trailingButton;
+ [button2 setTitle:@"Action2" forState:UIControlStateNormal];
+ [button2 setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
+ button2.uppercaseTitle = YES;
+ self.bannerView.bannerViewLayoutStyle = MDCBannerViewLayoutStyleSingleRow;
+ self.bannerView.imageView.hidden = YES;
+
+ // Then
+ [self generateSnapshotAndVerifyForView:self.bannerView];
+}
+
+- (void)testSingleRowStyleShortTextWithTwoActionsRTL {
+ // When
+ self.bannerView.textView.text = kBannerShortText;
+ MDCButton *button1 = self.bannerView.leadingButton;
+ [button1 setTitle:@"Action1" forState:UIControlStateNormal];
+ [button1 setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
+ button1.uppercaseTitle = YES;
+ MDCButton *button2 = self.bannerView.trailingButton;
+ [button2 setTitle:@"Action2" forState:UIControlStateNormal];
+ [button2 setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
+ button2.uppercaseTitle = YES;
+ self.bannerView.bannerViewLayoutStyle = MDCBannerViewLayoutStyleSingleRow;
+ self.bannerView.imageView.hidden = YES;
+ [self changeViewToRTL:self.bannerView];
+
+ // Then
+ [self generateSnapshotAndVerifyForView:self.bannerView];
+}
+
- (void)testLongTextWithTwoActionsAndIconLTR {
// When
self.bannerView.textView.text = kBannerLongText;
diff --git a/components/Buttons/examples/ButtonsTypicalUseExample.m b/components/Buttons/examples/ButtonsTypicalUseExample.m
index 6898ccfd69b..f449f28fff5 100644
--- a/components/Buttons/examples/ButtonsTypicalUseExample.m
+++ b/components/Buttons/examples/ButtonsTypicalUseExample.m
@@ -14,8 +14,9 @@
#import "MaterialButtons.h"
#import "MaterialButtons+Theming.h"
-#import "MaterialContainerScheme.h"
#import "MaterialTypography.h"
+#import "MaterialMath.h"
+#import "MaterialContainerScheme.h"
#import "supplemental/ButtonsTypicalUseSupplemental.h"
@@ -60,14 +61,9 @@ - (void)viewDidLoad {
MDCButton *containedButton = [[MDCButton alloc] init];
[containedButton setTitle:@"Button" forState:UIControlStateNormal];
[containedButton applyContainedThemeWithScheme:self.containerScheme];
- CGSize containedButtonSize = [containedButton intrinsicContentSize];
- CGFloat containedButtonVerticalInset =
- MAX(0, (kMinimumAccessibleButtonSize.height - containedButtonSize.height) / 2);
- CGFloat containedButtonHorizontalInset =
- MAX(0, (kMinimumAccessibleButtonSize.width - containedButtonSize.width) / 2);
- containedButton.visibleAreaInsets =
- UIEdgeInsetsMake(containedButtonVerticalInset, containedButtonHorizontalInset,
- containedButtonVerticalInset, containedButtonHorizontalInset);
+ [containedButton sizeToFit];
+ containedButton.visibleAreaInsets = MDCVisibleAreaInsetsForMinimumTappability(
+ containedButton.frame, kMinimumAccessibleButtonSize);
[containedButton sizeToFit];
[containedButton addTarget:self
action:@selector(didTap:)
@@ -93,14 +89,9 @@ - (void)viewDidLoad {
MDCButton *textButton = [[MDCButton alloc] init];
[textButton applyTextThemeWithScheme:self.containerScheme];
[textButton setTitle:@"Button" forState:UIControlStateNormal];
- CGSize textButtonSize = [textButton intrinsicContentSize];
- CGFloat textButtonVerticalInset =
- MAX(0, (kMinimumAccessibleButtonSize.height - textButtonSize.height) / 2);
- CGFloat textButtonHorizontalInset =
- MAX(0, (kMinimumAccessibleButtonSize.width - textButtonSize.width) / 2);
+ [textButton sizeToFit];
textButton.visibleAreaInsets =
- UIEdgeInsetsMake(textButtonVerticalInset, textButtonHorizontalInset, textButtonVerticalInset,
- textButtonHorizontalInset);
+ MDCVisibleAreaInsetsForMinimumTappability(textButton.frame, kMinimumAccessibleButtonSize);
[textButton sizeToFit];
[textButton addTarget:self
action:@selector(didTap:)
diff --git a/components/Buttons/src/MDCButton.h b/components/Buttons/src/MDCButton.h
index 8d6490d07b5..13b6741ae76 100644
--- a/components/Buttons/src/MDCButton.h
+++ b/components/Buttons/src/MDCButton.h
@@ -90,6 +90,11 @@
*/
@property(nonatomic, assign) UIEdgeInsets visibleAreaInsets;
+/**
+ The default content edge insets of the button. They are set at initialization time.
+ */
+@property(nonatomic, readonly) UIEdgeInsets defaultContentEdgeInsets;
+
/**
The offset (in points) of the button's inkView or rippleView (depending on which is being used -
see @c enableRippleBehavior)
diff --git a/components/Buttons/src/private/MDCButton+Subclassing.h b/components/Buttons/src/private/MDCButton+Subclassing.h
index 7bfbfe600b8..c96af5e6230 100644
--- a/components/Buttons/src/private/MDCButton+Subclassing.h
+++ b/components/Buttons/src/private/MDCButton+Subclassing.h
@@ -37,7 +37,4 @@
/** The bounding path of the button. The shadow will follow that path. */
- (nonnull UIBezierPath *)boundingPath;
-/** The default content edge insets of the button. They are set at initialization time. */
-- (UIEdgeInsets)defaultContentEdgeInsets;
-
@end
diff --git a/components/Dialogs/src/MDCAlertController.m b/components/Dialogs/src/MDCAlertController.m
index 2be9f330aec..5fa85c60c19 100644
--- a/components/Dialogs/src/MDCAlertController.m
+++ b/components/Dialogs/src/MDCAlertController.m
@@ -775,6 +775,11 @@ - (void)setupAlertView {
self.alertView.titleLabel.accessibilityLabel = self.titleAccessibilityLabel ?: self.title;
self.alertView.messageTextView.accessibilityLabel =
self.messageAccessibilityLabel ?: self.message ?: self.attributedMessage.string;
+ // Set messageTextView's accessibilityValue to the empty string to resolve b/158732017.
+ // MessageTextView acts as a label and should not have an accessibilityValue.
+ // Setting the accessibilityValue to nil causes VoiceOver to use the default value, which is the
+ // text of the message, so the value must be set to the empty string instead.
+ self.alertView.messageTextView.accessibilityValue = @"";
self.alertView.messageTextView.delegate = self;
self.alertView.titleIconImageView.accessibilityLabel = self.imageAccessibilityLabel;
diff --git a/components/LibraryInfo/src/MDCLibraryInfo.m b/components/LibraryInfo/src/MDCLibraryInfo.m
index 4698e86030e..74fd059db49 100644
--- a/components/LibraryInfo/src/MDCLibraryInfo.m
+++ b/components/LibraryInfo/src/MDCLibraryInfo.m
@@ -19,7 +19,7 @@
// This string is updated automatically as a part of the release process and should not be edited
// manually. Do not rename this constant or change the formatting without updating the release
// scripts.
-static NSString const *MDCLibraryInfoVersionString = @"109.7.0";
+static NSString const *MDCLibraryInfoVersionString = @"109.8.0";
@implementation MDCLibraryInfo
diff --git a/components/LibraryInfo/tests/unit/LibraryInfoTests.m b/components/LibraryInfo/tests/unit/LibraryInfoTests.m
index 880c24e8a67..7cea91dc7b8 100644
--- a/components/LibraryInfo/tests/unit/LibraryInfoTests.m
+++ b/components/LibraryInfo/tests/unit/LibraryInfoTests.m
@@ -26,7 +26,7 @@ - (void)testVersionFormat {
// Given
// This regex pattern does the following:
- // Accept: "109.7.0", etc.
+ // Accept: "109.8.0", etc.
// Reject: "0.0.0", "1.2", "1", "-1.2.3", "Hi, I'm a version 1.2.3", "1.2.3 is my version", etc.
//
// Note the major version must be >= 1 since "0.0.0" is used as the version when something goes
diff --git a/components/ProgressView/examples/ProgressViewExample.m b/components/ProgressView/examples/ProgressViewExample.m
index 5b3854e12fb..3a09a9cddd5 100644
--- a/components/ProgressView/examples/ProgressViewExample.m
+++ b/components/ProgressView/examples/ProgressViewExample.m
@@ -43,6 +43,9 @@ @interface ProgressViewExample : UIViewController
@property(nonatomic, strong) MDCProgressView *backwardProgressAnimateView;
@property(nonatomic, strong) UILabel *backwardProgressAnimateLabel;
+@property(nonatomic, strong) MDCProgressView *indeterminateProgressView;
+@property(nonatomic, strong) UILabel *indeterminateProgressLabel;
+
@property(nonatomic, strong) MDCSemanticColorScheme *colorScheme;
@property(nonatomic, strong) MDCTypographyScheme *typographyScheme;
@@ -97,6 +100,14 @@ - (void)setupProgressViews {
[self.container addSubview:_backwardProgressAnimateView];
// Have a non-zero progress at setup time.
_backwardProgressAnimateView.progress = (float)0.33;
+
+ _indeterminateProgressView = [[MDCProgressView alloc] init];
+ _indeterminateProgressView.translatesAutoresizingMaskIntoConstraints = NO;
+ _indeterminateProgressView.mode = MDCProgressViewModeIndeterminate;
+ _indeterminateProgressView.progressTintColor = self.colorScheme.primaryColor;
+ _indeterminateProgressView.trackTintColor =
+ [self.colorScheme.primaryColor colorWithAlphaComponent:(CGFloat)0.24];
+ [self.container addSubview:_indeterminateProgressView];
}
@end
@@ -197,6 +208,13 @@ - (void)setupLabels {
_backwardProgressAnimateLabel.textColor = self.colorScheme.onBackgroundColor;
_backwardProgressAnimateLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.container addSubview:_backwardProgressAnimateLabel];
+
+ _indeterminateProgressLabel = [[UILabel alloc] init];
+ _indeterminateProgressLabel.text = @"Indeterminate progress";
+ _indeterminateProgressLabel.font = self.typographyScheme.caption;
+ _indeterminateProgressLabel.textColor = self.colorScheme.onBackgroundColor;
+ _indeterminateProgressLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.container addSubview:_indeterminateProgressLabel];
}
- (void)setupConstraints {
@@ -214,6 +232,8 @@ - (void)setupConstraints {
@"backwardResetLabel" : _backwardProgressResetLabel,
@"backwardAnimateView" : _backwardProgressAnimateView,
@"backwardAnimateLabel" : _backwardProgressAnimateLabel,
+ @"indeterminateView" : _indeterminateProgressView,
+ @"indeterminateLabel" : _indeterminateProgressLabel,
};
NSDictionary *metrics = @{
@"t" : @20,
@@ -229,7 +249,8 @@ - (void)setupConstraints {
"[coloredView(==h)]-(p)-[coloredLabel]-(s)-"
"[gradientView(==h)]-(p)-[gradientLabel]-(s)-"
"[backwardResetView(==h)]-(p)-[backwardResetLabel]-(s)-"
- "[backwardAnimateView(==h)]-(p)-[backwardAnimateLabel]"
+ "[backwardAnimateView(==h)]-(p)-[backwardAnimateLabel]-(s)-"
+ "[indeterminateView(==h)]-(p)-[indeterminateLabel]"
options:0
metrics:metrics
views:views];
@@ -243,12 +264,14 @@ - (void)setupConstraints {
@"H:|-(p)-[gradientView]-(p)-|",
@"H:|-(p)-[backwardResetView]-(p)-|",
@"H:|-(p)-[backwardAnimateView]-(p)-|",
+ @"H:|-(p)-[indeterminateView]-(p)-|",
@"H:|-(p)-[stockLabel]-(p)-|",
@"H:|-(p)-[tintedLabel]-(p)-|",
@"H:|-(p)-[coloredLabel]-(p)-|",
@"H:|-(p)-[gradientLabel]-(p)-|",
@"H:|-(p)-[backwardResetLabel]-(p)-|",
@"H:|-(p)-[backwardAnimateLabel]-(p)-|",
+ @"H:|-(p)-[indeterminateLabel]-(p)-|",
];
for (NSString *format in horizontalVisualFormats) {
[horizontalConstraints
@@ -271,6 +294,7 @@ - (void)didPressAnimateButton:(UIButton *)sender {
completion:^(BOOL ignored) {
sender.enabled = YES;
}];
+ [self animateIndeterminateProgressBarWithCountdown:4];
}
- (void)animateStep1:(MDCProgressView *)progressView {
@@ -337,6 +361,27 @@ - (void)animateBackwardProgressAnimateViewWithCountdown:(NSInteger)remainingCoun
}
}
+- (void)animateIndeterminateProgressBarWithCountdown:(NSInteger)remainingCounts {
+ --remainingCounts;
+ __weak ProgressViewExample *weakSelf = self;
+
+ if (!_indeterminateProgressView.animating) {
+ [_indeterminateProgressView setHidden:NO animated:YES completion:nil];
+ [_indeterminateProgressView startAnimating];
+ }
+
+ if (remainingCounts > 0) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
+ (int64_t)(MDCProgressViewAnimationDuration * NSEC_PER_SEC)),
+ dispatch_get_main_queue(), ^{
+ [weakSelf animateIndeterminateProgressBarWithCountdown:remainingCounts];
+ });
+ } else {
+ [_indeterminateProgressView setHidden:YES animated:YES completion:nil];
+ [_indeterminateProgressView stopAnimating];
+ }
+}
+
#pragma mark - CatalogByConvention
+ (NSDictionary *)catalogMetadata {
diff --git a/components/ProgressView/src/MDCProgressView.h b/components/ProgressView/src/MDCProgressView.h
index eac3bd8f67c..ef89e712f55 100644
--- a/components/ProgressView/src/MDCProgressView.h
+++ b/components/ProgressView/src/MDCProgressView.h
@@ -24,6 +24,15 @@ typedef NS_ENUM(NSInteger, MDCProgressViewBackwardAnimationMode) {
MDCProgressViewBackwardAnimationModeAnimate
};
+/** The mode the progress bar is in. */
+typedef NS_ENUM(NSInteger, MDCProgressViewMode) {
+ /** Display the progress in a determinate way. */
+ MDCProgressViewModeDeterminate,
+
+ /** Display the progress in an indeterminate way. */
+ MDCProgressViewModeIndeterminate
+};
+
/**
A Material linear determinate progress view.
@@ -76,6 +85,20 @@ IB_DESIGNABLE
*/
@property(nonatomic, assign) float progress;
+/**
+ If the progress view shows a constant loading animation (MDCProgressViewModeIndeterminate) or is
+ based on the progress property (MDCProgressViewModeDeterminate).
+ The default value is MDCProgressViewModeDeterminate.
+ */
+@property(nonatomic, assign) MDCProgressViewMode mode;
+
+/**
+ Indicates if the progress view is animating when @c mode is @MDCProgressViewModeIndeterminate.
+
+ The default value is NO.
+ */
+@property(nonatomic, assign, getter=isAnimating) BOOL animating;
+
/**
The backward progress animation mode.
@@ -106,6 +129,26 @@ IB_DESIGNABLE
animated:(BOOL)animated
completion:(void (^__nullable)(BOOL finished))completion;
+/**
+ Changes the Determinate state, optionally animating the change.
+ @param mode The mode to change the progress view to.
+ @param animated Whether the change should be animated.
+ @param completion The completion block executed at the end of the animation.
+ */
+- (void)setMode:(MDCProgressViewMode)mode
+ animated:(BOOL)animated
+ completion:(void (^__nullable)(BOOL finished))completion;
+
+/**
+ Start the progress bar's indeterminate animation.
+ */
+- (void)startAnimating;
+
+/**
+ Stop the progress bar's indeterminate animation.
+ */
+- (void)stopAnimating;
+
/**
A block that is invoked when the @c MDCProgressView receives a call to @c
traitCollectionDidChange:. The block is called after the call to the superclass.
diff --git a/components/ProgressView/src/MDCProgressView.m b/components/ProgressView/src/MDCProgressView.m
index 6534fc3bd04..6eb1b2893e1 100644
--- a/components/ProgressView/src/MDCProgressView.m
+++ b/components/ProgressView/src/MDCProgressView.m
@@ -32,16 +32,22 @@
static const NSTimeInterval MDCProgressViewAnimationDuration = 0.25;
+static const NSTimeInterval kAnimationDuration = 1.8;
+
+static const CGFloat MDCProgressViewBarIndeterminateWidthPercentage = (CGFloat)0.52;
+
// The Bundle for string resources.
static NSString *const kBundle = @"MaterialProgressView.bundle";
@interface MDCProgressView ()
@property(nonatomic, strong) MDCProgressGradientView *progressView;
@property(nonatomic, strong) UIView *trackView;
+@property(nonatomic, strong) UIView *transitionView;
@property(nonatomic) BOOL animatingHide;
// A UIProgressView to return the same format for the accessibility value. For example, when
// progress is 0.497, it reports "fifty per cent".
@property(nonatomic, readonly) UIProgressView *accessibilityProgressView;
+@property(nonatomic) CFTimeInterval indeterminateAnimationStartTime;
@end
@@ -69,13 +75,24 @@ - (void)commonMDCProgressViewInit {
self.clipsToBounds = YES;
self.isAccessibilityElement = YES;
+ _mode = MDCProgressViewModeDeterminate;
+ _animating = NO;
+
_backwardProgressAnimationMode = MDCProgressViewBackwardAnimationModeReset;
_trackView = [[UIView alloc] initWithFrame:self.frame];
_trackView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self addSubview:_trackView];
- _progressView = [[MDCProgressGradientView alloc] initWithFrame:CGRectZero];
+ _transitionView =
+ [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, CGRectGetHeight(self.bounds))];
+ _transitionView.backgroundColor = MDCProgressViewDefaultTintColor();
+ [self addSubview:_transitionView];
+
+ float barWidth = [self indeterminateLoadingBarWidth];
+ CGRect progressBarFrame = CGRectMake(-barWidth, 0, barWidth, CGRectGetHeight(self.bounds));
+
+ _progressView = [[MDCProgressGradientView alloc] initWithFrame:progressBarFrame];
[self addSubview:_progressView];
_progressView.colors = @[
@@ -98,6 +115,11 @@ - (void)layoutSubviews {
[self updateProgressView];
[self updateTrackView];
}
+
+ if (_mode == MDCProgressViewModeIndeterminate && _animating) {
+ [self stopAnimating];
+ [self startAnimating];
+ }
}
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
@@ -135,6 +157,94 @@ - (UIColor *)trackTintColor {
- (void)setTrackTintColor:(UIColor *)trackTintColor {
self.trackView.backgroundColor = trackTintColor;
+ self.transitionView.backgroundColor = _progressTintColor;
+}
+
+- (void)setMode:(MDCProgressViewMode)mode {
+ if (_mode == mode) {
+ return;
+ }
+ _mode = mode;
+
+ // If the progress bar is animating in indeterminate mode, restart the animation.
+ if (_animating && _mode == MDCProgressViewModeIndeterminate) {
+ [self stopAnimating];
+ [self startAnimating];
+ }
+}
+
+- (void)setMode:(MDCProgressViewMode)mode
+ animated:(BOOL)animated
+ completion:(void (^__nullable)(BOOL finished))completion {
+ if (_mode == mode) {
+ if (completion) {
+ completion(YES);
+ }
+ return;
+ }
+ _mode = mode;
+
+ if (!animated) {
+ if (_mode == MDCProgressViewModeIndeterminate) {
+ self.progressView.frame = CGRectMake(0, 0, 0, CGRectGetHeight(self.bounds));
+ }
+ // Update the transition without an animation.
+ [self updateProgressView];
+ if (completion) {
+ completion(YES);
+ }
+ }
+
+ // Change from indeterminate to determinate.
+ if (_mode == MDCProgressViewModeDeterminate) {
+ // If the indeterminate view wasn't animating, just animate the determinate bar in.
+ if (!_animating) {
+ [self setProgress:_progress animated:animated completion:completion];
+ if (completion) {
+ completion(YES);
+ }
+ return;
+ }
+
+ // Transition from the indeterminate to the determinate bar.
+ CFTimeInterval stopTime = [self.progressView.layer convertTime:CACurrentMediaTime()
+ fromLayer:nil];
+ CFTimeInterval timeDiff = stopTime - _indeterminateAnimationStartTime;
+
+ CGFloat percentage = timeDiff / kAnimationDuration - floor(timeDiff / kAnimationDuration);
+ CGFloat xPosition =
+ (CGRectGetWidth(self.bounds) + [self indeterminateLoadingBarWidth]) * percentage -
+ [self indeterminateLoadingBarWidth];
+
+ [self.progressView.layer removeAllAnimations];
+ CGRect progressBarStartFrame =
+ CGRectMake(xPosition, 0, [self indeterminateLoadingBarWidth], CGRectGetHeight(self.bounds));
+ self.progressView.frame = progressBarStartFrame;
+
+ [UIView animateWithDuration:[[self class] animationDuration]
+ delay:0
+ options:[[self class] animationOptions]
+ animations:^{
+ [self updateProgressView];
+ }
+ completion:completion];
+ } else {
+ // Change from determinate to indeterminate.
+ CGRect zeroFrame = CGRectMake(0, 0, 0, CGRectGetHeight(self.bounds));
+ // Animate the transition from progress to indeterminate.
+ CGRect transitionViewFrame =
+ CGRectMake(0, 0, CGRectGetWidth(self.bounds) * _progress, CGRectGetHeight(self.bounds));
+ self.transitionView.frame = transitionViewFrame;
+ [self layoutIfNeeded];
+
+ [UIView animateWithDuration:[[self class] animationDuration]
+ delay:0
+ options:[[self class] animationOptions]
+ animations:^{
+ self.transitionView.frame = zeroFrame;
+ }
+ completion:completion];
+ }
}
- (void)setCornerRadius:(CGFloat)cornerRadius {
@@ -154,6 +264,10 @@ - (void)setProgress:(float)progress {
if (progress < 0)
progress = 0;
_progress = progress;
+ // Indeterminate mode ignores the progress.
+ if (_mode == MDCProgressViewModeIndeterminate) {
+ return;
+ }
[self accessibilityValueDidChange];
[self setNeedsLayout];
}
@@ -161,6 +275,13 @@ - (void)setProgress:(float)progress {
- (void)setProgress:(float)progress
animated:(BOOL)animated
completion:(void (^__nullable)(BOOL finished))userCompletion {
+ if (_mode == MDCProgressViewModeIndeterminate) {
+ self.progress = progress;
+ if (userCompletion) {
+ userCompletion(NO);
+ }
+ return;
+ }
if (progress < self.progress &&
self.backwardProgressAnimationMode == MDCProgressViewBackwardAnimationModeReset) {
self.progress = 0;
@@ -290,6 +411,16 @@ - (NSString *)defaultAccessibilityLabel {
[[self class] bundle], @"Progress View");
}
+- (void)startAnimating {
+ [self startAnimatingBar];
+ _animating = YES;
+}
+
+- (void)stopAnimating {
+ _animating = NO;
+ [self.progressView.layer removeAllAnimations];
+}
+
#pragma mark - Resource Bundle
+ (NSBundle *)bundle {
@@ -333,6 +464,9 @@ + (UIColor *)defaultTrackTintColorForProgressTintColor:(UIColor *)progressTintCo
}
- (void)updateProgressView {
+ if (_mode == MDCProgressViewModeIndeterminate) {
+ return;
+ }
// Update progressView with the current progress value.
CGFloat scale = self.window.screen.scale > 0 ? self.window.screen.scale : 1;
CGFloat pointWidth = self.progress * CGRectGetWidth(self.bounds);
@@ -349,4 +483,31 @@ - (void)updateTrackView {
self.trackView.frame = self.hidden ? CGRectMake(0.0, size.height, size.width, 0.0) : self.bounds;
}
+- (void)startAnimatingBar {
+ // If the bar isn't indeterminate or the bar is already animating, don't add the animation again.
+ if (_mode == MDCProgressViewModeDeterminate || _animating) {
+ return;
+ }
+
+ CGFloat barWidth = [self indeterminateLoadingBarWidth];
+ CGRect progressBarStartFrame = CGRectMake(-barWidth, 0, barWidth, CGRectGetHeight(self.bounds));
+ self.progressView.frame = progressBarStartFrame;
+
+ CGPoint progressBarEndPoint =
+ CGPointMake(self.progressView.layer.position.x + CGRectGetWidth(self.bounds) + barWidth,
+ CGRectGetHeight(self.bounds) / 2);
+ CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
+ animation.fromValue = [NSValue valueWithCGPoint:self.progressView.layer.position];
+ animation.toValue = [NSValue valueWithCGPoint:progressBarEndPoint];
+ animation.duration = kAnimationDuration;
+ animation.repeatCount = HUGE_VALF;
+ [self.progressView.layer addAnimation:animation forKey:@"position"];
+ _indeterminateAnimationStartTime = [self.progressView.layer convertTime:CACurrentMediaTime()
+ fromLayer:nil];
+}
+
+- (CGFloat)indeterminateLoadingBarWidth {
+ return CGRectGetWidth(self.bounds) * MDCProgressViewBarIndeterminateWidthPercentage;
+}
+
@end
diff --git a/components/ProgressView/tests/unit/ProgressViewTests.m b/components/ProgressView/tests/unit/ProgressViewTests.m
index b6857e20658..75845e8f919 100644
--- a/components/ProgressView/tests/unit/ProgressViewTests.m
+++ b/components/ProgressView/tests/unit/ProgressViewTests.m
@@ -61,6 +61,14 @@ - (void)testSetProgressAnimated {
[NSRunLoop.mainRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
+- (void)testIndeterminateProgressView {
+ _progressView.mode = MDCProgressViewModeIndeterminate;
+ [_progressView startAnimating];
+ XCTAssertTrue(_progressView.isAnimating);
+ [_progressView stopAnimating];
+ XCTAssertFalse(_progressView.isAnimating);
+}
+
- (void)testProgressClampedAt0 {
_progressView.progress = -1;
XCTAssertEqual(_progressView.progress, 0);
diff --git a/components/Tabs/examples/supplemental/TabBarTextOnlyExampleSupplemental.m b/components/Tabs/examples/supplemental/TabBarTextOnlyExampleSupplemental.m
index e5473cb568f..2c19c0eacaf 100644
--- a/components/Tabs/examples/supplemental/TabBarTextOnlyExampleSupplemental.m
+++ b/components/Tabs/examples/supplemental/TabBarTextOnlyExampleSupplemental.m
@@ -17,7 +17,6 @@
*/
#import "TabBarTextOnlyExampleSupplemental.h"
-#import "MaterialFlexibleHeader+ShiftBehaviorEnabledWithStatusBar.h"
static CGFloat const kAppBarMinHeight = 56;
static CGFloat const kTabBarHeight = 48;
diff --git a/components/Tabs/tests/unit/MDCItemBarTests.m b/components/Tabs/tests/unit/MDCItemBarTests.m
index ece91f6b846..69f36329872 100644
--- a/components/Tabs/tests/unit/MDCItemBarTests.m
+++ b/components/Tabs/tests/unit/MDCItemBarTests.m
@@ -160,9 +160,10 @@ - (void)testItemBarAddsPointerInteractionsToCells {
for (NSUInteger itemIndex = 0; itemIndex < visibleCells.count; itemIndex++) {
UICollectionViewCell *cell = visibleCells[itemIndex];
XCTAssertEqual(cell.interactions.count, 1,
- @"cell at index %lu should only have one UIInteraction.", itemIndex);
+ @"cell at index %lu should only have one UIInteraction.",
+ (unsigned long)itemIndex);
XCTAssert([cell.interactions.firstObject isKindOfClass:[UIPointerInteraction class]],
- @"cell at index %lu should have a UIPointerInteraction.", itemIndex);
+ @"cell at index %lu should have a UIPointerInteraction.", (unsigned long)itemIndex);
}
}
#endif
diff --git a/components/Tabs/tests/unit/TabBarView/MDCTabBarViewTests.m b/components/Tabs/tests/unit/TabBarView/MDCTabBarViewTests.m
index 0b486872952..f557e4a33df 100644
--- a/components/Tabs/tests/unit/TabBarView/MDCTabBarViewTests.m
+++ b/components/Tabs/tests/unit/TabBarView/MDCTabBarViewTests.m
@@ -1337,9 +1337,11 @@ - (void)testItemViewsHavePointerInteractions {
for (NSUInteger itemIndex = 0; itemIndex < itemViews.count; itemIndex++) {
MDCTabBarViewItemView *itemView = itemViews[itemIndex];
XCTAssertEqual(itemView.interactions.count, 1,
- @"itemView at index %lu should only have one UIInteraction.", itemIndex);
+ @"itemView at index %lu should only have one UIInteraction.",
+ (unsigned long)itemIndex);
XCTAssert([itemView.interactions.firstObject isKindOfClass:[UIPointerInteraction class]],
- @"itemView at index %lu should have a UIPointerInteraction.", itemIndex);
+ @"itemView at index %lu should have a UIPointerInteraction.",
+ (unsigned long)itemIndex);
}
}
#endif
diff --git a/components/TextControls/src/BaseTextFields/MDCBaseTextField.m b/components/TextControls/src/BaseTextFields/MDCBaseTextField.m
index 8f10594e214..d19e221191b 100644
--- a/components/TextControls/src/BaseTextFields/MDCBaseTextField.m
+++ b/components/TextControls/src/BaseTextFields/MDCBaseTextField.m
@@ -23,7 +23,7 @@
#import "MaterialTextControlsPrivate+Shared.h"
#import "MaterialTextControlsPrivate+TextFields.h"
-@interface MDCBaseTextField ()
+@interface MDCBaseTextField ()
@property(strong, nonatomic) UILabel *label;
@property(nonatomic, strong) MDCTextControlAssistiveLabelView *assistiveLabelView;
@@ -219,6 +219,8 @@ - (MDCBaseTextFieldLayout *)calculateLayoutWithTextFieldSize:(CGSize)textFieldSi
font:self.normalFont
floatingFont:self.floatingFont
label:self.label
+ labelPosition:self.labelPosition
+ sideViewAlignment:self.sideViewAlignment
leftView:self.leftView
leftViewMode:self.leftViewMode
rightView:self.rightView
@@ -469,10 +471,7 @@ - (CGRect)borderRectForBounds:(CGRect)bounds {
}
- (CGRect)clearButtonRectForBounds:(CGRect)bounds {
- if (self.labelPosition == MDCTextControlLabelPositionFloating) {
- return self.layout.clearButtonFrameFloating;
- }
- return self.layout.clearButtonFrameNormal;
+ return self.layout.clearButtonFrame;
}
- (CGRect)placeholderRectForBounds:(CGRect)bounds {
@@ -619,6 +618,12 @@ - (MDCTextControlColorViewModel *)textControlColorViewModelForState:
return colorViewModel;
}
+#pragma mark MDCTextControlTextField
+
+- (MDCTextControlTextFieldSideViewAlignment)sideViewAlignment {
+ return MDCTextControlTextFieldSideViewAlignmentCenteredInContainer;
+}
+
#pragma mark Accessibility Overrides
- (NSString *)accessibilityLabel {
diff --git a/components/TextControls/src/UnderlinedTextFields/MDCUnderlinedTextField.m b/components/TextControls/src/UnderlinedTextFields/MDCUnderlinedTextField.m
index 8730c151b05..c3a3e034410 100644
--- a/components/TextControls/src/UnderlinedTextFields/MDCUnderlinedTextField.m
+++ b/components/TextControls/src/UnderlinedTextFields/MDCUnderlinedTextField.m
@@ -17,6 +17,7 @@
#import
#import "MaterialTextControlsPrivate+Shared.h"
+#import "MaterialTextControlsPrivate+TextFields.h"
#import "MaterialTextControlsPrivate+UnderlinedStyle.h"
@interface MDCUnderlinedTextField (Private)
@@ -47,6 +48,10 @@ - (void)commonMDCUnderlinedTextFieldInit {
self.containerStyle = [[MDCTextControlStyleUnderlined alloc] init];
}
+- (MDCTextControlTextFieldSideViewAlignment)sideViewAlignment {
+ return MDCTextControlTextFieldSideViewAlignmentAlignedWithText;
+}
+
#pragma mark Stateful Color APIs
- (void)setUnderlineColor:(UIColor *)underlineColor forState:(MDCTextControlState)state {
diff --git a/components/TextFields/src/MDCTextField.m b/components/TextFields/src/MDCTextField.m
index 23a60999aa8..6c04f7032c7 100644
--- a/components/TextFields/src/MDCTextField.m
+++ b/components/TextFields/src/MDCTextField.m
@@ -852,7 +852,11 @@ - (NSString *)accessibilityValue {
if (self.text.length > 0) {
return [super accessibilityValue];
}
- return nil;
+
+ // Returning nil here causes iOS to default to [super accessibilityValue], which results in both
+ // accessibilityValue and accessibilityLabel being read out by VoiceOver, so we return the empty
+ // string instead.
+ return @"";
}
#pragma mark - Testing
diff --git a/components/private/Math/src/MDCMath.h b/components/private/Math/src/MDCMath.h
index 419ac020a58..0e869569f36 100644
--- a/components/private/Math/src/MDCMath.h
+++ b/components/private/Math/src/MDCMath.h
@@ -254,3 +254,43 @@ static inline BOOL MDCEdgeInsetsEqualToEdgeInsets(UIEdgeInsets insets1, UIEdgeIn
BOOL rightEqual = MDCCGFloatEqual(insets1.right, insets2.right);
return topEqual && leftEqual && bottomEqual && rightEqual;
}
+
+/**
+ This method calculates and returns the @c visibleAreaInsets to set on the view in order for it to
+ fit the correct minimumTouchTarget, given the view's frame.
+
+ @param frame The provided view's frame that needs to be adjusted to fit the minimum touch target
+ size.
+ @param minimumTouchTarget Given the width and height of the minimum touch target we can provide a
+ correct
+ @c visibleAreaInsets for the provided view frame.
+ */
+static inline UIEdgeInsets MDCVisibleAreaInsetsForMinimumTappability(CGRect frame,
+ CGSize minimumTouchTarget) {
+ UIEdgeInsets visibleAreaInsets = UIEdgeInsetsZero;
+ CGFloat additionalRequiredHeight;
+ CGFloat height = CGRectGetHeight(frame);
+ if (MDCCGFloatEqual(height, 0.f)) {
+ NSLog(@"Frame has no height to tap: %@", @(frame));
+ additionalRequiredHeight = 0.f;
+ } else {
+ additionalRequiredHeight = minimumTouchTarget.height - height;
+ }
+ CGFloat additionalRequiredWidth;
+ CGFloat width = CGRectGetWidth(frame);
+ if (MDCCGFloatEqual(width, 0.f)) {
+ NSLog(@"Frame has no width to tap: %@", @(frame));
+ additionalRequiredWidth = 0.f;
+ } else {
+ additionalRequiredWidth = minimumTouchTarget.width - width;
+ }
+ if (additionalRequiredHeight > 0.f) {
+ visibleAreaInsets.top = MDCCeil(additionalRequiredHeight * 0.5f);
+ visibleAreaInsets.bottom = additionalRequiredHeight - visibleAreaInsets.top;
+ }
+ if (additionalRequiredWidth > 0.f) {
+ visibleAreaInsets.left = MDCCeil(additionalRequiredWidth * 0.5f);
+ visibleAreaInsets.right = additionalRequiredWidth - visibleAreaInsets.left;
+ }
+ return visibleAreaInsets;
+}
diff --git a/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlStyleBase.m b/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlStyleBase.m
index 32df9ec2c59..4d42d49ebd6 100644
--- a/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlStyleBase.m
+++ b/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlStyleBase.m
@@ -53,8 +53,8 @@ - (void)removeStyleFrom:(id)textControl {
preferredContainerHeight:preferredContainerHeight];
}
-- (MDCTextControlHorizontalPositioningReferenceDefault *)horizontalPositioningReference {
- return [[MDCTextControlHorizontalPositioningReferenceDefault alloc] init];
+- (MDCTextControlHorizontalPositioningReference *)horizontalPositioningReference {
+ return [[MDCTextControlHorizontalPositioningReference alloc] init];
}
@end
diff --git a/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlStyleFilled.m b/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlStyleFilled.m
index 7f165da37a7..8902d73f678 100644
--- a/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlStyleFilled.m
+++ b/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlStyleFilled.m
@@ -112,9 +112,9 @@ - (void)removeStyleFrom:(id)textControl {
preferredContainerHeight:preferredContainerHeight];
}
-- (nonnull MDCTextControlHorizontalPositioningReferenceDefault *)horizontalPositioningReference {
- MDCTextControlHorizontalPositioningReferenceDefault *positioningReference =
- [[MDCTextControlHorizontalPositioningReferenceDefault alloc] init];
+- (nonnull MDCTextControlHorizontalPositioningReference *)horizontalPositioningReference {
+ MDCTextControlHorizontalPositioningReference *positioningReference =
+ [[MDCTextControlHorizontalPositioningReference alloc] init];
return positioningReference;
}
diff --git a/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlStyleOutlined.m b/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlStyleOutlined.m
index c3c4ad0c6a8..e8d488c1f2a 100644
--- a/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlStyleOutlined.m
+++ b/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlStyleOutlined.m
@@ -132,8 +132,8 @@ - (void)removeStyleFrom:(id)TextControl {
preferredContainerHeight:preferredContainerHeight];
}
-- (MDCTextControlHorizontalPositioningReferenceDefault *)horizontalPositioningReference {
- return [[MDCTextControlHorizontalPositioningReferenceDefault alloc] init];
+- (MDCTextControlHorizontalPositioningReference *)horizontalPositioningReference {
+ return [[MDCTextControlHorizontalPositioningReference alloc] init];
}
#pragma mark Internal Styling Methods
diff --git a/components/private/TextControlsPrivate/src/Shared/MDCTextControl.h b/components/private/TextControlsPrivate/src/Shared/MDCTextControl.h
index eef6263d57e..09d79b15517 100644
--- a/components/private/TextControlsPrivate/src/Shared/MDCTextControl.h
+++ b/components/private/TextControlsPrivate/src/Shared/MDCTextControl.h
@@ -188,6 +188,6 @@ FOUNDATION_EXTERN const CGFloat kMDCTextControlDefaultAnimationDuration;
This method returns an object that tells the view where to position its views
horizontally.
*/
-- (nonnull MDCTextControlHorizontalPositioningReferenceDefault *)horizontalPositioningReference;
+- (nonnull MDCTextControlHorizontalPositioningReference *)horizontalPositioningReference;
@end
diff --git a/components/private/TextControlsPrivate/src/Shared/MDCTextControlHorizontalPositioning.h b/components/private/TextControlsPrivate/src/Shared/MDCTextControlHorizontalPositioning.h
new file mode 100644
index 00000000000..029ff7d2ef1
--- /dev/null
+++ b/components/private/TextControlsPrivate/src/Shared/MDCTextControlHorizontalPositioning.h
@@ -0,0 +1,34 @@
+// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import
+
+/**
+ Objects conforming to this protocol provide information to layout objects regarding the horizontal
+ positioning of TextControl subviews.
+ */
+@protocol MDCTextControlHorizontalPositioning
+
+/**
+ The amount of horizontal padding between the leftmost subview and the left edge of the screen as
+ well as the rightmost view and the right edge of the screen.
+*/
+@property(nonatomic, assign) CGFloat horizontalEdgePadding;
+
+/**
+ The amount of horizontal padding between the various subviews.
+*/
+@property(nonatomic, assign) CGFloat horizontalInterItemSpacing;
+
+@end
diff --git a/components/private/TextControlsPrivate/src/Shared/MDCTextControlHorizontalPositioningReference.h b/components/private/TextControlsPrivate/src/Shared/MDCTextControlHorizontalPositioningReference.h
index a0910fc308d..c7544d0afe0 100644
--- a/components/private/TextControlsPrivate/src/Shared/MDCTextControlHorizontalPositioningReference.h
+++ b/components/private/TextControlsPrivate/src/Shared/MDCTextControlHorizontalPositioningReference.h
@@ -14,28 +14,11 @@
#import
-/**
- Objects conforming to this protocol provide information to layout objects regarding the horizontal
- positioning of TextControl subviews.
- */
-@protocol MDCTextControlHorizontalPositioningReference
-
-/**
- The amount of horizontal padding between the leftmost subview and the left edge of the screen as
- well as the rightmost view and the right edge of the screen.
-*/
-@property(nonatomic, assign) CGFloat horizontalEdgePadding;
-
-/**
- The amount of horizontal padding between the various subviews.
-*/
-@property(nonatomic, assign) CGFloat horizontalInterItemSpacing;
-
-@end
+#import "MDCTextControlHorizontalPositioning.h"
/**
- A default implementation of @c MDCTextControlHorizontalPositioningReference
+ An object conforming to @c MDCTextControlHorizontalPositioning
*/
-@interface MDCTextControlHorizontalPositioningReferenceDefault
- : NSObject
+@interface MDCTextControlHorizontalPositioningReference
+ : NSObject
@end
diff --git a/components/private/TextControlsPrivate/src/Shared/MDCTextControlHorizontalPositioningReference.m b/components/private/TextControlsPrivate/src/Shared/MDCTextControlHorizontalPositioningReference.m
index 0e38b89fec8..6043da97641 100644
--- a/components/private/TextControlsPrivate/src/Shared/MDCTextControlHorizontalPositioningReference.m
+++ b/components/private/TextControlsPrivate/src/Shared/MDCTextControlHorizontalPositioningReference.m
@@ -12,35 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#import "MDCTextControlHorizontalPositioningReference.h"
-
#import
-static const CGFloat kMDCTextControlHorizontalPositioningReferenceDefaultHorizontalEdgePadding =
- 12.0f;
-static const CGFloat
- kMDCTextControlHorizontalPositioningReferenceDefaultHorizontalInterItemPadding = 12.0f;
+#import "MDCTextControlHorizontalPositioningReference.h"
-@interface MDCTextControlHorizontalPositioningReferenceDefault ()
-@end
+static const CGFloat kMDCTextControlHorizontalPositioningReferenceHorizontalEdgePadding = 12.0f;
+static const CGFloat kMDCTextControlHorizontalPositioningReferenceHorizontalInterItemPadding =
+ 12.0f;
-@implementation MDCTextControlHorizontalPositioningReferenceDefault
+@implementation MDCTextControlHorizontalPositioningReference
@synthesize horizontalEdgePadding = _horizontalEdgePadding;
@synthesize horizontalInterItemSpacing = _horizontalInterItemSpacing;
- (instancetype)init {
self = [super init];
if (self) {
- [self commonMDCTextControlHorizontalPositioningReferenceDefaultInit];
+ self.horizontalEdgePadding = kMDCTextControlHorizontalPositioningReferenceHorizontalEdgePadding;
+ self.horizontalInterItemSpacing =
+ kMDCTextControlHorizontalPositioningReferenceHorizontalInterItemPadding;
}
return self;
}
-- (void)commonMDCTextControlHorizontalPositioningReferenceDefaultInit {
- self.horizontalEdgePadding =
- kMDCTextControlHorizontalPositioningReferenceDefaultHorizontalEdgePadding;
- self.horizontalInterItemSpacing =
- kMDCTextControlHorizontalPositioningReferenceDefaultHorizontalInterItemPadding;
-}
-
@end
diff --git a/components/private/TextControlsPrivate/src/Shared/MaterialTextControlsPrivate+Shared.h b/components/private/TextControlsPrivate/src/Shared/MaterialTextControlsPrivate+Shared.h
index dff1408ede6..63510e6a53b 100644
--- a/components/private/TextControlsPrivate/src/Shared/MaterialTextControlsPrivate+Shared.h
+++ b/components/private/TextControlsPrivate/src/Shared/MaterialTextControlsPrivate+Shared.h
@@ -17,6 +17,7 @@
#import "MDCTextControlAssistiveLabelViewLayout.h"
#import "MDCTextControlColorViewModel.h"
#import "MDCTextControlGradientManager.h"
+#import "MDCTextControlHorizontalPositioning.h"
#import "MDCTextControlHorizontalPositioningReference.h"
#import "MDCTextControlLabelAnimation.h"
#import "MDCTextControlLabelPosition.h"
diff --git a/components/private/TextControlsPrivate/src/TextFields/MDCBaseTextFieldLayout.h b/components/private/TextControlsPrivate/src/TextFields/MDCBaseTextFieldLayout.h
index e98a7e6b2de..018fadde046 100644
--- a/components/private/TextControlsPrivate/src/TextFields/MDCBaseTextFieldLayout.h
+++ b/components/private/TextControlsPrivate/src/TextFields/MDCBaseTextFieldLayout.h
@@ -13,14 +13,14 @@
// limitations under the License.
#import "MaterialTextControlsPrivate+Shared.h"
+#import "MDCTextControlTextFieldSideViewAlignment.h"
@interface MDCBaseTextFieldLayout : NSObject
@property(nonatomic, assign) BOOL leftViewHidden;
@property(nonatomic, assign) BOOL rightViewHidden;
-@property(nonatomic, assign) CGRect clearButtonFrameFloating;
-@property(nonatomic, assign) CGRect clearButtonFrameNormal;
+@property(nonatomic, assign) CGRect clearButtonFrame;
@property(nonatomic, assign) CGRect labelFrameFloating;
@property(nonatomic, assign) CGRect labelFrameNormal;
@property(nonatomic, assign) CGRect textRectNormal;
@@ -47,11 +47,13 @@
positioningReference:
(nonnull id)positioningReference
horizontalPositioningReference:
- (nonnull id)horizontalPositioningReference
+ (nonnull id)horizontalPositioningReference
text:(nullable NSString *)text
font:(nonnull UIFont *)font
floatingFont:(nonnull UIFont *)floatingFont
label:(nonnull UILabel *)label
+ labelPosition:(MDCTextControlLabelPosition)labelPosition
+ sideViewAlignment:(MDCTextControlTextFieldSideViewAlignment)sideViewAlignment
leftView:(nullable UIView *)leftView
leftViewMode:(UITextFieldViewMode)leftViewMode
rightView:(nullable UIView *)rightView
diff --git a/components/private/TextControlsPrivate/src/TextFields/MDCBaseTextFieldLayout.m b/components/private/TextControlsPrivate/src/TextFields/MDCBaseTextFieldLayout.m
index 2357bdc9e54..112b1381569 100644
--- a/components/private/TextControlsPrivate/src/TextFields/MDCBaseTextFieldLayout.m
+++ b/components/private/TextControlsPrivate/src/TextFields/MDCBaseTextFieldLayout.m
@@ -27,11 +27,13 @@ - (instancetype)initWithTextFieldSize:(CGSize)textFieldSize
positioningReference:
(id)positioningReference
horizontalPositioningReference:
- (id)horizontalPositioningReference
+ (id)horizontalPositioningReference
text:(NSString *)text
font:(UIFont *)font
floatingFont:(UIFont *)floatingFont
label:(UILabel *)label
+ labelPosition:(MDCTextControlLabelPosition)labelPosition
+ sideViewAlignment:(MDCTextControlTextFieldSideViewAlignment)sideViewAlignment
leftView:(UIView *)leftView
leftViewMode:(UITextFieldViewMode)leftViewMode
rightView:(UIView *)rightView
@@ -54,6 +56,8 @@ - (instancetype)initWithTextFieldSize:(CGSize)textFieldSize
font:font
floatingFont:floatingFont
label:label
+ labelPosition:labelPosition
+ sideViewAlignment:sideViewAlignment
leftView:leftView
leftViewMode:leftViewMode
rightView:rightView
@@ -77,11 +81,13 @@ - (void)calculateLayoutWithTextFieldSize:(CGSize)textFieldSize
positioningReference:
(id)positioningReference
horizontalPositioningReference:
- (id)horizontalPositioningReference
+ (id)horizontalPositioningReference
text:(NSString *)text
font:(UIFont *)font
floatingFont:(UIFont *)floatingFont
label:(UILabel *)label
+ labelPosition:(MDCTextControlLabelPosition)labelPosition
+ sideViewAlignment:(MDCTextControlTextFieldSideViewAlignment)sideViewAlignment
leftView:(UIView *)leftView
leftViewMode:(UITextFieldViewMode)leftViewMode
rightView:(UIView *)rightView
@@ -133,39 +139,6 @@ - (void)calculateLayoutWithTextFieldSize:(CGSize)textFieldSize
clearButtonMinX = clearButtonMaxX - clearButtonSideLength;
}
- CGFloat floatingLabelMinY = positioningReference.paddingBetweenContainerTopAndFloatingLabel;
- CGFloat floatingLabelHeight = floatingFont.lineHeight;
- CGFloat floatingLabelMaxY = floatingLabelMinY + floatingLabelHeight;
-
- CGFloat textRectMinYWithFloatingLabel =
- floatingLabelMaxY + positioningReference.paddingBetweenFloatingLabelAndEditingText;
-
- CGFloat textRectHeight = [self textHeightWithFont:font];
- CGFloat textRectCenterYWithFloatingLabel =
- textRectMinYWithFloatingLabel + ((CGFloat)0.5 * textRectHeight);
-
- CGFloat textRectMinYNormal = positioningReference.paddingBetweenContainerTopAndNormalLabel;
- CGFloat textRectCenterYNormal = textRectMinYNormal + ((CGFloat)0.5 * textRectHeight);
- CGFloat containerMidY = (CGFloat)0.5 * positioningReference.containerHeight;
-
- CGFloat leftViewHeight = CGRectGetHeight(leftView.frame);
- CGFloat leftViewMinY = 0;
- if (displaysLeftView) {
- leftViewMinY = [self minYForSubviewWithHeight:leftViewHeight centerY:containerMidY];
- }
-
- CGFloat rightViewHeight = CGRectGetHeight(rightView.frame);
- CGFloat rightViewMinY = 0;
- if (displaysRightView) {
- rightViewMinY = [self minYForSubviewWithHeight:rightViewHeight centerY:containerMidY];
- }
-
- CGFloat clearButtonMinY = [self minYForSubviewWithHeight:clearButtonSideLength
- centerY:textRectCenterYNormal];
- CGFloat clearButtonFloatingMinY =
- [self minYForSubviewWithHeight:clearButtonSideLength
- centerY:textRectCenterYWithFloatingLabel];
-
CGFloat textRectMinX = 0;
CGFloat textRectMaxX = 0;
CGFloat labelMinX = 0;
@@ -208,22 +181,43 @@ - (void)calculateLayoutWithTextFieldSize:(CGSize)textFieldSize
: textFieldWidth - horizontalEdgePadding;
}
+ CGFloat textRectMinYNormal = positioningReference.paddingBetweenContainerTopAndNormalLabel;
CGFloat textRectWidth = textRectMaxX - textRectMinX;
+ CGFloat textRectHeight = [self textHeightWithFont:font];
CGRect textRectNormal =
CGRectMake(textRectMinX, textRectMinYNormal, textRectWidth, textRectHeight);
+
+ CGFloat floatingLabelMinY = positioningReference.paddingBetweenContainerTopAndFloatingLabel;
+ CGFloat floatingLabelHeight = floatingFont.lineHeight;
+ CGFloat floatingLabelMaxY = floatingLabelMinY + floatingLabelHeight;
+ CGFloat textRectMinYWithFloatingLabel =
+ floatingLabelMaxY + positioningReference.paddingBetweenFloatingLabelAndEditingText;
+ CGFloat textRectCenterYWithFloatingLabel =
+ textRectMinYWithFloatingLabel + ((CGFloat)0.5 * textRectHeight);
CGFloat textRectMinYFloatingLabel =
(CGFloat)floor((double)(textRectCenterYWithFloatingLabel - (textRectHeight * (CGFloat)0.5)));
CGRect textRectFloating =
CGRectMake(textRectMinX, textRectMinYFloatingLabel, textRectWidth, textRectHeight);
+ CGFloat containerMidY = (CGFloat)0.5 * positioningReference.containerHeight;
+ BOOL isFloatingLabel = labelPosition == MDCTextControlLabelPositionFloating;
+ CGFloat textRectMidY =
+ isFloatingLabel ? CGRectGetMidY(textRectFloating) : CGRectGetMidY(textRectNormal);
+ CGFloat sideViewMidY = [self sideViewMidYWithSideViewAlignment:sideViewAlignment
+ containerMidY:containerMidY
+ textRectMidY:textRectMidY];
+
+ CGFloat leftViewHeight = CGRectGetHeight(leftView.frame);
+ CGFloat rightViewHeight = CGRectGetHeight(rightView.frame);
+ CGFloat leftViewMinY = [self minYForSubviewWithHeight:leftViewHeight centerY:sideViewMidY];
+ CGFloat rightViewMinY = [self minYForSubviewWithHeight:rightViewHeight centerY:sideViewMidY];
CGRect leftViewFrame = CGRectMake(leftViewMinX, leftViewMinY, leftViewWidth, leftViewHeight);
CGRect rightViewFrame =
CGRectMake(rightViewMinX, rightViewMinY, CGRectGetWidth(rightView.frame), rightViewHeight);
-
- CGRect clearButtonFrameNormal =
+ CGFloat clearButtonMinY = [self minYForSubviewWithHeight:clearButtonSideLength
+ centerY:sideViewMidY];
+ CGRect clearButtonFrame =
CGRectMake(clearButtonMinX, clearButtonMinY, clearButtonSideLength, clearButtonSideLength);
- CGRect clearButtonFrameFloating = CGRectMake(clearButtonMinX, clearButtonFloatingMinY,
- clearButtonSideLength, clearButtonSideLength);
CGRect labelFrameNormal = [self labelFrameWithText:label.text
labelPosition:MDCTextControlLabelPositionNormal
@@ -258,8 +252,7 @@ - (void)calculateLayoutWithTextFieldSize:(CGSize)textFieldSize
self.assistiveLabelViewLayout.calculatedHeight);
self.leftViewFrame = leftViewFrame;
self.rightViewFrame = rightViewFrame;
- self.clearButtonFrameFloating = clearButtonFrameFloating;
- self.clearButtonFrameNormal = clearButtonFrameNormal;
+ self.clearButtonFrame = clearButtonFrame;
self.textRectFloating = textRectFloating;
self.textRectNormal = textRectNormal;
self.labelFrameFloating = labelFrameFloating;
@@ -269,6 +262,23 @@ - (void)calculateLayoutWithTextFieldSize:(CGSize)textFieldSize
self.containerHeight = positioningReference.containerHeight;
}
+- (CGFloat)sideViewMidYWithSideViewAlignment:
+ (MDCTextControlTextFieldSideViewAlignment)sideViewAlignment
+ containerMidY:(CGFloat)containerMidY
+ textRectMidY:(CGFloat)textRectMidY {
+ CGFloat sideViewMidY = containerMidY;
+ switch (sideViewAlignment) {
+ case MDCTextControlTextFieldSideViewAlignmentCenteredInContainer:
+ break;
+ case MDCTextControlTextFieldSideViewAlignmentAlignedWithText:
+ sideViewMidY = textRectMidY;
+ break;
+ default:
+ break;
+ }
+ return sideViewMidY;
+}
+
- (CGFloat)minYForSubviewWithHeight:(CGFloat)height centerY:(CGFloat)centerY {
return (CGFloat)round((double)(centerY - ((CGFloat)0.5 * height)));
}
diff --git a/components/private/TextControlsPrivate/src/TextFields/MDCTextControlTextField.h b/components/private/TextControlsPrivate/src/TextFields/MDCTextControlTextField.h
new file mode 100644
index 00000000000..708d8541324
--- /dev/null
+++ b/components/private/TextControlsPrivate/src/TextFields/MDCTextControlTextField.h
@@ -0,0 +1,20 @@
+// Copyright 2020-present the Material Components for iOS authors. All Rights
+// Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "MDCTextControlTextFieldSideViewAlignment.h" // IWYU pragma: export
+
+@protocol MDCTextControlTextField
+@property(nonatomic, assign, readonly) MDCTextControlTextFieldSideViewAlignment sideViewAlignment;
+@end
diff --git a/components/private/TextControlsPrivate/src/TextFields/MDCTextControlTextFieldSideViewAlignment.h b/components/private/TextControlsPrivate/src/TextFields/MDCTextControlTextFieldSideViewAlignment.h
new file mode 100644
index 00000000000..51e86a72835
--- /dev/null
+++ b/components/private/TextControlsPrivate/src/TextFields/MDCTextControlTextFieldSideViewAlignment.h
@@ -0,0 +1,30 @@
+// Copyright 2020-present the Material Components for iOS authors. All Rights
+// Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ This enum describes different approaches to vertically positioning side views (@c leadingView, @c
+ trailingView, and the clear button) in a text field. MDCBaseTextFieldLayout uses this to help it
+ determine where to put side views on MDCBaseTextField and its subclasses.
+ */
+typedef NS_ENUM(NSUInteger, MDCTextControlTextFieldSideViewAlignment) {
+ /**
+ This case will result in the side views being vertically centered in the text field's container.
+ */
+ MDCTextControlTextFieldSideViewAlignmentCenteredInContainer,
+ /**
+ This case will result in the side views sharing the same mid Y as the text rect.
+ */
+ MDCTextControlTextFieldSideViewAlignmentAlignedWithText,
+};
diff --git a/components/private/TextControlsPrivate/src/TextFields/MaterialTextControlsPrivate+TextFields.h b/components/private/TextControlsPrivate/src/TextFields/MaterialTextControlsPrivate+TextFields.h
index 9af2addce2b..2ea83d5b91c 100644
--- a/components/private/TextControlsPrivate/src/TextFields/MaterialTextControlsPrivate+TextFields.h
+++ b/components/private/TextControlsPrivate/src/TextFields/MaterialTextControlsPrivate+TextFields.h
@@ -14,3 +14,5 @@
// limitations under the License.
#import "MDCBaseTextFieldLayout.h"
+#import "MDCTextControlTextField.h"
+#import "MDCTextControlTextFieldSideViewAlignment.h"
diff --git a/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlStyleUnderlined.m b/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlStyleUnderlined.m
index c3bb7bb3f7c..3eea57d79c3 100644
--- a/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlStyleUnderlined.m
+++ b/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlStyleUnderlined.m
@@ -25,6 +25,7 @@
static const CGFloat kUnderlinedContainerStyleUnderlineWidthThick = 2.0f;
static const CGFloat kUnderlinedFloatingLabelScaleFactor = 0.75f;
+static const CGFloat kUnderlinedHorizontalEdgePaddingDefault = 2;
@interface MDCTextControlStyleUnderlined ()
@@ -127,10 +128,10 @@ - (UIFont *)floatingFontWithNormalFont:(UIFont *)font {
return [font fontWithSize:floatingFontSize];
}
-- (nonnull MDCTextControlHorizontalPositioningReferenceDefault *)horizontalPositioningReference {
- MDCTextControlHorizontalPositioningReferenceDefault *positioningReference =
- [[MDCTextControlHorizontalPositioningReferenceDefault alloc] init];
- positioningReference.horizontalEdgePadding = 2;
+- (nonnull MDCTextControlHorizontalPositioningReference *)horizontalPositioningReference {
+ MDCTextControlHorizontalPositioningReference *positioningReference =
+ [[MDCTextControlHorizontalPositioningReference alloc] init];
+ positioningReference.horizontalEdgePadding = kUnderlinedHorizontalEdgePaddingDefault;
return positioningReference;
}
diff --git a/components/private/TextControlsPrivate/tests/unit/TextFields/MDCBaseTextFieldLayoutTests.m b/components/private/TextControlsPrivate/tests/unit/TextFields/MDCBaseTextFieldLayoutTests.m
index 5c306d5f0ae..d8940a8ba23 100644
--- a/components/private/TextControlsPrivate/tests/unit/TextFields/MDCBaseTextFieldLayoutTests.m
+++ b/components/private/TextControlsPrivate/tests/unit/TextFields/MDCBaseTextFieldLayoutTests.m
@@ -48,12 +48,13 @@ - (MDCBaseTextFieldLayout *)createLayoutWithSideViewsAndViewMode:(UITextFieldVie
MDCBaseTextFieldLayout *layout = [[MDCBaseTextFieldLayout alloc]
initWithTextFieldSize:textFieldSize
positioningReference:positioningReference
- horizontalPositioningReference:[[MDCTextControlHorizontalPositioningReferenceDefault alloc]
- init]
+ horizontalPositioningReference:[[MDCTextControlHorizontalPositioningReference alloc] init]
text:@"Text"
font:font
floatingFont:floatingFont
label:[[UILabel alloc] init]
+ labelPosition:MDCTextControlLabelPositionFloating
+ sideViewAlignment:MDCTextControlTextFieldSideViewAlignmentCenteredInContainer
leftView:[self createSideView]
leftViewMode:viewMode
rightView:[self createSideView]
diff --git a/demos/supplemental/RemoteImageServiceForMDCDemos.podspec b/demos/supplemental/RemoteImageServiceForMDCDemos.podspec
index 1eb1f66bc27..879bf3554b2 100644
--- a/demos/supplemental/RemoteImageServiceForMDCDemos.podspec
+++ b/demos/supplemental/RemoteImageServiceForMDCDemos.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "RemoteImageServiceForMDCDemos"
- s.version = "109.7.0"
+ s.version = "109.8.0"
s.summary = "A helper image class for the MDC demos."
s.description = "This spec is made for use in the MDC demos. It gets images via url."
s.homepage = "https://github.com/material-components/material-components-ios"