From dde71806927bbc0af0ba01dfa7055e6d6a89476b Mon Sep 17 00:00:00 2001 From: Jake Rockland Date: Mon, 13 Jul 2020 12:05:36 -0700 Subject: [PATCH 01/22] Update the MDCBottomDrawerViewController MDC themer to use dynamic colors. PiperOrigin-RevId: 321004879 --- ...ttomDrawerViewController+MaterialTheming.m | 62 +++++++------------ 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/components/NavigationDrawer/src/Theming/MDCBottomDrawerViewController+MaterialTheming.m b/components/NavigationDrawer/src/Theming/MDCBottomDrawerViewController+MaterialTheming.m index 5b639d717b1..16ae37f2907 100644 --- a/components/NavigationDrawer/src/Theming/MDCBottomDrawerViewController+MaterialTheming.m +++ b/components/NavigationDrawer/src/Theming/MDCBottomDrawerViewController+MaterialTheming.m @@ -13,10 +13,7 @@ // limitations under the License. #import "MaterialElevation.h" -#import "MaterialNavigationDrawer.h" #import "MDCBottomDrawerViewController+MaterialTheming.h" -#import "UIColor+MaterialDynamic.h" -#import "MaterialColorScheme.h" static const CGFloat kCollapsedCornerRadius = 8.0f; static const CGFloat kFullScreenCornerRadius = 0.0f; @@ -39,57 +36,46 @@ - (void)applyThemeWithScheme:(id)scheme - (void)applyColorThemeWithColorScheme:(id)colorScheme applyToTrackingScrollView:(BOOL)applyToTrackingScrollView { - [MDCBottomDrawerViewController mdc_setBackgroundColorForBottomDrawer:self - applyToTrackingScrollView:applyToTrackingScrollView - withScheme:colorScheme]; + [MDCBottomDrawerViewController + mdc_setResolvedBackgroundColorForBottomDrawer:self + applyToTrackingScrollView:applyToTrackingScrollView + withScheme:colorScheme]; if (colorScheme.elevationOverlayEnabledForDarkMode) { self.mdc_elevationDidChangeBlock = ^(id _Nonnull object, CGFloat absoluteElevation) { if ([object isKindOfClass:[MDCBottomDrawerViewController class]]) { MDCBottomDrawerViewController *bottomDrawer = (MDCBottomDrawerViewController *)object; [MDCBottomDrawerViewController - mdc_setBackgroundColorForBottomDrawer:bottomDrawer - applyToTrackingScrollView:applyToTrackingScrollView - withScheme:colorScheme]; + mdc_setResolvedBackgroundColorForBottomDrawer:bottomDrawer + applyToTrackingScrollView:applyToTrackingScrollView + withScheme:colorScheme]; } }; +#if defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) + if (@available(iOS 13.0, *)) { + self.traitCollectionDidChangeBlock = ^(MDCBottomDrawerViewController *_Nonnull bottomDrawer, + UITraitCollection *_Nullable previousTraitCollection) { + if ([bottomDrawer.traitCollection + hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) { + [MDCBottomDrawerViewController + mdc_setResolvedBackgroundColorForBottomDrawer:bottomDrawer + applyToTrackingScrollView:applyToTrackingScrollView + withScheme:colorScheme]; + } + }; + } +#endif } self.scrimColor = [colorScheme.onSurfaceColor colorWithAlphaComponent:kScrimAlpha]; self.topHandleColor = [colorScheme.onSurfaceColor colorWithAlphaComponent:kTopHandleAlpha]; } -+ (void)mdc_setBackgroundColorForBottomDrawer:(MDCBottomDrawerViewController *)bottomDrawer - applyToTrackingScrollView:(BOOL)applyToTrackingScrollView - withScheme:(id)scheme { ++ (void)mdc_setResolvedBackgroundColorForBottomDrawer:(MDCBottomDrawerViewController *)bottomDrawer + applyToTrackingScrollView:(BOOL)applyToTrackingScrollView + withScheme:(id)scheme { UIColor *backgroundColor = [scheme.backgroundColor mdc_resolvedColorWithTraitCollection:bottomDrawer.traitCollection elevation:bottomDrawer.view.mdc_absoluteElevation]; -#if defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) - if (@available(iOS 13.0, *)) { - // Note that UITraitCollection with explicit `traitCollectionWithUserInterfaceStyle:` must - // come after `bottomDrawer.traitCollection` for the `userInterfaceStyle` to be properly - // overwritten. - UITraitCollection *darkTraitCollection = - [UITraitCollection traitCollectionWithTraitsFromCollections:@[ - bottomDrawer.traitCollection, - [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark] - ]]; - UITraitCollection *lightTraitCollection = - [UITraitCollection traitCollectionWithTraitsFromCollections:@[ - bottomDrawer.traitCollection, - [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight] - ]]; - UIColor *darkBackgroundColor = [scheme.backgroundColor - mdc_resolvedColorWithTraitCollection:darkTraitCollection - elevation:bottomDrawer.view.mdc_absoluteElevation]; - UIColor *lightBackgroundColor = [scheme.backgroundColor - mdc_resolvedColorWithTraitCollection:lightTraitCollection - elevation:bottomDrawer.view.mdc_absoluteElevation]; - backgroundColor = [UIColor colorWithUserInterfaceStyleDarkColor:darkBackgroundColor - defaultColor:lightBackgroundColor]; - } -#endif - bottomDrawer.headerViewController.view.backgroundColor = backgroundColor; bottomDrawer.contentViewController.view.backgroundColor = backgroundColor; if (applyToTrackingScrollView) { From 81b5c39f7e072bf57f4e1dabfb5d26d60736861e Mon Sep 17 00:00:00 2001 From: Galia Kaufman Date: Mon, 13 Jul 2020 15:28:30 -0700 Subject: [PATCH 02/22] [Dialogs] Disabling selection in alerts while allowing tappable links Internally subclassing UITextView in order to disable selectability of text. PiperOrigin-RevId: 321046270 --- .../private/MDCAlertControllerView+Private.m | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/components/Dialogs/src/private/MDCAlertControllerView+Private.m b/components/Dialogs/src/private/MDCAlertControllerView+Private.m index d6652a3057c..7a96a0b588d 100644 --- a/components/Dialogs/src/private/MDCAlertControllerView+Private.m +++ b/components/Dialogs/src/private/MDCAlertControllerView+Private.m @@ -36,6 +36,9 @@ static const CGFloat MDCDialogMessageOpacity = 0.54f; +@interface MDCNonselectableTextView : UITextView +@end + @interface MDCAlertControllerView () @property(nonatomic, getter=isVerticalActionsLayout) BOOL verticalActionsLayout; @@ -92,7 +95,7 @@ - (instancetype)initWithFrame:(CGRect)frame { self.titleLabel.accessibilityTraits |= UIAccessibilityTraitHeader; [self.titleScrollView addSubview:self.titleLabel]; - self.messageTextView = [[UITextView alloc] initWithFrame:CGRectZero]; + self.messageTextView = [[MDCNonselectableTextView alloc] initWithFrame:CGRectZero]; self.messageTextView.textAlignment = NSTextAlignmentNatural; self.messageTextView.textContainerInset = UIEdgeInsetsZero; self.messageTextView.textContainer.lineFragmentPadding = 0.0f; @@ -1064,3 +1067,30 @@ - (void)updateFonts { } @end + +@implementation MDCNonselectableTextView + +// Disabling text selection when selectable is YES, while allowing gestures for inlined links. +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { + if (![super pointInside:point withEvent:event]) { + return NO; + } + + UITextPosition *position = [self closestPositionToPoint:point]; + if (!position) { + return NO; + } + + UITextRange *range = [self.tokenizer rangeEnclosingPosition:position + withGranularity:UITextGranularityCharacter + inDirection:UITextLayoutDirectionLeft]; + if (!range) { + return NO; + } + + NSInteger offset = [self offsetFromPosition:self.beginningOfDocument toPosition:range.start]; + id link = [self.attributedText attribute:NSLinkAttributeName atIndex:offset effectiveRange:nil]; + return link != nil; +} + +@end From be54e359513d3156f40198becf635d230449a64a Mon Sep 17 00:00:00 2001 From: Wenyu Zhang Date: Mon, 13 Jul 2020 19:54:17 -0700 Subject: [PATCH 03/22] [MDCProgressView] Fix indeterminate animation being removed before it is presented on screen. PiperOrigin-RevId: 321083086 --- .../examples/ProgressViewInDialogExample.m | 102 ++++++++++++++++++ components/ProgressView/src/MDCProgressView.m | 2 + 2 files changed, 104 insertions(+) create mode 100644 components/ProgressView/examples/ProgressViewInDialogExample.m diff --git a/components/ProgressView/examples/ProgressViewInDialogExample.m b/components/ProgressView/examples/ProgressViewInDialogExample.m new file mode 100644 index 00000000000..e1c9c11d315 --- /dev/null +++ b/components/ProgressView/examples/ProgressViewInDialogExample.m @@ -0,0 +1,102 @@ +// 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 + +#import "MaterialButtons.h" +#import "MaterialButtons+Theming.h" +#import "MaterialDialogs.h" +#import "MaterialProgressView.h" +#import "MaterialProgressView+Theming.h" +#import "MaterialColorScheme.h" +#import "MaterialContainerScheme.h" + +// static const CGFloat kMDCProgressViewIndeterminateAnimationDuration = 4; +static const CGFloat kMDCProgressViewHeight = 2; + +@interface ProgressViewInDialogExample : UIViewController + +@property(nonatomic, strong) MDCAlertController *alertViewController; +@property(nonatomic, strong) MDCProgressView *indeterminateProgressView; +@property(nonatomic, strong) MDCContainerScheme *containerScheme; +@property(nonatomic, strong) MDCButton *alertButton; +@end + +@implementation ProgressViewInDialogExample + +- (void)viewDidLoad { + [super viewDidLoad]; + + if (!self.containerScheme) { + self.containerScheme = [[MDCContainerScheme alloc] init]; + } + + self.title = @"Progress View (Dialogs)"; + self.view.backgroundColor = self.containerScheme.colorScheme.backgroundColor; + + [self setupButton]; + [self setupDialog]; +} + +- (void)setupButton { + MDCButton *alertButton = [[MDCButton alloc] init]; + [alertButton setTitle:@"Present Dialog" forState:UIControlStateNormal]; + [alertButton applyTextThemeWithScheme:self.containerScheme]; + [alertButton addTarget:self + action:@selector(didTapButton) + forControlEvents:UIControlEventTouchUpInside]; + alertButton.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:alertButton]; + [alertButton.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = YES; + [alertButton.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = YES; + self.alertButton = alertButton; +} + +- (void)setupDialog { + MDCProgressView *indeterminateProgressView = [[MDCProgressView alloc] init]; + indeterminateProgressView.mode = MDCProgressViewModeIndeterminate; + indeterminateProgressView.translatesAutoresizingMaskIntoConstraints = NO; + indeterminateProgressView.frame = CGRectMake(0, 0, 0, kMDCProgressViewHeight); + [indeterminateProgressView applyThemeWithScheme:self.containerScheme]; + [indeterminateProgressView startAnimating]; + self.indeterminateProgressView = indeterminateProgressView; + + MDCAlertController *alertViewController = [[MDCAlertController alloc] init]; + alertViewController.title = @"Dialog with Progress View"; + MDCAlertAction *dismissAction = [MDCAlertAction + actionWithTitle:@"OK" + handler:^(MDCAlertAction *_Nonnull action) { + [self.alertViewController dismissViewControllerAnimated:YES completion:nil]; + }]; + [alertViewController addAction:dismissAction]; + alertViewController.accessoryView = indeterminateProgressView; + self.alertViewController = alertViewController; +} + +- (void)didTapButton { + [self presentViewController:self.alertViewController animated:YES completion:nil]; +} + +#pragma mark - CatalogByConvention + ++ (NSDictionary *)catalogMetadata { + return @{ + @"breadcrumbs" : @[ @"Progress View", @"Progress View In Dialog" ], + @"description" : @"Progress indicators presented in a dialog", + @"primaryDemo" : @NO, + @"presentable" : @YES, + }; +} + +@end diff --git a/components/ProgressView/src/MDCProgressView.m b/components/ProgressView/src/MDCProgressView.m index 49e72661f7c..68513f1526d 100644 --- a/components/ProgressView/src/MDCProgressView.m +++ b/components/ProgressView/src/MDCProgressView.m @@ -445,6 +445,7 @@ - (void)startAnimatingBar { CAAnimationGroup *progressViewAnimationGroup = [[CAAnimationGroup alloc] init]; progressViewAnimationGroup.animations = @[ progressViewHead, progressViewTail ]; progressViewAnimationGroup.duration = 1.8; + progressViewAnimationGroup.removedOnCompletion = NO; progressViewAnimationGroup.repeatCount = HUGE_VALF; [self.progressView.shapeLayer addAnimation:progressViewAnimationGroup @@ -474,6 +475,7 @@ - (void)startAnimatingBar { indeterminateProgressViewAnimationGroup.animations = @[ indeterminateProgressViewHead, indeterminateProgressViewTail ]; indeterminateProgressViewAnimationGroup.duration = 1.8; + indeterminateProgressViewAnimationGroup.removedOnCompletion = NO; indeterminateProgressViewAnimationGroup.repeatCount = HUGE_VALF; [self.indeterminateProgressView.shapeLayer addAnimation:indeterminateProgressViewAnimationGroup From 1b69ff6f39089b1e8a842f11bdd9a7fb98bdb22b Mon Sep 17 00:00:00 2001 From: Ingerid Fosli Date: Tue, 14 Jul 2020 12:45:25 -0700 Subject: [PATCH 04/22] Update sizeThatFits calculation to include layout margins in the height calculation and ensure entire leading button can be clicked when trailing button is hidden, by constraining the buttonContainerView to always be at least as tall as the leading button. Note: With the update in the height calculation, the additional constraint seems to have no effect, and the banner's layout is correct either way. I prefer to keep it, however, as it'll ensure we don't get the same "button cannot be clicked" error in the future, at least not without breaking constraints. Happy to reconsider this, though! PiperOrigin-RevId: 321214409 --- components/Banner/examples/AppBarBannerExample.m | 1 + components/Banner/src/MDCBannerView.m | 9 ++++++++- .../Banner/tests/snapshot/MDCBannerSnapshotTests.m | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/components/Banner/examples/AppBarBannerExample.m b/components/Banner/examples/AppBarBannerExample.m index 634929254ce..73d1b0504b9 100644 --- a/components/Banner/examples/AppBarBannerExample.m +++ b/components/Banner/examples/AppBarBannerExample.m @@ -86,6 +86,7 @@ - (void)dismissBanner { - (void)showBanner { self.banner = [[MDCBannerView alloc] init]; [self.banner applyThemeWithScheme:_containerScheme]; + self.banner.layoutMargins = UIEdgeInsetsMake(0, 8, 0, 8); self.banner.textView.text = @"This banner has been set as bottomBar of this AppBar."; [self.banner.leadingButton setTitle:@"Dismiss" forState:UIControlStateNormal]; [self.banner.leadingButton addTarget:self diff --git a/components/Banner/src/MDCBannerView.m b/components/Banner/src/MDCBannerView.m index 389d44c72be..77122b4d5ff 100644 --- a/components/Banner/src/MDCBannerView.m +++ b/components/Banner/src/MDCBannerView.m @@ -73,6 +73,7 @@ @interface MDCBannerView () @property(nonatomic, readwrite, strong) NSLayoutConstraint *buttonContainerConstraintTopWithTextViewGreater; @property(nonatomic, readwrite, strong) NSLayoutConstraint *buttonContainerConstraintBottom; +@property(nonatomic, readwrite, strong) NSLayoutConstraint *buttonContainerConstraintHeight; @property(nonatomic, readwrite, strong) NSLayoutConstraint *leadingButtonConstraintLeading; @property(nonatomic, readwrite, strong) NSLayoutConstraint *leadingButtonConstraintTop; @@ -117,6 +118,7 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { - (void)commonBannerViewInit { self.backgroundColor = UIColor.whiteColor; _bannerViewLayoutStyle = MDCBannerViewLayoutStyleAutomatic; + self.layoutMargins = UIEdgeInsetsZero; // Create textView UITextView *textView = [[UITextView alloc] init]; @@ -258,6 +260,9 @@ - (void)setUpButtonContainerConstraints { self.buttonContainerConstraintTopWithTextViewGreater = [self.buttonContainerView.topAnchor constraintGreaterThanOrEqualToAnchor:self.textView.bottomAnchor constant:kVerticalSpaceBetweenButtonAndTextView]; + self.buttonContainerConstraintHeight = [self.buttonContainerView.heightAnchor + constraintGreaterThanOrEqualToAnchor:self.leadingButton.heightAnchor + constant:0]; } - (void)setUpButtonsConstraints { @@ -319,6 +324,7 @@ - (void)deactivateAllConstraints { self.buttonContainerConstraintTopWithTextView.active = NO; self.buttonContainerConstraintTopWithTextViewGreater.active = NO; self.buttonContainerConstraintBottom.active = NO; + self.buttonContainerConstraintHeight.active = NO; self.leadingButtonConstraintLeading.active = NO; self.leadingButtonConstraintTop.active = NO; self.leadingButtonConstraintTrailing.active = NO; @@ -345,7 +351,7 @@ - (void)setFrame:(CGRect)frame { - (CGSize)sizeThatFits:(CGSize)size { MDCBannerViewLayoutStyle layoutStyle = [self layoutStyleForSizeToFit:size]; - CGFloat frameHeight = 0.0f; + CGFloat frameHeight = self.layoutMargins.top + self.layoutMargins.bottom; CGSize contentSize = [self contentSizeForLayoutSize:size]; switch (layoutStyle) { case MDCBannerViewLayoutStyleSingleRow: { @@ -430,6 +436,7 @@ - (void)updateConstraintsWithLayoutStyle:(MDCBannerViewLayoutStyle)layoutStyle { } self.buttonContainerConstraintTrailing.active = YES; self.buttonContainerConstraintBottom.active = YES; + self.buttonContainerConstraintHeight.active = YES; if (layoutStyle == MDCBannerViewLayoutStyleSingleRow) { self.imageViewConstraintCenterY.active = YES; diff --git a/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m b/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m index 51a65f398e4..fa82cb57439 100644 --- a/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m +++ b/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m @@ -436,6 +436,7 @@ - (void)testDynamicTypeForContentSizeCategoryExtraExtraLarge { [[MDCTypographyScheme alloc] initWithDefaults:MDCTypographySchemeDefaultsMaterial201902]; // When + self.bannerView.layoutMargins = UIEdgeInsetsMake(0, 8, 0, 8); self.bannerView.textView.text = kBannerShortText; self.bannerView.textView.font = self.typographyScheme.body2; MDCButton *button = self.bannerView.leadingButton; @@ -460,6 +461,7 @@ - (void)testDynamicTypeForAttributedTextStringWhenContentSizeCategoryIsExtraExtr self.typographyScheme = [[MDCTypographyScheme alloc] initWithDefaults:MDCTypographySchemeDefaultsMaterial201902]; MDCButton *button = self.bannerView.leadingButton; + self.bannerView.layoutMargins = UIEdgeInsetsMake(0, 8, 0, 8); [button setTitle:@"Action" forState:UIControlStateNormal]; [button setTitleFont:self.typographyScheme.button forState:UIControlStateNormal]; button.uppercaseTitle = YES; From 3002e9a73f9b6c070495fc7f27d1cc183b8bdd4f Mon Sep 17 00:00:00 2001 From: Jan Philipp Sachse Date: Wed, 15 Jul 2020 02:00:25 -0700 Subject: [PATCH 05/22] Add UIMenu support to MDCButtonBar. This will keep the primary action and menu properties of the UIBarButtonItem and apply them to the button. If no primary action is set, the menu will be used as primary action automatically. PiperOrigin-RevId: 321320327 --- .../ButtonBar/examples/ButtonBarMenuExample.m | 176 ++++++++++++++++++ components/ButtonBar/src/MDCButtonBar.m | 32 +++- .../src/private/MDCAppBarButtonBarBuilder.m | 26 ++- 3 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 components/ButtonBar/examples/ButtonBarMenuExample.m diff --git a/components/ButtonBar/examples/ButtonBarMenuExample.m b/components/ButtonBar/examples/ButtonBarMenuExample.m new file mode 100644 index 00000000000..e9621a04ad9 --- /dev/null +++ b/components/ButtonBar/examples/ButtonBarMenuExample.m @@ -0,0 +1,176 @@ +// Copyright 2016-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 + +#import "MaterialAvailability.h" +#import "MaterialButtonBar.h" +#import "MaterialContainerScheme.h" + +@interface ButtonBarMenuExample : UIViewController +@property(nonatomic, strong) MDCSemanticColorScheme *colorScheme; +@property(nonatomic, strong) MDCTypographyScheme *typographyScheme; +@end + +@implementation ButtonBarMenuExample + +- (id)init { + self = [super init]; + if (self) { + self.colorScheme = + [[MDCSemanticColorScheme alloc] initWithDefaults:MDCColorSchemeDefaultsMaterial201804]; + self.typographyScheme = + [[MDCTypographyScheme alloc] initWithDefaults:MDCTypographySchemeDefaultsMaterial201804]; + self.title = @"Button Bar (Menu)"; + } + return self; +} + +- (MDCContainerScheme *)containerScheme { + MDCContainerScheme *scheme = [[MDCContainerScheme alloc] init]; + scheme.colorScheme = self.colorScheme; + scheme.typographyScheme = self.typographyScheme; + return scheme; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + MDCButtonBar *buttonBar = [[MDCButtonBar alloc] init]; + buttonBar.backgroundColor = self.colorScheme.primaryColor; + buttonBar.tintColor = self.colorScheme.onPrimaryColor; + [buttonBar setButtonsTitleFont:self.typographyScheme.button forState:UIControlStateNormal]; + + // MDCButtonBar ignores the style of UIBarButtonItem. + UIBarButtonItemStyle ignored = UIBarButtonItemStyleDone; + + UIBarButtonItem *menuAsSecondaryActionItem; + UIBarButtonItem *menuAsPrimaryActionItem; + UIBarButtonItem *changingActionItem; +#if MDC_AVAILABLE_SDK_IOS(14_0) + if (@available(iOS 14.0, *)) { + UIAction *primaryAction = [UIAction actionWithTitle:@"Menu on hold" + image:nil + identifier:nil + handler:^(__kindof UIAction *_Nonnull action) { + NSLog(@"Primary action was tapped."); + }]; + + UIMenu *secondaryMenu = [self exampleMenuWithTitle:@"A secondary action menu"]; + menuAsSecondaryActionItem = [[UIBarButtonItem alloc] initWithImage:nil menu:secondaryMenu]; + menuAsSecondaryActionItem.primaryAction = primaryAction; + + UIMenu *primaryMenu = [self exampleMenuWithTitle:@"A primary action menu"]; + menuAsPrimaryActionItem = [[UIBarButtonItem alloc] initWithImage:nil menu:primaryMenu]; + menuAsPrimaryActionItem.title = @"Menu on tap"; + + changingActionItem = + [[UIBarButtonItem alloc] initWithTitle:@"Change to menu" + style:ignored + target:self + action:@selector(didTapChangingActionItem:)]; + } else { + menuAsSecondaryActionItem = [[UIBarButtonItem alloc] initWithTitle:@"UIMenu" + style:ignored + target:nil + action:nil]; + menuAsPrimaryActionItem = [[UIBarButtonItem alloc] initWithTitle:@"only works" + style:ignored + target:nil + action:nil]; + changingActionItem = [[UIBarButtonItem alloc] initWithTitle:@"on iOS 14+" + style:ignored + target:nil + action:nil]; + } +#else + menuAsSecondaryActionItem = [[UIBarButtonItem alloc] initWithTitle:@"UIMenu" + style:ignored + target:nil + action:nil]; + menuAsPrimaryActionItem = [[UIBarButtonItem alloc] initWithTitle:@"only works" + style:ignored + target:nil + action:nil]; + changingActionItem = [[UIBarButtonItem alloc] initWithTitle:@"with Xcode 12+" + style:ignored + target:nil + action:nil]; +#endif + + buttonBar.items = @[ menuAsSecondaryActionItem, menuAsPrimaryActionItem, changingActionItem ]; + + // MDCButtonBar's sizeThatFits gives a "best-fit" size of the provided items. + CGSize size = [buttonBar sizeThatFits:self.view.bounds.size]; + CGFloat x = (self.view.bounds.size.width - size.width) / 2; + CGFloat y = self.view.bounds.size.height / 2 - size.height; + buttonBar.frame = CGRectMake(x, y, size.width, size.height); + buttonBar.autoresizingMask = + (UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | + UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin); + [self.view addSubview:buttonBar]; + + // Ensure that the controller's view isn't transparent. + self.view.backgroundColor = [UIColor colorWithWhite:(CGFloat)0.9 alpha:1]; +} + +#pragma mark - User actions + +- (void)didTapChangingActionItem:(id)sender { + if (![sender isKindOfClass:UIBarButtonItem.class]) return; + UIBarButtonItem *item = (UIBarButtonItem *)sender; +#if MDC_AVAILABLE_SDK_IOS(14_0) + if (@available(iOS 14.0, *)) { + item.menu = [self exampleMenuWithTitle:@"This menu was added after the first tap"]; + item.title = @"Menu on tap"; + } +#endif + NSLog(@"Did tap action item: %@", item); +} + +#pragma mark - Factory methods + +- (UIMenu *)exampleMenuWithTitle:(NSString *)title API_AVAILABLE(ios(14.0)) { + UIAction *firstAction = [UIAction actionWithTitle:@"An action" + image:nil + identifier:nil + handler:^(__kindof UIAction *_Nonnull action) { + NSLog(@"First element was tapped."); + }]; + UIAction *secondAction = [UIAction actionWithTitle:@"A second action" + image:nil + identifier:nil + handler:^(__kindof UIAction *_Nonnull action) { + NSLog(@"Second element was tapped."); + }]; + + NSArray *menuElements = @[ firstAction, secondAction ]; + return [UIMenu menuWithTitle:title children:menuElements]; +} + +@end + +@implementation ButtonBarMenuExample (CatalogByConvention) + ++ (NSDictionary *)catalogMetadata { + return @{ + @"breadcrumbs" : @[ @"Button Bar", @"Button Bar (Menu)" ], + @"description" : @"The Button Bar is a view that represents a list of UIBarButtonItems as " + @"horizontally-aligned buttons.", + @"primaryDemo" : @NO, + @"presentable" : @NO, + }; +} + +@end diff --git a/components/ButtonBar/src/MDCButtonBar.m b/components/ButtonBar/src/MDCButtonBar.m index d8b1b120c9a..fc72cf16340 100644 --- a/components/ButtonBar/src/MDCButtonBar.m +++ b/components/ButtonBar/src/MDCButtonBar.m @@ -16,6 +16,7 @@ #import +#import "MaterialAvailability.h" #import "MDCButtonBarDelegate.h" #import "MDCAppBarButtonBarBuilder.h" #import "MaterialButtons.h" @@ -313,7 +314,28 @@ - (void)observeValueForKeyPath:(NSString *)keyPath [self invalidateIntrinsicContentSize]; } - } else { + } +#if MDC_AVAILABLE_SDK_IOS(14_0) + else if (@available(iOS 14.0, *)) { + if ([keyPath isEqualToString:NSStringFromSelector(@selector(menu))]) { + if ([buttonView isKindOfClass:[UIButton class]]) { + ((UIButton *)buttonView).menu = newValue; + if (!self.items[itemIndex].primaryAction) { + ((UIButton *)buttonView).showsMenuAsPrimaryAction = YES; + } + } + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(primaryAction))]) { + // As of iOS 14.0 there is no public API to change the primary action of a button. + // It's only possible to provide the action upon initialization of the view, so all + // views get reloaded. + [self reloadButtonViews]; + } else { + NSLog(@"Unknown key path notification received by %@ for %@.", + NSStringFromClass([self class]), keyPath); + } + } +#endif + else { NSLog(@"Unknown key path notification received by %@ for %@.", NSStringFromClass([self class]), keyPath); } @@ -419,6 +441,14 @@ - (void)setItems:(NSArray *)items { NSStringFromSelector(@selector(image)), NSStringFromSelector(@selector(tag)), NSStringFromSelector(@selector(tintColor)), NSStringFromSelector(@selector(title)) ]; +#if MDC_AVAILABLE_SDK_IOS(14_0) + if (@available(iOS 14.0, *)) { + NSMutableArray *mutableKeyPaths = [keyPaths mutableCopy]; + [mutableKeyPaths addObject:NSStringFromSelector(@selector(menu))]; + [mutableKeyPaths addObject:NSStringFromSelector(@selector(primaryAction))]; + keyPaths = mutableKeyPaths; + } +#endif // Remove old observers for (UIBarButtonItem *item in _items) { diff --git a/components/ButtonBar/src/private/MDCAppBarButtonBarBuilder.m b/components/ButtonBar/src/private/MDCAppBarButtonBarBuilder.m index c6aa9ca6a3e..f68ebdf151f 100644 --- a/components/ButtonBar/src/private/MDCAppBarButtonBarBuilder.m +++ b/components/ButtonBar/src/private/MDCAppBarButtonBarBuilder.m @@ -17,8 +17,9 @@ #import #import -#import "MDCButtonBar+Private.h" +#import "MaterialAvailability.h" #import "MDCButtonBarButton.h" +#import "MDCButtonBar+Private.h" #import "MaterialButtons.h" // Additional insets for the left-most or right-most items. @@ -133,7 +134,17 @@ - (UIView *)buttonBar:(MDCButtonBar *)buttonBar NSStringFromClass([MDCButtonBar class])); #endif +#ifdef __IPHONE_14_0 + MDCButtonBarButton *button; + if (@available(iOS 14.0, *)) { + button = [MDCButtonBarButton buttonWithType:UIButtonTypeCustom + primaryAction:buttonItem.primaryAction]; + } else { + button = [[MDCButtonBarButton alloc] init]; + } +#else MDCButtonBarButton *button = [[MDCButtonBarButton alloc] init]; +#endif [button setBackgroundColor:[UIColor clearColor] forState:UIControlStateNormal]; button.disabledAlpha = kDisabledButtonAlpha; button.enableRippleBehavior = buttonBar.enableRippleBehavior; @@ -172,6 +183,19 @@ - (UIView *)buttonBar:(MDCButtonBar *)buttonBar [button addTarget:buttonBar action:@selector(didTapButton:event:) forControlEvents:UIControlEventTouchUpInside]; +#if MDC_AVAILABLE_SDK_IOS(14_0) + if (@available(iOS 14.0, *)) { + if (buttonItem.menu) { + // Setting the menu as primary action will result in the target / action pair not being + // called. Setting the primaryAction on a menu item will result in it not having a target / + // action pair anymore and not taking a new one on until primaryAction is cleared again. + button.menu = buttonItem.menu; + if (!buttonItem.primaryAction) { + button.showsMenuAsPrimaryAction = YES; + } + } + } +#endif UIEdgeInsets contentInsets = [MDCAppBarButtonBarBuilder contentInsetsForButton:button From 245cab36af7fa24f8371ba4ffae4d4a4b24cbfe2 Mon Sep 17 00:00:00 2001 From: Yarden Eitan Date: Wed, 15 Jul 2020 09:50:53 -0700 Subject: [PATCH 06/22] [Dragons] Add an iOS 14 expandable UICollectionView list configuration This leverages the new iOS 14 APIs that have a built in list configuration for UICollectionViews and a built-in expandable cells support. The new code is under iOS 14 and swift compiler availability flags. Screenshots: | Pre-iOS 14 | iOS 14 | | ------------- | ------------- | | ![Simulator Screen Shot - iPhone SE (2nd generation) - 2020-07-13 at 18 18 28](https://user-images.githubusercontent.com/4066863/87359155-4af91d80-c535-11ea-8bd6-ab359284399a.png) | ![Simulator Screen Shot - iPhone SE (2nd generation) - 2020-07-13 at 17 59 01](https://user-images.githubusercontent.com/4066863/87359220-606e4780-c535-11ea-9960-6d765af9c566.png) | | ![Simulator Screen Shot - iPhone SE (2nd generation) - 2020-07-13 at 18 05 58](https://user-images.githubusercontent.com/4066863/87359182-53e9ef00-c535-11ea-9816-4d0f53b971e8.png) | ![Simulator Screen Shot - iPhone SE (2nd generation) - 2020-07-13 at 17 59 20](https://user-images.githubusercontent.com/4066863/87359239-67955580-c535-11ea-81b7-d01183cbd4b6.png) | | ![Simulator Screen Shot - iPhone SE (2nd generation) - 2020-07-13 at 18 06 22](https://user-images.githubusercontent.com/4066863/87359195-59473980-c535-11ea-9f0f-4907fe171431.png) |![Simulator Screen Shot - iPhone SE (2nd generation) - 2020-07-13 at 17 59 12](https://user-images.githubusercontent.com/4066863/87359246-69f7af80-c535-11ea-8dee-7975a8a5648b.png) | Closes https://github.com/material-components/material-components-ios/pull/10037 COPYBARA_INTEGRATE_REVIEW=https://github.com/material-components/material-components-ios/pull/10037 from yarneo:ios14fun 5fca06581f3e0c339ab2267979cc6ab02ef6ee37 PiperOrigin-RevId: 321379559 --- catalog/MDCDragons/MDCDragonsController.swift | 238 ++++++++++++++---- 1 file changed, 194 insertions(+), 44 deletions(-) diff --git a/catalog/MDCDragons/MDCDragonsController.swift b/catalog/MDCDragons/MDCDragonsController.swift index 31dcf743979..e18ff4e192e 100644 --- a/catalog/MDCDragons/MDCDragonsController.swift +++ b/catalog/MDCDragons/MDCDragonsController.swift @@ -45,6 +45,7 @@ class MDCDragonsController: UIViewController, static let subtitleColor = UIColor(white: 0, alpha: 0.60) } fileprivate var cellsBySection: [[DragonCell]] + fileprivate var nodes: [CBCNode] fileprivate var searched: [DragonCell]! fileprivate var results: [DragonCell]! fileprivate var tableView: UITableView! @@ -56,6 +57,14 @@ class MDCDragonsController: UIViewController, var headerView: HeaderView! + enum Section { + case main + } + + @available(iOS 14.0, *) + private(set) lazy var dataSource: UICollectionViewDiffableDataSource! = nil + var collectionView: UICollectionView! = nil + init(node: CBCNode) { let filteredPresentable = node.children.filter { return $0.isPresentable() } let filteredDragons = Set(node.children).subtracting(filteredPresentable) @@ -64,6 +73,7 @@ class MDCDragonsController: UIViewController, filteredPresentable.map { DragonCell(node: $0) }, ] cellsBySection = cellsBySection.map { $0.sorted { $0.node.title < $1.node.title } } + nodes = node.children super.init(nibName: nil, bundle: nil) results = getLeafNodes(node: node) searched = results @@ -99,40 +109,46 @@ class MDCDragonsController: UIViewController, headerViewController.headerView.minMaxHeightIncludesSafeArea = false headerViewController.headerView.maximumHeight = Constants.headerViewMaxHeight headerViewController.headerView.minimumHeight = Constants.headerViewMinHeight - tableView = UITableView(frame: self.view.bounds, style: .grouped) - tableView.register( - MDCDragonsTableViewCell.self, - forCellReuseIdentifier: "MDCDragonsTableViewCell") - tableView.backgroundColor = Constants.bgColor - tableView.delegate = self - tableView.dataSource = self - tableView.rowHeight = UITableView.automaticDimension - tableView.estimatedRowHeight = 44 - view.addSubview(tableView) - view.backgroundColor = Constants.bgColor - if #available(iOS 11, *) { - tableView.translatesAutoresizingMaskIntoConstraints = false + view.backgroundColor = Constants.bgColor - let guide = view.safeAreaLayoutGuide - NSLayoutConstraint.activate([ - tableView.leftAnchor.constraint(equalTo: guide.leftAnchor), - tableView.rightAnchor.constraint(equalTo: guide.rightAnchor), - tableView.topAnchor.constraint(equalTo: view.topAnchor), - tableView.bottomAnchor.constraint(equalTo: guide.bottomAnchor), - ]) + if #available(iOS 14.0, *) { + #if compiler(>=5.3) + configureCollectionView() + configureDataSource() + #endif } else { - preiOS11Constraints() + tableView = UITableView(frame: self.view.bounds, style: .grouped) + tableView.register( + MDCDragonsTableViewCell.self, + forCellReuseIdentifier: "MDCDragonsTableViewCell") + tableView.backgroundColor = Constants.bgColor + tableView.delegate = self + tableView.dataSource = self + tableView.rowHeight = UITableView.automaticDimension + tableView.estimatedRowHeight = 44 + view.addSubview(tableView) + if #available(iOS 11, *) { + tableView.translatesAutoresizingMaskIntoConstraints = false + + let guide = view.safeAreaLayoutGuide + NSLayoutConstraint.activate([ + tableView.leftAnchor.constraint(equalTo: guide.leftAnchor), + tableView.rightAnchor.constraint(equalTo: guide.rightAnchor), + tableView.topAnchor.constraint(equalTo: view.topAnchor), + tableView.bottomAnchor.constraint(equalTo: guide.bottomAnchor), + ]) + + tableView.contentInsetAdjustmentBehavior = .always + } else { + preiOS11Constraints() + } } setupHeaderView() let tapgesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) tapgesture.delegate = self view.addGestureRecognizer(tapgesture) - - if #available(iOS 11.0, *) { - tableView.contentInsetAdjustmentBehavior = .always - } } func preiOS11Constraints() { @@ -148,7 +164,11 @@ class MDCDragonsController: UIViewController, headerViewController.headerView.addSubview(headerView) headerViewController.headerView.forwardTouchEvents(for: headerView) headerViewController.headerView.backgroundColor = Constants.headerColor - headerViewController.headerView.trackingScrollView = tableView + if #available(iOS 14.0, *) { + headerViewController.headerView.trackingScrollView = collectionView + } else { + headerViewController.headerView.trackingScrollView = tableView + } view.addSubview(headerViewController.view) headerViewController.didMove(toParent: self) } @@ -342,12 +362,13 @@ extension MDCDragonsController { return $0.node.title.range(of: searchText, options: .caseInsensitive) != nil } } - tableView.reloadData() + // load our initial data + refreshTable() } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searched = results - tableView.reloadData() + refreshTable() } func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { @@ -356,13 +377,23 @@ extension MDCDragonsController { func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { isSearchActive = true - tableView.reloadData() + refreshTable() } @objc func dismissKeyboard() { self.view.endEditing(true) isSearchActive = false - tableView.reloadData() + + } + + func refreshTable() { + if #available(iOS 14.0, *) { + #if compiler(>=5.3) + applySnapshot() + #endif + } else { + tableView.reloadData() + } } @objc(gestureRecognizer:shouldReceiveTouch:) @@ -370,8 +401,13 @@ extension MDCDragonsController { -> Bool { if gestureRecognizer is UITapGestureRecognizer { - let location = touch.location(in: tableView) - return (tableView.indexPathForRow(at: location) == nil) + if #available(iOS 14.0, *) { + let location = touch.location(in: collectionView) + return (collectionView.indexPathForItem(at: location) == nil) + } else { + let location = touch.location(in: tableView) + return (tableView.indexPathForRow(at: location) == nil) + } } return true } @@ -432,19 +468,133 @@ extension MDCDragonsController { } func updateScrollViewWithKeyboardNotificationUserInfo(userInfo: [AnyHashable: Any]) { - guard let endFrame = userInfo[AnyHashable("UIKeyboardFrameEndUserInfoKey")] as? CGRect - else { return } - let endKeyboardFrameOriginInWindow = view.convert(endFrame.origin, from: nil) - let tableViewMaxY = tableView.frame.maxY - let baseInset = tableViewMaxY - endKeyboardFrameOriginInWindow.y - let scrollIndicatorInset = baseInset - var contentInset = baseInset - if #available(iOS 11, *) { - if endKeyboardFrameOriginInWindow.y < tableViewMaxY { - contentInset -= view.safeAreaInsets.bottom + if #available(iOS 14.0, *) { + } else { + guard let endFrame = userInfo[AnyHashable("UIKeyboardFrameEndUserInfoKey")] as? CGRect + else { return } + let endKeyboardFrameOriginInWindow = view.convert(endFrame.origin, from: nil) + let tableViewMaxY = tableView.frame.maxY + let baseInset = tableViewMaxY - endKeyboardFrameOriginInWindow.y + let scrollIndicatorInset = baseInset + var contentInset = baseInset + if #available(iOS 11, *) { + if endKeyboardFrameOriginInWindow.y < tableViewMaxY { + contentInset -= view.safeAreaInsets.bottom + } + } + tableView.contentInset.bottom = contentInset + tableView.scrollIndicatorInsets.bottom = scrollIndicatorInset + } + } + + #if compiler(>=5.3) + @available(iOS 14.0, *) + func configureCollectionView() { + let collectionView = UICollectionView( + frame: view.bounds, collectionViewLayout: generateLayout()) + view.addSubview(collectionView) + collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth] + collectionView.backgroundColor = .white + self.collectionView = collectionView + collectionView.delegate = self + } + + @available(iOS 14.0, *) + func configureDataSource() { + let containerCellRegistration = + UICollectionView.CellRegistration< + UICollectionViewListCell, + CBCNode + > { (cell, indexPath, menuItem) in + // Populate the cell with our item description. + var contentConfiguration = cell.defaultContentConfiguration() + contentConfiguration.text = menuItem.title + contentConfiguration.textProperties.color = Constants.titleColor + cell.contentConfiguration = contentConfiguration + + let disclosureOptions = UICellAccessory.OutlineDisclosureOptions(style: .header) + cell.accessories = [.outlineDisclosure(options: disclosureOptions)] + cell.backgroundConfiguration = UIBackgroundConfiguration.clear() + } + + let cellRegistration = + UICollectionView.CellRegistration< + UICollectionViewListCell, + CBCNode + > { cell, indexPath, menuItem in + // Populate the cell with our item description. + var contentConfiguration = cell.defaultContentConfiguration() + contentConfiguration.text = menuItem.title + contentConfiguration.textProperties.color = Constants.subtitleColor + cell.contentConfiguration = contentConfiguration + cell.backgroundConfiguration = UIBackgroundConfiguration.clear() + } + + dataSource = + UICollectionViewDiffableDataSource(collectionView: collectionView) { + ( + collectionView: UICollectionView, + indexPath: IndexPath, + item: CBCNode + ) -> UICollectionViewCell? in + // Return the cell. + if item.children.isEmpty { + return collectionView.dequeueConfiguredReusableCell( + using: cellRegistration, + for: indexPath, + item: item) + } else { + return collectionView.dequeueConfiguredReusableCell( + using: containerCellRegistration, + for: indexPath, + item: item) + } + } + // load our initial data + applySnapshot(animatingDifferences: false) + } + + @available(iOS 14.0, *) + func generateLayout() -> UICollectionViewLayout { + let listConfiguration = UICollectionLayoutListConfiguration(appearance: .plain) + let layout = UICollectionViewCompositionalLayout.list(using: listConfiguration) + return layout + } + + @available(iOS 14.0, *) + func applySnapshot(animatingDifferences: Bool = true) { + let sectionSnapshot = + createSectionSnapshot(section: isSearchActive ? searched.map { $0.node } : nodes) + self.dataSource.apply(sectionSnapshot, to: .main, animatingDifferences: animatingDifferences) + } + + @available(iOS 14.0, *) + func createSectionSnapshot(section: [CBCNode]) -> NSDiffableDataSourceSectionSnapshot { + var snapshot = NSDiffableDataSourceSectionSnapshot() + + func addItems(_ menuItems: [CBCNode], to parent: CBCNode?) { + snapshot.append(menuItems, to: parent) + for menuItem in menuItems where !menuItem.children.isEmpty { + addItems(menuItem.children, to: menuItem) + } + } + + addItems(section, to: nil) + return snapshot + } + #endif + +} + +extension MDCDragonsController: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + if #available(iOS 14.0, *) { + guard let menuItem = self.dataSource.itemIdentifier(for: indexPath) else { return } + collectionView.deselectItem(at: indexPath, animated: true) + + if menuItem.isExample() || isSearchActive { + setupTransition(nodeData: DragonCell(node: menuItem)) } } - tableView.contentInset.bottom = contentInset - tableView.scrollIndicatorInsets.bottom = scrollIndicatorInset } } From 1bca00af2f0aaefaf8c6f80cfe659b8a990d9094 Mon Sep 17 00:00:00 2001 From: Andrew Overton Date: Wed, 15 Jul 2020 10:10:10 -0700 Subject: [PATCH 07/22] [ActionSheet] Rename ActionSheet assets PiperOrigin-RevId: 321383766 --- ...rance.png => actionsheet-default-appearance.png} | Bin ....png => actionsheet-left-outset-right-inset.png} | Bin ...ts.png => actionsheet-top-and-bottom-insets.png} | Bin 3 files changed, 0 insertions(+), 0 deletions(-) rename components/ActionSheet/docs/assets/{actionsheet-default appearance.png => actionsheet-default-appearance.png} (100%) rename components/ActionSheet/docs/assets/{actionsheet-left outset right inset.png => actionsheet-left-outset-right-inset.png} (100%) rename components/ActionSheet/docs/assets/{actionsheet-top and bottom insets.png => actionsheet-top-and-bottom-insets.png} (100%) diff --git a/components/ActionSheet/docs/assets/actionsheet-default appearance.png b/components/ActionSheet/docs/assets/actionsheet-default-appearance.png similarity index 100% rename from components/ActionSheet/docs/assets/actionsheet-default appearance.png rename to components/ActionSheet/docs/assets/actionsheet-default-appearance.png diff --git a/components/ActionSheet/docs/assets/actionsheet-left outset right inset.png b/components/ActionSheet/docs/assets/actionsheet-left-outset-right-inset.png similarity index 100% rename from components/ActionSheet/docs/assets/actionsheet-left outset right inset.png rename to components/ActionSheet/docs/assets/actionsheet-left-outset-right-inset.png diff --git a/components/ActionSheet/docs/assets/actionsheet-top and bottom insets.png b/components/ActionSheet/docs/assets/actionsheet-top-and-bottom-insets.png similarity index 100% rename from components/ActionSheet/docs/assets/actionsheet-top and bottom insets.png rename to components/ActionSheet/docs/assets/actionsheet-top-and-bottom-insets.png From 45c4130d1804c5324beda3aaf719a8d25af75f8d Mon Sep 17 00:00:00 2001 From: Yarden Eitan Date: Wed, 15 Jul 2020 10:18:34 -0700 Subject: [PATCH 08/22] [NavDrawer] Make sure to update color of scrim view background when trait collection changes its user interface style. PiperOrigin-RevId: 321385466 --- .../MDCBottomDrawerContainerViewController.m | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/components/NavigationDrawer/src/private/MDCBottomDrawerContainerViewController.m b/components/NavigationDrawer/src/private/MDCBottomDrawerContainerViewController.m index 5bc38c755df..7a4fb863093 100644 --- a/components/NavigationDrawer/src/private/MDCBottomDrawerContainerViewController.m +++ b/components/NavigationDrawer/src/private/MDCBottomDrawerContainerViewController.m @@ -496,19 +496,36 @@ - (void)setScrimShouldAdoptTrackingScrollViewBackgroundColor: scrimShouldAdoptTrackingScrollViewBackgroundColor) { _scrimShouldAdoptTrackingScrollViewBackgroundColor = scrimShouldAdoptTrackingScrollViewBackgroundColor; - if ([self.delegate respondsToSelector:@selector - (bottomDrawerContainerViewControllerNeedsScrimAppearanceUpdate: - scrimShouldAdoptTrackingScrollViewBackgroundColor:)]) { - [self.delegate bottomDrawerContainerViewControllerNeedsScrimAppearanceUpdate:self - scrimShouldAdoptTrackingScrollViewBackgroundColor: - _scrimShouldAdoptTrackingScrollViewBackgroundColor]; - } + [self updateScrimViewColor]; } self.shadowedView.layer.shadowColor = _scrimShouldAdoptTrackingScrollViewBackgroundColor ? UIColor.clearColor.CGColor : self.drawerShadowColor.CGColor; } +- (void)updateScrimViewColor { + if ([self.delegate respondsToSelector:@selector + (bottomDrawerContainerViewControllerNeedsScrimAppearanceUpdate: + scrimShouldAdoptTrackingScrollViewBackgroundColor:)]) { + [self.delegate bottomDrawerContainerViewControllerNeedsScrimAppearanceUpdate:self + scrimShouldAdoptTrackingScrollViewBackgroundColor: + _scrimShouldAdoptTrackingScrollViewBackgroundColor]; + } +} + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { + [super traitCollectionDidChange:previousTraitCollection]; + +#if defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) + if (@available(iOS 13.0, *)) { + if ([self.traitCollection + hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) { + [self updateScrimViewColor]; + } + } +#endif +} + - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { CGFloat drawerOffset = self.contentHeaderTopInset - self.topAreaInsetForHeader; CGFloat calculatedYContentOffset = From 40e108d6b3e005372ae3afe07c1b6ca29abe19d2 Mon Sep 17 00:00:00 2001 From: Andrew Overton Date: Wed, 15 Jul 2020 10:32:24 -0700 Subject: [PATCH 09/22] [Tabs] Add non fixed clustered centered layout style PiperOrigin-RevId: 321388615 --- ...DCTabBarViewTypicalExampleViewController.m | 10 ++ .../Tabs/src/TabBarView/MDCTabBarView.h | 6 + .../Tabs/src/TabBarView/MDCTabBarView.m | 125 +++++++++++++----- 3 files changed, 111 insertions(+), 30 deletions(-) diff --git a/components/Tabs/examples/MDCTabBarViewTypicalExampleViewController.m b/components/Tabs/examples/MDCTabBarViewTypicalExampleViewController.m index 560b027fa53..9a470bb58d0 100644 --- a/components/Tabs/examples/MDCTabBarViewTypicalExampleViewController.m +++ b/components/Tabs/examples/MDCTabBarViewTypicalExampleViewController.m @@ -417,12 +417,22 @@ - (void)didTapAlignmentButton { handler:^(MDCActionSheetAction *_Nonnull action) { self.tabBar.preferredLayoutStyle = MDCTabBarViewLayoutStyleScrollableCentered; }]; + MDCActionSheetAction *nonFixedClusteredCenteredAction = [MDCActionSheetAction + actionWithTitle:@"Non-Fixed Clustered Centered" + image:((currentStyle == MDCTabBarViewLayoutStyleNonFixedClusteredCentered) + ? checkIcon + : nil) + handler:^(MDCActionSheetAction *_Nonnull action) { + self.tabBar.preferredLayoutStyle = + MDCTabBarViewLayoutStyleNonFixedClusteredCentered; + }]; [actionSheet addAction:fixedJustifiedAction]; [actionSheet addAction:fixedClusteredLeadingAction]; [actionSheet addAction:fixedClusteredTrailingAction]; [actionSheet addAction:fixedClusteredCenteredAction]; [actionSheet addAction:scrollableAction]; [actionSheet addAction:scrollableCenteredAction]; + [actionSheet addAction:nonFixedClusteredCenteredAction]; [actionSheet applyThemeWithScheme:self.containerScheme]; actionSheet.alwaysAlignTitleLeadingEdges = YES; [self presentViewController:actionSheet animated:YES completion:nil]; diff --git a/components/Tabs/src/TabBarView/MDCTabBarView.h b/components/Tabs/src/TabBarView/MDCTabBarView.h index b27b243377b..bdf940639dd 100644 --- a/components/Tabs/src/TabBarView/MDCTabBarView.h +++ b/components/Tabs/src/TabBarView/MDCTabBarView.h @@ -52,6 +52,12 @@ typedef NS_ENUM(NSUInteger, MDCTabBarViewLayoutStyle) { The same as MDCTabBarViewLayoutStyleScrollable, but the selected tab is centered within the bar if its position in the scrollview's content area permits it.*/ MDCTabBarViewLayoutStyleScrollableCentered = 5, + + /** + Each item's width is based on its content. The items are arranged in the horizontal center of the + bar. + */ + MDCTabBarViewLayoutStyleNonFixedClusteredCentered = 6, }; /** diff --git a/components/Tabs/src/TabBarView/MDCTabBarView.m b/components/Tabs/src/TabBarView/MDCTabBarView.m index 67011a48f66..5ef7ee9ef79 100644 --- a/components/Tabs/src/TabBarView/MDCTabBarView.m +++ b/components/Tabs/src/TabBarView/MDCTabBarView.m @@ -24,9 +24,9 @@ #import "private/MDCTabBarViewPrivateIndicatorContext.h" #import -#import -#import #import +#import "MaterialAnimationTiming.h" // ComponentImport +#import // KVO contexts static char *const kKVOContextMDCTabBarView = "kKVOContextMDCTabBarView"; @@ -632,7 +632,11 @@ - (void)layoutSubviews { } case MDCTabBarViewLayoutStyleScrollableCentered: case MDCTabBarViewLayoutStyleScrollable: { - [self layoutSubviewsForScrollableLayout]; + [self layoutSubviewsForScrollableLayout:layoutStyle]; + break; + } + case MDCTabBarViewLayoutStyleNonFixedClusteredCentered: { + [self layoutSubviewsForNonFixedClusteredCentered]; break; } } @@ -672,14 +676,40 @@ - (BOOL)isScrollableLayoutStyle { } - (MDCTabBarViewLayoutStyle)effectiveLayoutStyle { + return [self effectiveLayoutStyleWithStyle:self.preferredLayoutStyle]; +} + +- (MDCTabBarViewLayoutStyle)effectiveLayoutStyleWithStyle:(MDCTabBarViewLayoutStyle)layoutStyle { if (self.items.count == 0) { return MDCTabBarViewLayoutStyleFixed; } CGSize availableSize = [self availableSizeForSubviewLayout]; - switch (self.preferredLayoutStyle) { + switch (layoutStyle) { + case MDCTabBarViewLayoutStyleNonFixedClusteredCentered: { + CGFloat nonFixedClusteredCenteredWidth = + [self intrinsicContentSizeForNonFixedLayoutStyle: + MDCTabBarViewLayoutStyleNonFixedClusteredCentered] + .width; + BOOL tabBarIsTooNarrow = availableSize.width < nonFixedClusteredCenteredWidth; + if (tabBarIsTooNarrow) { + return MDCTabBarViewLayoutStyleScrollable; + } else { + return MDCTabBarViewLayoutStyleNonFixedClusteredCentered; + } + } case MDCTabBarViewLayoutStyleScrollableCentered: { - return MDCTabBarViewLayoutStyleScrollableCentered; + CGFloat scrollableCenteredWidth = + [self + intrinsicContentSizeForNonFixedLayoutStyle:MDCTabBarViewLayoutStyleScrollableCentered] + .width; + BOOL tabBarIsTooWide = availableSize.width > scrollableCenteredWidth; + if (tabBarIsTooWide) { + return + [self effectiveLayoutStyleWithStyle:MDCTabBarViewLayoutStyleNonFixedClusteredCentered]; + } else { + return MDCTabBarViewLayoutStyleScrollableCentered; + } } case MDCTabBarViewLayoutStyleScrollable: { return MDCTabBarViewLayoutStyleScrollable; @@ -734,8 +764,7 @@ - (void)layoutSubviewsForJustifiedLayout { if (self.itemViews.count == 0) { return; } - BOOL isRTL = - self.mdf_effectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft; + BOOL isRTL = [self isRTL]; CGSize contentSize = [self availableSizeForSubviewLayout]; UIEdgeInsets contentPadding = [self contentPaddingForLayoutStyle:MDCTabBarViewLayoutStyleFixed]; @@ -758,12 +787,11 @@ - (void)layoutSubviewsForFixedClusteredLayout:(MDCTabBarViewLayoutStyle)layoutSt return; } - BOOL isRTL = - self.mdf_effectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft; + BOOL isRTL = [self isRTL]; UIEdgeInsets contentPadding = [self contentPaddingForLayoutStyle:layoutStyle]; CGSize contentSize = [self availableSizeForSubviewLayout]; - CGFloat itemViewWidth = [self estimatedItemViewSizeForClusteredFixedLayout].width; + CGFloat itemViewWidth = [self itemViewSizeForClusteredFixedLayout].width; CGFloat totalRequiredWidth = itemViewWidth * self.items.count; // Start-out assuming left-aligned because it requires no computation. CGFloat itemViewOriginX = isRTL ? contentPadding.right : contentPadding.left; @@ -791,17 +819,15 @@ - (void)layoutSubviewsForFixedClusteredLayout:(MDCTabBarViewLayoutStyle)layoutSt } } -- (void)layoutSubviewsForScrollableLayout { - BOOL isRTL = - self.mdf_effectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft; - UIEdgeInsets contentPadding = - [self contentPaddingForLayoutStyle:MDCTabBarViewLayoutStyleScrollable]; +- (void)layoutSubviewsForScrollableLayout:(MDCTabBarViewLayoutStyle)layoutStyle { + BOOL isRTL = [self isRTL]; + UIEdgeInsets contentPadding = [self contentPaddingForLayoutStyle:layoutStyle]; // Default for LTR CGFloat itemViewOriginX = contentPadding.left; if (isRTL) { itemViewOriginX = 0; - CGFloat requiredBarSize = [self intrinsicContentSizeForScrollableLayout].width; + CGFloat requiredBarSize = [self intrinsicContentSizeForNonFixedLayoutStyle:layoutStyle].width; CGFloat boundsBarDiff = [self availableSizeForSubviewLayout].width - requiredBarSize; if (boundsBarDiff > 0) { itemViewOriginX = boundsBarDiff; @@ -821,6 +847,33 @@ - (void)layoutSubviewsForScrollableLayout { } } +- (void)layoutSubviewsForNonFixedClusteredCentered { + UIEdgeInsets contentPadding = + [self contentPaddingForLayoutStyle:MDCTabBarViewLayoutStyleNonFixedClusteredCentered]; + BOOL isRTL = [self isRTL]; + if (isRTL) { + CGFloat right = contentPadding.right; + contentPadding.right = contentPadding.left; + contentPadding.left = right; + } + CGFloat availableSpaceMinX = contentPadding.left; + CGFloat availableSpaceMaxX = [self availableSizeForSubviewLayout].width - contentPadding.right; + CGFloat availableSpaceWidth = availableSpaceMaxX - availableSpaceMinX; + CGFloat centerOfAvailableSpace = availableSpaceMinX + availableSpaceWidth * 0.5f; + CGSize combineditemSize = [self nonFixedCombinedItemSize]; + CGFloat halfOfCombinedItemSizeWidth = combineditemSize.width * 0.5f; + CGFloat itemViewMinX = centerOfAvailableSpace - halfOfCombinedItemSizeWidth; + CGFloat itemViewMinY = contentPadding.top; + NSEnumerator *itemViewEnumerator = + isRTL ? [self.itemViews reverseObjectEnumerator] : [self.itemViews objectEnumerator]; + for (UIView *view in itemViewEnumerator) { + CGSize intrinsicContentSize = view.intrinsicContentSize; + view.frame = + CGRectMake(itemViewMinX, itemViewMinY, intrinsicContentSize.width, combineditemSize.height); + itemViewMinX += intrinsicContentSize.width; + } +} + - (void)willMoveToSuperview:(UIView *)newSuperview { [super willMoveToSuperview:newSuperview]; self.needsScrollToSelectedItem = YES; @@ -832,8 +885,9 @@ - (CGSize)intrinsicContentSize { return [self intrinsicContentSizeForJustifiedLayout]; } case MDCTabBarViewLayoutStyleScrollableCentered: + case MDCTabBarViewLayoutStyleNonFixedClusteredCentered: case MDCTabBarViewLayoutStyleScrollable: { - return [self intrinsicContentSizeForScrollableLayout]; + return [self intrinsicContentSizeForNonFixedLayoutStyle:self.preferredLayoutStyle]; } case MDCTabBarViewLayoutStyleFixedClusteredLeading: case MDCTabBarViewLayoutStyleFixedClusteredCentered: @@ -853,7 +907,8 @@ - (CGSize)intrinsicContentSize { For @c Scrollable: The intrinsic size size of the tabs. */ - (CGSize)calculatedContentSize { - switch ([self effectiveLayoutStyle]) { + MDCTabBarViewLayoutStyle layoutStyle = [self effectiveLayoutStyle]; + switch (layoutStyle) { case MDCTabBarViewLayoutStyleFixed: { CGSize intrinsicContentSize = [self intrinsicContentSizeForJustifiedLayout]; CGSize boundsSize = CGSizeMake(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)); @@ -867,8 +922,9 @@ - (CGSize)calculatedContentSize { return [self availableSizeForSubviewLayout]; } case MDCTabBarViewLayoutStyleScrollableCentered: + case MDCTabBarViewLayoutStyleNonFixedClusteredCentered: case MDCTabBarViewLayoutStyleScrollable: { - return [self intrinsicContentSizeForScrollableLayout]; + return [self intrinsicContentSizeForNonFixedLayoutStyle:layoutStyle]; } } } @@ -888,7 +944,7 @@ - (CGSize)intrinsicContentSizeForJustifiedLayout { return contentSize; } -- (CGSize)intrinsicContentSizeForScrollableLayout { +- (CGSize)nonFixedCombinedItemSize { CGFloat totalWidth = 0; CGFloat maxHeight = 0; for (UIView *itemView in self.itemViews) { @@ -899,8 +955,12 @@ - (CGSize)intrinsicContentSizeForScrollableLayout { totalWidth += contentSize.width; } CGSize contentSize = CGSizeMake(totalWidth, MAX(kMinHeight, maxHeight)); - UIEdgeInsets contentPadding = - [self contentPaddingForLayoutStyle:MDCTabBarViewLayoutStyleScrollable]; + return contentSize; +} + +- (CGSize)intrinsicContentSizeForNonFixedLayoutStyle:(MDCTabBarViewLayoutStyle)layoutStyle { + CGSize contentSize = [self nonFixedCombinedItemSize]; + UIEdgeInsets contentPadding = [self contentPaddingForLayoutStyle:layoutStyle]; contentSize = CGSizeMake(contentSize.width + contentPadding.left + contentPadding.right, contentSize.height + contentPadding.top + contentPadding.bottom); return contentSize; @@ -910,7 +970,7 @@ - (CGSize)intrinsicContentSizeForClusteredLayout:(MDCTabBarViewLayoutStyle)layou if (self.items.count == 0) { return CGSizeZero; } - CGSize estimatedItemSize = [self estimatedItemViewSizeForClusteredFixedLayout]; + CGSize estimatedItemSize = [self itemViewSizeForClusteredFixedLayout]; CGSize contentSize = CGSizeMake(estimatedItemSize.width * self.items.count, estimatedItemSize.height); UIEdgeInsets contentPadding = [self contentPaddingForLayoutStyle:layoutStyle]; @@ -926,6 +986,11 @@ - (CGSize)sizeThatFits:(CGSize)size { #pragma mark - Helpers +- (BOOL)isRTL { + return self.mdf_effectiveUserInterfaceLayoutDirection == + UIUserInterfaceLayoutDirectionRightToLeft; +} + - (void)scrollToItem:(UITabBarItem *)item animated:(BOOL)animated { NSUInteger index = [self.items indexOfObject:item]; if (index == NSNotFound || index >= self.itemViews.count) { @@ -970,8 +1035,7 @@ - (CGRect)estimatedFrameForItemAtIndex:(NSUInteger)index { return CGRectZero; } - BOOL isRTL = - self.mdf_effectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft; + BOOL isRTL = [self isRTL]; CGFloat originAdjustment = self.isScrollableLayoutStyle ? kScrollableTabsLeadingEdgeInset : 0; CGFloat viewOriginX = isRTL ? self.contentSize.width - originAdjustment : originAdjustment; @@ -1016,21 +1080,22 @@ - (CGSize)expectedSizeForView:(UIView *)view { CGSize contentSize = [self availableSizeForSubviewLayout]; return CGSizeMake(contentSize.width / self.itemViews.count, contentSize.height); } - return [self estimatedIntrinsicSizeForView:view]; + return [self intrinsicContentSizeForView:view]; } case MDCTabBarViewLayoutStyleFixedClusteredCentered: case MDCTabBarViewLayoutStyleFixedClusteredTrailing: case MDCTabBarViewLayoutStyleFixedClusteredLeading: { - return [self estimatedItemViewSizeForClusteredFixedLayout]; + return [self itemViewSizeForClusteredFixedLayout]; } + case MDCTabBarViewLayoutStyleNonFixedClusteredCentered: case MDCTabBarViewLayoutStyleScrollableCentered: case MDCTabBarViewLayoutStyleScrollable: { - return [self estimatedIntrinsicSizeForView:view]; + return [self intrinsicContentSizeForView:view]; } } } -- (CGSize)estimatedIntrinsicSizeForView:(UIView *)view { +- (CGSize)intrinsicContentSizeForView:(UIView *)view { CGSize expectedItemSize = view.intrinsicContentSize; if (expectedItemSize.width == UIViewNoIntrinsicMetric) { NSAssert(expectedItemSize.width != UIViewNoIntrinsicMetric, @@ -1040,7 +1105,7 @@ - (CGSize)estimatedIntrinsicSizeForView:(UIView *)view { return expectedItemSize; } -- (CGSize)estimatedItemViewSizeForClusteredFixedLayout { +- (CGSize)itemViewSizeForClusteredFixedLayout { CGFloat largestWidth = 0; CGFloat largestHeight = 0; for (UIView *view in self.itemViews) { From 742c7b3da7b51551110740f9b062d1a4eb7712e6 Mon Sep 17 00:00:00 2001 From: Andrew Overton Date: Wed, 15 Jul 2020 10:46:03 -0700 Subject: [PATCH 10/22] [ActionSheet] Delete MDCActionSheetThemer and MDCActionSheetScheme PiperOrigin-RevId: 321392126 --- MaterialComponentsBeta.podspec | 17 -- .../ActionSheetThemer/MDCActionSheetScheme.h | 59 ----- .../ActionSheetThemer/MDCActionSheetScheme.m | 29 --- .../ActionSheetThemer/MDCActionSheetThemer.h | 37 --- .../ActionSheetThemer/MDCActionSheetThemer.m | 30 --- .../MaterialActionSheet+ActionSheetThemer.h | 16 -- .../MDCActionSheetThemeTest.m | 217 ------------------ 7 files changed, 405 deletions(-) delete mode 100644 components/ActionSheet/src/ActionSheetThemer/MDCActionSheetScheme.h delete mode 100644 components/ActionSheet/src/ActionSheetThemer/MDCActionSheetScheme.m delete mode 100644 components/ActionSheet/src/ActionSheetThemer/MDCActionSheetThemer.h delete mode 100644 components/ActionSheet/src/ActionSheetThemer/MDCActionSheetThemer.m delete mode 100644 components/ActionSheet/src/ActionSheetThemer/MaterialActionSheet+ActionSheetThemer.h delete mode 100644 components/ActionSheet/tests/unit/ActionSheetThemer/MDCActionSheetThemeTest.m diff --git a/MaterialComponentsBeta.podspec b/MaterialComponentsBeta.podspec index f2fe67d5d1d..99bd80373cd 100644 --- a/MaterialComponentsBeta.podspec +++ b/MaterialComponentsBeta.podspec @@ -15,23 +15,6 @@ Pod::Spec.new do |mdc| # ActionSheet - mdc.subspec "ActionSheet+ActionSheetThemer" do |extension| - extension.ios.deployment_target = '9.0' - extension.public_header_files = "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/*.h" - extension.source_files = "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/*.{h,m}", "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/private/*.{h,m}" - extension.dependency "MaterialComponents/#{extension.base_name.split('+')[0]}" - extension.dependency "MaterialComponentsBeta/ActionSheet+ColorThemer" - extension.dependency "MaterialComponentsBeta/ActionSheet+TypographyThemer" - - extension.test_spec 'UnitTests' do |unit_tests| - unit_tests.source_files = [ - "components/#{extension.base_name.split('+')[0]}/tests/unit/#{extension.base_name.split('+')[1]}/*.{h,m,swift}", - "components/#{extension.base_name.split('+')[0]}/tests/unit/#{extension.base_name.split('+')[1]}/supplemental/*.{h,m,swift}", "components/#{extension.base_name.split('+')[0]}/tests/unit/MDCActionSheetTestHelper.*" - ] - unit_tests.resources = "components/#{extension.base_name.split('+')[0]}/tests/unit/#{extension.base_name.split('+')[1]}/resources/*" - end - end - mdc.subspec "ActionSheet+ColorThemer" do |extension| extension.ios.deployment_target = '9.0' extension.public_header_files = "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/*.h" diff --git a/components/ActionSheet/src/ActionSheetThemer/MDCActionSheetScheme.h b/components/ActionSheet/src/ActionSheetThemer/MDCActionSheetScheme.h deleted file mode 100644 index 29c133f1e6f..00000000000 --- a/components/ActionSheet/src/ActionSheetThemer/MDCActionSheetScheme.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018-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 -#import - -#import - -/** - MDCActionSheetScheming represents the design parameters for an MDCActionSheet. - - An instance of this protocol can be applied to an instance of MDCAction using the - MDCActionSheetThemer API. - */ -@protocol MDCActionSheetScheming - -/** - The color scheme to be applied to an action sheet. - */ -@property(nonnull, readonly, nonatomic) id colorScheme; - -/** - The typography scheme to be applied to an action sheet. - */ -@property(nonnull, readonly, nonatomic) id typographyScheme; - -@end - -/** - An MDCActionSheetScheme is a mutable representation of the design parameters for an MDCActionSheet. - */ -@interface MDCActionSheetScheme : NSObject - -/** - A mutable representation of a color scheme. - - By default, this is initialized with the latest color scheme defaults. - */ -@property(nonnull, readwrite, nonatomic) id colorScheme; - -/** - A mutable representation of a typography scheme. - - By default, this is initialized with the latest typography scheme defaults. - */ -@property(nonnull, readwrite, nonatomic) id typographyScheme; - -@end diff --git a/components/ActionSheet/src/ActionSheetThemer/MDCActionSheetScheme.m b/components/ActionSheet/src/ActionSheetThemer/MDCActionSheetScheme.m deleted file mode 100644 index b0be02e9a5c..00000000000 --- a/components/ActionSheet/src/ActionSheetThemer/MDCActionSheetScheme.m +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018-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 "MDCActionSheetScheme.h" - -@implementation MDCActionSheetScheme - -- (instancetype)init { - self = [super init]; - if (self) { - _colorScheme = - [[MDCSemanticColorScheme alloc] initWithDefaults:MDCColorSchemeDefaultsMaterial201804]; - _typographyScheme = [[MDCTypographyScheme alloc] init]; - } - return self; -} - -@end diff --git a/components/ActionSheet/src/ActionSheetThemer/MDCActionSheetThemer.h b/components/ActionSheet/src/ActionSheetThemer/MDCActionSheetThemer.h deleted file mode 100644 index c98f92aa56e..00000000000 --- a/components/ActionSheet/src/ActionSheetThemer/MDCActionSheetThemer.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2018-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 -#import - -#import "MDCActionSheetScheme.h" - -/** - The Material Design action sheet themer for instances of MDCActionSheet. - */ -@interface MDCActionSheetThemer : NSObject -@end - -@interface MDCActionSheetThemer (ToBeDeprecated) - -/** - Applies a action scheme's properties to an MDCActionSheet. - - @param scheme The action sheet scheme to apply to the component instance. - @param actionSheetController component instance to which the scheme should be applied. - */ -+ (void)applyScheme:(nonnull id)scheme - toActionSheetController:(nonnull MDCActionSheetController *)actionSheetController; - -@end diff --git a/components/ActionSheet/src/ActionSheetThemer/MDCActionSheetThemer.m b/components/ActionSheet/src/ActionSheetThemer/MDCActionSheetThemer.m deleted file mode 100644 index 47e6c218ca0..00000000000 --- a/components/ActionSheet/src/ActionSheetThemer/MDCActionSheetThemer.m +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018-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 "MDCActionSheetThemer.h" - -#import "MaterialActionSheet+ColorThemer.h" -#import "MaterialActionSheet+TypographyThemer.h" - -@implementation MDCActionSheetThemer - -+ (void)applyScheme:(id)scheme - toActionSheetController:(MDCActionSheetController *)actionSheetController { - [MDCActionSheetColorThemer applySemanticColorScheme:scheme.colorScheme - toActionSheetController:actionSheetController]; - [MDCActionSheetTypographyThemer applyTypographyScheme:scheme.typographyScheme - toActionSheetController:actionSheetController]; -} - -@end diff --git a/components/ActionSheet/src/ActionSheetThemer/MaterialActionSheet+ActionSheetThemer.h b/components/ActionSheet/src/ActionSheetThemer/MaterialActionSheet+ActionSheetThemer.h deleted file mode 100644 index 6ef0d4e64a4..00000000000 --- a/components/ActionSheet/src/ActionSheetThemer/MaterialActionSheet+ActionSheetThemer.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2018-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 "MDCActionSheetScheme.h" -#import "MDCActionSheetThemer.h" diff --git a/components/ActionSheet/tests/unit/ActionSheetThemer/MDCActionSheetThemeTest.m b/components/ActionSheet/tests/unit/ActionSheetThemer/MDCActionSheetThemeTest.m deleted file mode 100644 index 4d1e488fc90..00000000000 --- a/components/ActionSheet/tests/unit/ActionSheetThemer/MDCActionSheetThemeTest.m +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2018-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 - -#import "../../../src/private/MDCActionSheetHeaderView.h" -#import "../../../src/private/MDCActionSheetItemTableViewCell.h" -#import "../MDCActionSheetTestHelper.h" -#import "MaterialActionSheet+ActionSheetThemer.h" -#import "MaterialActionSheet+ColorThemer.h" -#import "MaterialActionSheet+TypographyThemer.h" - -static const CGFloat kHighAlpha = (CGFloat)0.87; -static const CGFloat kMediumAlpha = (CGFloat)0.6; - -@interface MDCActionSheetHeaderView (Testing) -@property(nonatomic, strong) UILabel *titleLabel; -@property(nonatomic, strong) UILabel *messageLabel; -@end - -@interface MDCActionSheetItemTableViewCell (Testing) -@property(nonatomic, strong) UILabel *actionLabel; -@property(nonatomic, strong) UIImageView *actionImageView; -@property(nonatomic, strong) MDCInkTouchController *inkTouchController; -@end - -@interface MDCActionSheetThemeTest : XCTestCase -@property(nonatomic, strong) MDCActionSheetController *actionSheet; -@property(nonatomic, strong) MDCSemanticColorScheme *colorScheme; -@property(nonatomic, strong) MDCTypographyScheme *typographyScheme; -@end - -@implementation MDCActionSheetThemeTest - -- (void)setUp { - [super setUp]; - - self.actionSheet = [[MDCActionSheetController alloc] init]; - self.colorScheme = - [[MDCSemanticColorScheme alloc] initWithDefaults:MDCColorSchemeDefaultsMaterial201804]; - UIColor *surface = UIColor.blueColor; - UIColor *onSurface = UIColor.redColor; - self.colorScheme.surfaceColor = surface; - self.colorScheme.onSurfaceColor = onSurface; - self.typographyScheme = [[MDCTypographyScheme alloc] init]; - self.typographyScheme.subtitle1 = [UIFont systemFontOfSize:12.0 weight:UIFontWeightBold]; - self.typographyScheme.body2 = [UIFont systemFontOfSize:10.0 weight:UIFontWeightLight]; -} - -- (void)tearDown { - self.typographyScheme = nil; - self.colorScheme = nil; - self.actionSheet = nil; - - [super tearDown]; -} - -#pragma mark - Scheme test - -- (void)testDefaultScheme { - // Given - MDCActionSheetScheme *defaultScheme = [[MDCActionSheetScheme alloc] init]; - MDCTypographyScheme *defaultTypographyScheme = [[MDCTypographyScheme alloc] init]; - MDCSemanticColorScheme *defaultColorScheme = - [[MDCSemanticColorScheme alloc] initWithDefaults:MDCColorSchemeDefaultsMaterial201804]; - - // Then - XCTAssertEqualObjects(defaultScheme.typographyScheme.subtitle1, - defaultTypographyScheme.subtitle1); - XCTAssertEqualObjects(defaultScheme.typographyScheme.body2, defaultTypographyScheme.body2); - XCTAssertEqualObjects(defaultScheme.colorScheme.surfaceColor, defaultColorScheme.surfaceColor); - XCTAssertEqualObjects(defaultScheme.colorScheme.onSurfaceColor, - defaultColorScheme.onSurfaceColor); -} - -- (void)testCustomColorSchemeAppliedToGlobalScheme { - // Given - MDCActionSheetScheme *scheme = [[MDCActionSheetScheme alloc] init]; - - // When - scheme.colorScheme = self.colorScheme; - - // Then - XCTAssertEqualObjects(scheme.colorScheme.surfaceColor, self.colorScheme.surfaceColor); - XCTAssertEqualObjects(scheme.colorScheme.onSurfaceColor, self.colorScheme.onSurfaceColor); -} - -- (void)testCustomTypographySchemeAppliedToGlobalScheme { - // Given - MDCActionSheetScheme *scheme = [[MDCActionSheetScheme alloc] init]; - - // When - scheme.typographyScheme = self.typographyScheme; - - // Then - XCTAssertEqualObjects(scheme.typographyScheme.subtitle1, self.typographyScheme.subtitle1); - XCTAssertEqualObjects(scheme.typographyScheme.body2, self.typographyScheme.body2); -} - -#pragma mark - Header test - -- (void)testApplyColorThemerWithTitleAndMessage { - // Given - self.actionSheet.title = @"Test title"; - self.actionSheet.message = @"Test message"; - - // When - [MDCActionSheetColorThemer applySemanticColorScheme:self.colorScheme - toActionSheetController:self.actionSheet]; - - // Then - XCTAssertEqualObjects(self.actionSheet.header.titleLabel.textColor, - [self.colorScheme.onSurfaceColor colorWithAlphaComponent:kHighAlpha]); - XCTAssertEqualObjects(self.actionSheet.header.messageLabel.textColor, - [self.colorScheme.onSurfaceColor colorWithAlphaComponent:kMediumAlpha]); -} - -- (void)testApplyThemerWithOnlyTitle { - // Given - self.actionSheet.title = @"Test title"; - - // When - [MDCActionSheetColorThemer applySemanticColorScheme:self.colorScheme - toActionSheetController:self.actionSheet]; - - // Then - XCTAssertEqualObjects(self.actionSheet.header.titleLabel.textColor, - [self.colorScheme.onSurfaceColor colorWithAlphaComponent:kMediumAlpha]); -} - -- (void)testApplyThemerWithOnlyMessage { - // Given - self.actionSheet.message = @"Test message"; - - // When - [MDCActionSheetColorThemer applySemanticColorScheme:self.colorScheme - toActionSheetController:self.actionSheet]; - - // Then - XCTAssertEqualObjects(self.actionSheet.header.messageLabel.textColor, - [self.colorScheme.onSurfaceColor colorWithAlphaComponent:kMediumAlpha]); -} - -#pragma mark - default test - -- (void)testApplyThemer { - // Given - MDCActionSheetController *tempActionSheet = [[MDCActionSheetController alloc] init]; - MDCActionSheetScheme *scheme = [[MDCActionSheetScheme alloc] init]; - scheme.colorScheme = self.colorScheme; - scheme.typographyScheme = self.typographyScheme; - - // When - [MDCActionSheetThemer applyScheme:scheme toActionSheetController:tempActionSheet]; - [MDCActionSheetColorThemer applySemanticColorScheme:self.colorScheme - toActionSheetController:self.actionSheet]; - [MDCActionSheetTypographyThemer applyTypographyScheme:self.typographyScheme - toActionSheetController:self.actionSheet]; - - // Then - XCTAssertEqualObjects(tempActionSheet.backgroundColor, self.actionSheet.backgroundColor); - XCTAssertEqualObjects(tempActionSheet.titleTextColor, self.actionSheet.titleTextColor); - XCTAssertEqualObjects(tempActionSheet.messageTextColor, self.actionSheet.messageTextColor); - XCTAssertEqualObjects(tempActionSheet.titleFont, self.actionSheet.titleFont); - XCTAssertEqualObjects(tempActionSheet.messageFont, self.actionSheet.messageFont); - XCTAssertEqualObjects(tempActionSheet.actionFont, self.actionSheet.actionFont); - XCTAssertEqualObjects(tempActionSheet.actionTintColor, self.actionSheet.actionTintColor); - XCTAssertEqualObjects(tempActionSheet.actionTextColor, self.actionSheet.actionTextColor); -} - -- (void)testApplyColorTheme { - // When - [MDCActionSheetColorThemer applySemanticColorScheme:self.colorScheme - toActionSheetController:self.actionSheet]; - NSArray *cells = [MDCActionSheetTestHelper getCellsFromActionSheet:self.actionSheet]; - - // Then - XCTAssertNotEqual(cells.count, 0U); - for (MDCActionSheetItemTableViewCell *cell in cells) { - XCTAssertEqualObjects(cell.actionImageView.tintColor, - [self.colorScheme.onSurfaceColor colorWithAlphaComponent:kMediumAlpha]); - XCTAssertEqualObjects(cell.actionLabel.textColor, - [self.colorScheme.onSurfaceColor colorWithAlphaComponent:kHighAlpha]); - } -} - -- (void)testApplyTypographyTheme { - // Given - self.actionSheet.title = @"Test title"; - self.actionSheet.message = @"Test message"; - - // When - [MDCActionSheetTypographyThemer applyTypographyScheme:self.typographyScheme - toActionSheetController:self.actionSheet]; - NSArray *cells = [MDCActionSheetTestHelper getCellsFromActionSheet:self.actionSheet]; - - // Then - XCTAssertEqualObjects(self.actionSheet.header.titleLabel.font, self.typographyScheme.subtitle1); - XCTAssertEqualObjects(self.actionSheet.header.messageLabel.font, self.typographyScheme.body2); - XCTAssertNotEqual(cells.count, 0U); - for (MDCActionSheetItemTableViewCell *cell in cells) { - XCTAssertEqualObjects(cell.actionLabel.font, self.typographyScheme.subtitle1); - } -} - -@end From e4805f0c2c439b8e0d1099021a5aadfcbc23b532 Mon Sep 17 00:00:00 2001 From: Andrew Overton Date: Wed, 15 Jul 2020 13:50:47 -0700 Subject: [PATCH 11/22] [ActionSheet] Delete MDCActionSheetColorThemer PiperOrigin-RevId: 321431638 --- MaterialComponentsBeta.podspec | 16 ------- components/ActionSheet/README.md | 45 ------------------- components/ActionSheet/docs/color-theming.md | 39 ---------------- .../ColorThemer/MDCActionSheetColorThemer.h | 36 --------------- .../ColorThemer/MDCActionSheetColorThemer.m | 45 ------------------- .../MaterialActionSheet+ColorThemer.h | 15 ------- .../MDCActionSheetColorThemerTest.swift | 36 --------------- 7 files changed, 232 deletions(-) delete mode 100644 components/ActionSheet/docs/color-theming.md delete mode 100644 components/ActionSheet/src/ColorThemer/MDCActionSheetColorThemer.h delete mode 100644 components/ActionSheet/src/ColorThemer/MDCActionSheetColorThemer.m delete mode 100644 components/ActionSheet/src/ColorThemer/MaterialActionSheet+ColorThemer.h delete mode 100644 components/ActionSheet/tests/unit/ColorThemer/MDCActionSheetColorThemerTest.swift diff --git a/MaterialComponentsBeta.podspec b/MaterialComponentsBeta.podspec index 99bd80373cd..7067bd3e6e0 100644 --- a/MaterialComponentsBeta.podspec +++ b/MaterialComponentsBeta.podspec @@ -15,22 +15,6 @@ Pod::Spec.new do |mdc| # ActionSheet - mdc.subspec "ActionSheet+ColorThemer" do |extension| - extension.ios.deployment_target = '9.0' - extension.public_header_files = "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/*.h" - extension.source_files = "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/*.{h,m}", "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/private/*.{h,m}" - extension.dependency "MaterialComponents/#{extension.base_name.split('+')[0]}" - extension.dependency "MaterialComponents/schemes/Color" - - extension.test_spec 'UnitTests' do |unit_tests| - unit_tests.source_files = [ - "components/#{extension.base_name.split('+')[0]}/tests/unit/#{extension.base_name.split('+')[1]}/*.{h,m,swift}", - "components/#{extension.base_name.split('+')[0]}/tests/unit/#{extension.base_name.split('+')[1]}/supplemental/*.{h,m,swift}" - ] - unit_tests.resources = "components/#{extension.base_name.split('+')[0]}/tests/unit/#{extension.base_name.split('+')[1]}/resources/*" - end - end - mdc.subspec "ActionSheet+TypographyThemer" do |extension| extension.ios.deployment_target = '9.0' extension.public_header_files = "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/*.h" diff --git a/components/ActionSheet/README.md b/components/ActionSheet/README.md index 4574baef949..dcdd72d51fc 100644 --- a/components/ActionSheet/README.md +++ b/components/ActionSheet/README.md @@ -46,7 +46,6 @@ the screen and displays actions a user can take. - [Set `-scrimAccessibilityHint`](#set-`-scrimaccessibilityhint`) - [Set `-scrimAccessibilityTraits`](#set-`-scrimaccessibilitytraits`) - [Unsupported](#unsupported) - - [Color Theming](#color-theming) - [Typography Theming](#typography-theming) - - - @@ -331,50 +330,6 @@ actionSheet.scrimAccessibilityTraits = UIAccessibilityTraitButton; ## Unsupported - - -### Color Theming - -You can theme an Action Sheet with your app's color scheme using the ColorThemer extension. - -You must first add the Color Themer extension to your project: - -```bash -pod `MaterialComponentsBeta/ActionSheet+ColorThemer` -``` - - -#### Swift -```swift -// Step 1: Import the ColorThemer extension -import MaterialComponentsBeta.MaterialActionSheet_ColorThemer - -// Step 2: Create or get a color scheme -let colorScheme = MDCSemanticColorScheme() - -// Step 3: Apply the color scheme to your component -let actionSheet = MDCActionSheetController() -MDCActionSheetColorThemer.applySemanticColorScheme(colorScheme, to: actionSheet) -``` - -#### Objective-C - -```objc -// Step 1: Import the ColorThemer extension -#import "MaterialActionSheet+ColorThemer.h" - -// Step 2: Create or get a color scheme -id colorScheme = [[MDCSematnicColorScheme alloc] init]; - -// Step 3: Apply the color scheme to your component -MDCActionSheetController *actionSheet = [[MDCActionSheetController alloc] init]; -[MDCActionSheetColorThemer applySemanticColorScheme:self.colorScheme - toActionSheetController:actionSheet]; -``` - - - - ### Typography Theming You can theme an Action Sheet with your app's typography scheme using the TypographyThemer extension. diff --git a/components/ActionSheet/docs/color-theming.md b/components/ActionSheet/docs/color-theming.md deleted file mode 100644 index f4c593b35bf..00000000000 --- a/components/ActionSheet/docs/color-theming.md +++ /dev/null @@ -1,39 +0,0 @@ -### Color Theming - -You can theme an Action Sheet with your app's color scheme using the ColorThemer extension. - -You must first add the Color Themer extension to your project: - -```bash -pod `MaterialComponentsBeta/ActionSheet+ColorThemer` -``` - - -#### Swift -```swift -// Step 1: Import the ColorThemer extension -import MaterialComponentsBeta.MaterialActionSheet_ColorThemer - -// Step 2: Create or get a color scheme -let colorScheme = MDCSemanticColorScheme() - -// Step 3: Apply the color scheme to your component -let actionSheet = MDCActionSheetController() -MDCActionSheetColorThemer.applySemanticColorScheme(colorScheme, to: actionSheet) -``` - -#### Objective-C - -```objc -// Step 1: Import the ColorThemer extension -#import "MaterialActionSheet+ColorThemer.h" - -// Step 2: Create or get a color scheme -id colorScheme = [[MDCSematnicColorScheme alloc] init]; - -// Step 3: Apply the color scheme to your component -MDCActionSheetController *actionSheet = [[MDCActionSheetController alloc] init]; -[MDCActionSheetColorThemer applySemanticColorScheme:self.colorScheme - toActionSheetController:actionSheet]; -``` - diff --git a/components/ActionSheet/src/ColorThemer/MDCActionSheetColorThemer.h b/components/ActionSheet/src/ColorThemer/MDCActionSheetColorThemer.h deleted file mode 100644 index 6ac000a0414..00000000000 --- a/components/ActionSheet/src/ColorThemer/MDCActionSheetColorThemer.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2018-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 -#import -#import - -/** - The Material Design color system's themer for instances of MDCActionSheetController. - */ -@interface MDCActionSheetColorThemer : NSObject -@end - -@interface MDCActionSheetColorThemer (ToBeDeprecated) - -/** - Applies a color scheme's properties to an MDCActionSheetController - - @param colorScheme The color scheme to apply to the component instance. - @param actionSheetController A component instance to which the olor scheme should be applied. - */ -+ (void)applySemanticColorScheme:(nonnull id)colorScheme - toActionSheetController:(nonnull MDCActionSheetController *)actionSheetController; - -@end diff --git a/components/ActionSheet/src/ColorThemer/MDCActionSheetColorThemer.m b/components/ActionSheet/src/ColorThemer/MDCActionSheetColorThemer.m deleted file mode 100644 index 10b3fa35996..00000000000 --- a/components/ActionSheet/src/ColorThemer/MDCActionSheetColorThemer.m +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018-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 "MDCActionSheetColorThemer.h" - -static const CGFloat kHighAlpha = (CGFloat)0.87; -static const CGFloat kMediumAlpha = (CGFloat)0.6; -static const CGFloat kInkAlpha = (CGFloat)0.16; - -@implementation MDCActionSheetColorThemer - -+ (void)applySemanticColorScheme:(id)colorScheme - toActionSheetController:(MDCActionSheetController *)actionSheetController { - actionSheetController.backgroundColor = colorScheme.surfaceColor; - if (actionSheetController.message && ![actionSheetController.message isEqualToString:@""]) { - // If there is a message then this can be high opacity and won't clash with actions. - actionSheetController.titleTextColor = - [colorScheme.onSurfaceColor colorWithAlphaComponent:kHighAlpha]; - } else { - actionSheetController.titleTextColor = - [colorScheme.onSurfaceColor colorWithAlphaComponent:kMediumAlpha]; - } - actionSheetController.messageTextColor = - [colorScheme.onSurfaceColor colorWithAlphaComponent:kMediumAlpha]; - actionSheetController.imageRenderingMode = UIImageRenderingModeAlwaysTemplate; - actionSheetController.actionTintColor = - [colorScheme.onSurfaceColor colorWithAlphaComponent:kMediumAlpha]; - actionSheetController.actionTextColor = - [colorScheme.onSurfaceColor colorWithAlphaComponent:kHighAlpha]; - actionSheetController.rippleColor = - [colorScheme.onSurfaceColor colorWithAlphaComponent:kInkAlpha]; -} - -@end diff --git a/components/ActionSheet/src/ColorThemer/MaterialActionSheet+ColorThemer.h b/components/ActionSheet/src/ColorThemer/MaterialActionSheet+ColorThemer.h deleted file mode 100644 index 509da44bae4..00000000000 --- a/components/ActionSheet/src/ColorThemer/MaterialActionSheet+ColorThemer.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2018-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 "MDCActionSheetColorThemer.h" diff --git a/components/ActionSheet/tests/unit/ColorThemer/MDCActionSheetColorThemerTest.swift b/components/ActionSheet/tests/unit/ColorThemer/MDCActionSheetColorThemerTest.swift deleted file mode 100644 index eb1f46c90c9..00000000000 --- a/components/ActionSheet/tests/unit/ColorThemer/MDCActionSheetColorThemerTest.swift +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2019-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 XCTest -import MaterialComponents.MaterialActionSheet -import MaterialComponentsBeta.MaterialActionSheet_ColorThemer - -class ActionSheetColorThemerTest: XCTestCase { - func testApplyThemerToBackgroundColor() { - // Given - let actionSheet = MDCActionSheetController() - let surfaceColor: UIColor = .blue - let colorScheme = MDCSemanticColorScheme(defaults: .material201804) - colorScheme.surfaceColor = surfaceColor - - // When - MDCActionSheetColorThemer.applySemanticColorScheme(colorScheme, to: actionSheet) - let _ = actionSheet.view - - // Then - XCTAssertEqual(actionSheet.view.backgroundColor, surfaceColor) - XCTAssertEqual(actionSheet.backgroundColor, surfaceColor) - } -} - From eb324d0394c0c0ee474551ac63650d617f5646a5 Mon Sep 17 00:00:00 2001 From: Alyssa Weiss Date: Wed, 15 Jul 2020 14:17:47 -0700 Subject: [PATCH 12/22] Internal change PiperOrigin-RevId: 321437366 --- .../src/private/MDCBottomAppBarLayer.m | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/components/BottomAppBar/src/private/MDCBottomAppBarLayer.m b/components/BottomAppBar/src/private/MDCBottomAppBarLayer.m index b5f52a4841a..eebec660314 100644 --- a/components/BottomAppBar/src/private/MDCBottomAppBarLayer.m +++ b/components/BottomAppBar/src/private/MDCBottomAppBarLayer.m @@ -114,20 +114,27 @@ - (UIBezierPath *)drawWithPathToCut:(UIBezierPath *)bottomBarPath [bottomBarPath addLineToPoint:CGPointMake(0, height * 2 + yOffset)]; [bottomBarPath addLineToPoint:CGPointMake(0, yOffset)]; - [bottomBarPath addLineToPoint:CGPointMake(arcCenter1.x - arcRadius, yOffset)]; + + CGFloat xOffset = arcCenter1.y == yOffset + ? arcRadius + : sqrt(pow(arcRadius, 2) - pow(yOffset - arcCenter1.y, 2)); + [bottomBarPath addLineToPoint:CGPointMake(arcCenter1.x - xOffset, yOffset)]; + + CGFloat angleOffset1 = asin((yOffset - arcCenter1.y) / arcRadius); [bottomBarPath addArcWithCenter:arcCenter1 radius:arcRadius - startAngle:-180 * (float)M_PI / 180 - endAngle:-270 * (float)M_PI / 180 + startAngle:(180.0 * (float)M_PI / 180.0) - angleOffset1 + endAngle:90.0 * (float)M_PI / 180.0 clockwise:NO]; - [bottomBarPath addLineToPoint:CGPointMake(arcCenter2.x, arcRadius + yOffset)]; + [bottomBarPath addLineToPoint:CGPointMake(arcCenter2.x, arcRadius + arcCenter2.y)]; + CGFloat angleOffset2 = asin((yOffset - arcCenter2.y) / arcRadius); [bottomBarPath addArcWithCenter:arcCenter2 radius:arcRadius - startAngle:-270 * (float)M_PI / 180 - endAngle:-360 * (float)M_PI / 180 + startAngle:90.0 * (float)M_PI / 180.0 + endAngle:angleOffset2 clockwise:NO]; [bottomBarPath addLineToPoint:CGPointMake(width, yOffset)]; @@ -153,19 +160,23 @@ - (UIBezierPath *)drawWithPlainPath:(UIBezierPath *)bottomBarPath [bottomBarPath addLineToPoint:CGPointMake(0, yOffset)]; [bottomBarPath addLineToPoint:CGPointMake(arcCenter1.x - arcRadius, yOffset)]; + CGFloat angleOffset1 = arcCenter1.y == yOffset ? 0 : asin((yOffset - arcCenter1.y) / arcRadius); + // draw circle with radius 0 to ensure we have the same number of points as the cut-out path - [bottomBarPath addArcWithCenter:arcCenter1 + [bottomBarPath addArcWithCenter:CGPointMake(arcCenter1.x, yOffset) radius:0 - startAngle:-180 * (float)M_PI / 180 - endAngle:-270 * (float)M_PI / 180 + startAngle:(180.0 * (float)M_PI / 180.0) - angleOffset1 + endAngle:90.0 * (float)M_PI / 180.0 clockwise:NO]; [bottomBarPath addLineToPoint:CGPointMake(arcCenter2.x, yOffset)]; + CGFloat angleOffset2 = arcCenter2.y == yOffset ? 0 : asin((yOffset - arcCenter2.y) / arcRadius); + // draw circle with radius 0 to ensure we have the same number of points as the cut-out path - [bottomBarPath addArcWithCenter:arcCenter2 + [bottomBarPath addArcWithCenter:CGPointMake(arcCenter2.x, yOffset) radius:0 - startAngle:-270 * (float)M_PI / 180 - endAngle:-360 * (float)M_PI / 180 + startAngle:90.0 * (float)M_PI / 180.0 + endAngle:angleOffset2 clockwise:NO]; [bottomBarPath addLineToPoint:CGPointMake(width, yOffset)]; From a5d83de736d27458c42e3c76a4208e2e40704d53 Mon Sep 17 00:00:00 2001 From: Andrew Overton Date: Thu, 16 Jul 2020 10:11:28 -0700 Subject: [PATCH 13/22] [ActionSheet] Delete MDCActionSheetTypographyThemer PiperOrigin-RevId: 321590607 --- MaterialComponentsBeta.podspec | 11 ----- components/ActionSheet/README.md | 46 ------------------- components/ActionSheet/docs/README.md | 5 -- .../ActionSheet/docs/typography-theming.md | 39 ---------------- .../MDCActionSheetTypographyThemer.h | 36 --------------- .../MDCActionSheetTypographyThemer.m | 26 ----------- .../MaterialActionSheet+TypographyThemer.h | 15 ------ 7 files changed, 178 deletions(-) delete mode 100644 components/ActionSheet/docs/typography-theming.md delete mode 100644 components/ActionSheet/src/TypographyThemer/MDCActionSheetTypographyThemer.h delete mode 100644 components/ActionSheet/src/TypographyThemer/MDCActionSheetTypographyThemer.m delete mode 100644 components/ActionSheet/src/TypographyThemer/MaterialActionSheet+TypographyThemer.h diff --git a/MaterialComponentsBeta.podspec b/MaterialComponentsBeta.podspec index 7067bd3e6e0..d6341c15a28 100644 --- a/MaterialComponentsBeta.podspec +++ b/MaterialComponentsBeta.podspec @@ -12,17 +12,6 @@ Pod::Spec.new do |mdc| # See MaterialComponents.podspec for the subspec structure and template. - - # ActionSheet - - mdc.subspec "ActionSheet+TypographyThemer" do |extension| - extension.ios.deployment_target = '9.0' - extension.public_header_files = "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/*.h" - extension.source_files = "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/*.{h,m}", "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/private/*.{h,m}" - extension.dependency "MaterialComponents/#{extension.base_name.split('+')[0]}" - extension.dependency "MaterialComponents/schemes/Typography" - end - mdc.subspec "BottomNavigation" do |component| component.ios.deployment_target = '9.0' component.public_header_files = "components/#{component.base_name}/src/MDCBottomNavigationBarController.h", "components/#{component.base_name}/src/MaterialBottomNavigationBeta.h" diff --git a/components/ActionSheet/README.md b/components/ActionSheet/README.md index dcdd72d51fc..64f86dc0358 100644 --- a/components/ActionSheet/README.md +++ b/components/ActionSheet/README.md @@ -45,8 +45,6 @@ the screen and displays actions a user can take. - [Set `-scrimAccessibilityLabel`](#set-`-scrimaccessibilitylabel`) - [Set `-scrimAccessibilityHint`](#set-`-scrimaccessibilityhint`) - [Set `-scrimAccessibilityTraits`](#set-`-scrimaccessibilitytraits`) -- [Unsupported](#unsupported) - - [Typography Theming](#typography-theming) - - - @@ -326,47 +324,3 @@ MDCActionSheetController *actionSheet = [MDCActionSheetController alloc] init]; actionSheet.scrimAccessibilityTraits = UIAccessibilityTraitButton; ``` - - -## Unsupported - -### Typography Theming - -You can theme an Action Sheet with your app's typography scheme using the TypographyThemer extension. - -You must first add the Typography Themer extension to your project: - -```bash -pod `MaterialComponentsBeta/ActionSheet+TypographyThemer` -``` - - -#### Swift -```swift -// Step 1: Import the ColorThemer extension -import MaterialComponentsBeta.MaterialActionSheet_TypographyThemer - -// Step 2: Create or get a color scheme -let typographyScheme = MDCTypographyScheme() - -// Step 3: Apply the color scheme to your component -let actionSheet = MDCActionSheetController() -MDCActionSheetTypographyThemer.applyTypographyScheme(typographyScheme, to: actionSheet) -``` - -#### Objective-C - -```objc -// Step 1: Import the ColorThemer extension -#import "MaterialActionSheet+TypographyThemer.h" - -// Step 2: Create or get a color scheme -id typographyScheme = [[MDCTypographyScheme alloc] init]; - -// Step 3: Apply the color scheme to your component -MDCActionSheetController *actionSheet = [[MDCActionSheetController alloc] init]; -[MDCActionSheetTypographyThemer applyTypographyScheme:self.typographyScheme - toActionSheetController:actionSheet]; -``` - - diff --git a/components/ActionSheet/docs/README.md b/components/ActionSheet/docs/README.md index 15bd0882644..f0edbf2366c 100644 --- a/components/ActionSheet/docs/README.md +++ b/components/ActionSheet/docs/README.md @@ -60,8 +60,3 @@ Material UIAlertController please see the `MDCAlertController` class. - [Theming](theming.md) - [Accessibility](accessibility.md) - -## Unsupported - -- [Color Theming](color-theming.md) -- [Typography Theming](typography-theming.md) diff --git a/components/ActionSheet/docs/typography-theming.md b/components/ActionSheet/docs/typography-theming.md deleted file mode 100644 index 0cb3e94bcdc..00000000000 --- a/components/ActionSheet/docs/typography-theming.md +++ /dev/null @@ -1,39 +0,0 @@ -### Typography Theming - -You can theme an Action Sheet with your app's typography scheme using the TypographyThemer extension. - -You must first add the Typography Themer extension to your project: - -```bash -pod `MaterialComponentsBeta/ActionSheet+TypographyThemer` -``` - - -#### Swift -```swift -// Step 1: Import the ColorThemer extension -import MaterialComponentsBeta.MaterialActionSheet_TypographyThemer - -// Step 2: Create or get a color scheme -let typographyScheme = MDCTypographyScheme() - -// Step 3: Apply the color scheme to your component -let actionSheet = MDCActionSheetController() -MDCActionSheetTypographyThemer.applyTypographyScheme(typographyScheme, to: actionSheet) -``` - -#### Objective-C - -```objc -// Step 1: Import the ColorThemer extension -#import "MaterialActionSheet+TypographyThemer.h" - -// Step 2: Create or get a color scheme -id typographyScheme = [[MDCTypographyScheme alloc] init]; - -// Step 3: Apply the color scheme to your component -MDCActionSheetController *actionSheet = [[MDCActionSheetController alloc] init]; -[MDCActionSheetTypographyThemer applyTypographyScheme:self.typographyScheme - toActionSheetController:actionSheet]; -``` - diff --git a/components/ActionSheet/src/TypographyThemer/MDCActionSheetTypographyThemer.h b/components/ActionSheet/src/TypographyThemer/MDCActionSheetTypographyThemer.h deleted file mode 100644 index afcb6819678..00000000000 --- a/components/ActionSheet/src/TypographyThemer/MDCActionSheetTypographyThemer.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2018-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 -#import -#import - -/** - The Material Design typography system's themer for instances of MDCActionSheetController. - */ -@interface MDCActionSheetTypographyThemer : NSObject -@end - -@interface MDCActionSheetTypographyThemer (ToBeDeprecated) - -/** - Applies a typography scheme's properties to an MDCActionSheetController. - - @param typographyScheme The typography scheme to apply to the component instance. - @param actionSheetController A component instance to which the typography scheme should be applied. - */ -+ (void)applyTypographyScheme:(nonnull id)typographyScheme - toActionSheetController:(nonnull MDCActionSheetController *)actionSheetController; - -@end diff --git a/components/ActionSheet/src/TypographyThemer/MDCActionSheetTypographyThemer.m b/components/ActionSheet/src/TypographyThemer/MDCActionSheetTypographyThemer.m deleted file mode 100644 index 9abc747cb7e..00000000000 --- a/components/ActionSheet/src/TypographyThemer/MDCActionSheetTypographyThemer.m +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018-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 "MDCActionSheetTypographyThemer.h" - -@implementation MDCActionSheetTypographyThemer - -+ (void)applyTypographyScheme:(id)typographyScheme - toActionSheetController:(MDCActionSheetController *)actionSheetController { - actionSheetController.titleFont = typographyScheme.subtitle1; - actionSheetController.messageFont = typographyScheme.body2; - actionSheetController.actionFont = typographyScheme.subtitle1; -} - -@end diff --git a/components/ActionSheet/src/TypographyThemer/MaterialActionSheet+TypographyThemer.h b/components/ActionSheet/src/TypographyThemer/MaterialActionSheet+TypographyThemer.h deleted file mode 100644 index 56bcc44bc6b..00000000000 --- a/components/ActionSheet/src/TypographyThemer/MaterialActionSheet+TypographyThemer.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2018-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 "MDCActionSheetTypographyThemer.h" From c09fcd9f9df8bfc6cc7e8dfc077fff70f7b3531c Mon Sep 17 00:00:00 2001 From: Andrew Overton Date: Thu, 16 Jul 2020 11:35:55 -0700 Subject: [PATCH 14/22] [MDC-iOS] Fix Podfile PiperOrigin-RevId: 321610318 --- catalog/Podfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/catalog/Podfile b/catalog/Podfile index 45f0d11b8e6..a9175edef7b 100644 --- a/catalog/Podfile +++ b/catalog/Podfile @@ -86,8 +86,6 @@ target "MDCCatalog" do 'Typography/UnitTests', ] pod 'MaterialComponentsBeta', :path => '../', :testspecs => [ - 'ActionSheet+ActionSheetThemer/UnitTests', - 'ActionSheet+ColorThemer/UnitTests', 'BottomNavigation/UnitTests', 'Tabs+TabBarView/UnitTests', ] From f2dd90e06cca99d66d4e33c99fad0d13180b1e04 Mon Sep 17 00:00:00 2001 From: Bryan Oltman Date: Thu, 16 Jul 2020 17:41:03 -0700 Subject: [PATCH 15/22] [Chips] Update inter-chip spacing in chip field to align with Material spec (8dp vertical and horizontal inter-chip spacing) See "Chips in groups" at https://material.io/components/chips#specs for reference. PiperOrigin-RevId: 321682695 --- components/Chips/src/MDCChipField.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/Chips/src/MDCChipField.m b/components/Chips/src/MDCChipField.m index 1b29a549616..f78b633ad82 100644 --- a/components/Chips/src/MDCChipField.m +++ b/components/Chips/src/MDCChipField.m @@ -26,8 +26,8 @@ static const CGFloat MDCChipFieldHorizontalInset = 15; static const CGFloat MDCChipFieldVerticalInset = 8; static const CGFloat MDCChipFieldIndent = 4; -static const CGFloat MDCChipFieldHorizontalMargin = 4; -static const CGFloat MDCChipFieldVerticalMargin = 5; +static const CGFloat MDCChipFieldHorizontalMargin = 8; +static const CGFloat MDCChipFieldVerticalMargin = 8; static const CGFloat MDCChipFieldClearButtonSquareWidthHeight = 24; static const CGFloat MDCChipFieldClearImageSquareWidthHeight = 18; static const UIKeyboardType MDCChipFieldDefaultKeyboardType = UIKeyboardTypeEmailAddress; From c1bc33cc4219ca4c4586316f87e8bf64d10972d0 Mon Sep 17 00:00:00 2001 From: Wenyu Zhang Date: Fri, 17 Jul 2020 10:20:56 -0700 Subject: [PATCH 16/22] [Banner] Add Banner snapshots for long text with no action style. PiperOrigin-RevId: 321803409 --- .../tests/snapshot/MDCBannerSnapshotTests.m | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m b/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m index fa82cb57439..1ddff3f17d6 100644 --- a/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m +++ b/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m @@ -201,6 +201,29 @@ - (void)testShortTextWithSingleActionRTLInArabic { [self generateSnapshotAndVerifyForView:self.bannerView]; } +- (void)testLongTextWithNoActionLTR { + // When + self.bannerView.textView.text = kBannerLongText; + self.bannerView.imageView.hidden = YES; + self.bannerView.leadingButton.hidden = YES; + self.bannerView.trailingButton.hidden = YES; + + // Then + [self generateSnapshotAndVerifyForView:self.bannerView]; +} + +- (void)testLongTextWithNoActionRTL { + // When + self.bannerView.textView.text = kBannerLongText; + self.bannerView.imageView.hidden = YES; + self.bannerView.leadingButton.hidden = YES; + self.bannerView.trailingButton.hidden = YES; + [self changeViewToRTL:self.bannerView]; + + // Then + [self generateSnapshotAndVerifyForView:self.bannerView]; +} + - (void)testLongTextWithSingleActionLTR { // When self.bannerView.textView.text = kBannerLongText; From 4216eb6ed560b65e7e5903d12da072a1f322b30d Mon Sep 17 00:00:00 2001 From: Wenyu Zhang Date: Fri, 17 Jul 2020 10:24:39 -0700 Subject: [PATCH 17/22] [Banner] Add height constraints to leadingButton and trailingButton on MDCBannerView to handle the case where they are hidden. PiperOrigin-RevId: 321804124 --- components/Banner/src/MDCBannerView.m | 32 ++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/components/Banner/src/MDCBannerView.m b/components/Banner/src/MDCBannerView.m index 77122b4d5ff..5cf2e20598c 100644 --- a/components/Banner/src/MDCBannerView.m +++ b/components/Banner/src/MDCBannerView.m @@ -83,9 +83,12 @@ @interface MDCBannerView () NSLayoutConstraint *leadingButtonConstraintBaseLineWithTrailingButton; @property(nonatomic, readwrite, strong) NSLayoutConstraint *leadingButtonConstraintTrailingWithTrailingButton; +@property(nonatomic, readwrite, strong) NSLayoutConstraint *leadingButtonConstraintHeightZero; + @property(nonatomic, readwrite, strong) NSLayoutConstraint *trailingButtonConstraintBottom; @property(nonatomic, readwrite, strong) NSLayoutConstraint *trailingButtonConstraintTop; @property(nonatomic, readwrite, strong) NSLayoutConstraint *trailingButtonConstraintTrailing; +@property(nonatomic, readwrite, strong) NSLayoutConstraint *trailingButtonConstraintHeightZero; @property(nonatomic, readwrite, strong) NSLayoutConstraint *dividerConstraintHeight; @property(nonatomic, readwrite, strong) NSLayoutConstraint *dividerConstraintBottom; @@ -283,6 +286,8 @@ - (void)setUpButtonsConstraints { forAxis:UILayoutConstraintAxisHorizontal]; [self.leadingButton setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; + self.leadingButtonConstraintHeightZero = + [self.leadingButton.heightAnchor constraintEqualToConstant:0.f]; self.trailingButtonConstraintBottom = [self.trailingButton.bottomAnchor constraintEqualToAnchor:self.buttonContainerView.bottomAnchor]; self.trailingButtonConstraintTop = @@ -294,6 +299,8 @@ - (void)setUpButtonsConstraints { forAxis:UILayoutConstraintAxisHorizontal]; [self.trailingButton setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; + self.trailingButtonConstraintHeightZero = + [self.trailingButton.heightAnchor constraintEqualToConstant:0.f]; } - (void)setUpDividerConstraints { @@ -331,9 +338,11 @@ - (void)deactivateAllConstraints { self.leadingButtonConstraintCenterY.active = NO; self.leadingButtonConstraintBaseLineWithTrailingButton.active = NO; self.leadingButtonConstraintTrailingWithTrailingButton.active = NO; + self.leadingButtonConstraintHeightZero.active = NO; self.trailingButtonConstraintBottom.active = NO; self.trailingButtonConstraintTop.active = NO; self.trailingButtonConstraintTrailing.active = NO; + self.trailingButtonConstraintHeightZero.active = NO; self.dividerConstraintBottom.active = NO; self.dividerConstraintHeight.active = NO; self.dividerConstraintLeading.active = NO; @@ -381,18 +390,25 @@ - (CGSize)sizeThatFits:(CGSize)size { case MDCBannerViewLayoutStyleMultiRowAlignedButton: { frameHeight += kTopPaddingLarge + kBottomPadding; frameHeight += [self getFrameHeightOfImageViewAndTextViewWithSizeToFit:contentSize]; - CGSize leadingButtonSize = [self.leadingButton sizeThatFits:CGSizeZero]; - CGSize trailingButtonSize = [self.trailingButton sizeThatFits:CGSizeZero]; + CGSize leadingButtonSize = + self.leadingButton.hidden ? CGSizeZero : [self.leadingButton sizeThatFits:CGSizeZero]; + CGSize trailingButtonSize = + self.trailingButton.hidden ? CGSizeZero : [self.trailingButton sizeThatFits:CGSizeZero]; frameHeight += MAX(leadingButtonSize.height, trailingButtonSize.height); break; } case MDCBannerViewLayoutStyleMultiRowStackedButton: { frameHeight += kTopPaddingLarge + kBottomPadding; frameHeight += [self getFrameHeightOfImageViewAndTextViewWithSizeToFit:contentSize]; - CGSize leadingButtonSize = [self.leadingButton sizeThatFits:CGSizeZero]; - CGSize trailingButtonSize = [self.trailingButton sizeThatFits:CGSizeZero]; - frameHeight += - leadingButtonSize.height + trailingButtonSize.height + kButtonVerticalIntervalSpace; + CGSize leadingButtonSize = + self.leadingButton.hidden ? CGSizeZero : [self.leadingButton sizeThatFits:CGSizeZero]; + CGSize trailingButtonSize = + self.trailingButton.hidden ? CGSizeZero : [self.trailingButton sizeThatFits:CGSizeZero]; + CGFloat verticalIntervalSpace = kButtonVerticalIntervalSpace; + if (self.leadingButton.hidden || self.trailingButton.hidden) { + verticalIntervalSpace = 0.f; + } + frameHeight += leadingButtonSize.height + trailingButtonSize.height + verticalIntervalSpace; break; } default: @@ -477,6 +493,7 @@ - (void)updateButtonsConstraintsWithLayoutStyle:(MDCBannerViewLayoutStyle)layout if (self.trailingButton.hidden) { self.leadingButtonConstraintTrailing.active = YES; self.leadingButtonConstraintCenterY.active = YES; + self.trailingButtonConstraintHeightZero.active = YES; } else { if (layoutStyle == MDCBannerViewLayoutStyleMultiRowStackedButton) { self.leadingButtonConstraintTop.active = YES; @@ -487,6 +504,9 @@ - (void)updateButtonsConstraintsWithLayoutStyle:(MDCBannerViewLayoutStyle)layout self.leadingButtonConstraintBaseLineWithTrailingButton.active = YES; } } + if (self.leadingButton.hidden) { + self.leadingButtonConstraintHeightZero.active = YES; + } self.leadingButtonConstraintLeading.active = YES; self.trailingButtonConstraintTrailing.active = YES; self.trailingButtonConstraintBottom.active = YES; From eb3cfafb8061094f9d986b3702bb93585ba6f138 Mon Sep 17 00:00:00 2001 From: Randall Li Date: Mon, 20 Jul 2020 11:53:23 -0400 Subject: [PATCH 18/22] Automatic changelog preparation for release. --- .gitattributes | 28 ++++++++++++++++++++--- CHANGELOG.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/.gitattributes b/.gitattributes index c13f46c3bdc..f56e2dfb895 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,25 @@ -# Do not merge this version into `stable`. # DO NOT CHANGE THIS FILE -snapshot_test_goldens/**/*.png filter=lfs diff=lfs merge=lfs -text # DO NOT EDIT THE LINE BELOW. -.gitattributes merge=gitattributes +# DO NOT CHANGE THIS FILE +# DO NOT EDIT THE LINE BELOW. +/.gitattributes merge=gitattributes +# DO NOT EDIT THE LINE ABOVE. +# +# You can of course edit this file, but make sure you understand what you are +# doing. This file defines a custom filter driver that prevents snapshot test +# images from being merged into `stable`. Snapshot test images are only +# valuable in `develop` because they are only intended to help developers +# identify changes in the appearance of the library. +# +# Before you change this file, please carefully consider whether such a change +# is actually necessary. When you do change this file, it should almost always +# be done in a dedicated commit directly on the `stable` branch and not part +# of a release. If you see this file being changed as part of a release, +# block the release and work with the releaser to ensure that the change needs +# to be propagated from the `develop` branch to the `stable` branch. In nearly +# all cases, it should not be propagated from `develop` to `stable`. +# +# If you are a releaser and see this file change and you're not sure why, you +# might have accidentally skipped [setting the correct +# driver in your cloned +# repository](https://github.com/material-components/material-components-ios/blob/develop/contributing/releasing.md#configure-the-merge-strategy-for-gitattributes). +# If that's the case, please either revert the accidental change manually or +# restart the release with a fresh clone and the correct driver. diff --git a/CHANGELOG.md b/CHANGELOG.md index 343262e9a5b..0f2f4579752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,63 @@ +# #develop# + +Replace this text with a summarized description of this release's contents. +## Breaking changes + +Replace this explanations for how to resolve the breaking changes. +## New deprecations + +Replace this text with links to deprecation guides. +## New features + +Replace this text with example code for each new feature. +## API changes + +## Component changes + +### ActionSheet + +* [Delete MDCActionSheetColorThemer](https://github.com/material-components/material-components-ios/commit/e4805f0c2c439b8e0d1099021a5aadfcbc23b532) (Andrew Overton) +* [Delete MDCActionSheetThemer and MDCActionSheetScheme](https://github.com/material-components/material-components-ios/commit/742c7b3da7b51551110740f9b062d1a4eb7712e6) (Andrew Overton) +* [Delete MDCActionSheetTypographyThemer](https://github.com/material-components/material-components-ios/commit/a5d83de736d27458c42e3c76a4208e2e40704d53) (Andrew Overton) +* [Rename ActionSheet assets](https://github.com/material-components/material-components-ios/commit/1bca00af2f0aaefaf8c6f80cfe659b8a990d9094) (Andrew Overton) + +### Banner + +* [Add Banner snapshots for long text with no action style.](https://github.com/material-components/material-components-ios/commit/c1bc33cc4219ca4c4586316f87e8bf64d10972d0) (Wenyu Zhang) +* [Add height constraints to leadingButton and trailingButton on MDCBannerView to handle the case where they are hidden.](https://github.com/material-components/material-components-ios/commit/4216eb6ed560b65e7e5903d12da072a1f322b30d) (Wenyu Zhang) +* [Update sizeThatFits calculation to include layout margins in the height calculation and ensure entire leading button can be clicked when trailing button is hidden, by constraining the buttonContainerView to always be at least as tall as the leading button.](https://github.com/material-components/material-components-ios/commit/1b69ff6f39089b1e8a842f11bdd9a7fb98bdb22b) (Ingerid Fosli) + +### BottomAppBar + +* [Internal change](https://github.com/material-components/material-components-ios/commit/eb324d0394c0c0ee474551ac63650d617f5646a5) (Alyssa Weiss) + +### ButtonBar + +* [Add UIMenu support to MDCButtonBar.](https://github.com/material-components/material-components-ios/commit/3002e9a73f9b6c070495fc7f27d1cc183b8bdd4f) (Jan Philipp Sachse) + +### Chips + +* [Update inter-chip spacing in chip field to align with Material spec (8dp vertical and horizontal inter-chip spacing)](https://github.com/material-components/material-components-ios/commit/f2dd90e06cca99d66d4e33c99fad0d13180b1e04) (Bryan Oltman) + +### Dialogs + +* [Disabling selection in alerts while allowing tappable links](https://github.com/material-components/material-components-ios/commit/81b5c39f7e072bf57f4e1dabfb5d26d60736861e) (Galia Kaufman) + +### NavigationDrawer + +* [Make sure to update color of scrim view background when trait collection changes its user interface style.](https://github.com/material-components/material-components-ios/commit/45c4130d1804c5324beda3aaf719a8d25af75f8d) (Yarden Eitan) +* [Update the MDCBottomDrawerViewController MDC themer to use dynamic colors.](https://github.com/material-components/material-components-ios/commit/dde71806927bbc0af0ba01dfa7055e6d6a89476b) (Jake Rockland) + +### ProgressView + +* [Fix indeterminate animation being removed before it is presented on screen.](https://github.com/material-components/material-components-ios/commit/be54e359513d3156f40198becf635d230449a64a) (Wenyu Zhang) + +### Tabs + +* [Add non fixed clustered centered layout style](https://github.com/material-components/material-components-ios/commit/40e108d6b3e005372ae3afe07c1b6ca29abe19d2) (Andrew Overton) + +--- + # 110.3.0 In this release we switched button to use a `centerVisibleArea` over the `visibleAreaInsets` because it is more versitile. We made improvements to TextControl's layout as well. Bottom drawer can now respond to iPad Slide Over layout changes. From c71593c1eb7ec85157e533e9067ff0fb2f4f6f25 Mon Sep 17 00:00:00 2001 From: Randall Li Date: Mon, 20 Jul 2020 14:03:19 -0400 Subject: [PATCH 19/22] Hand-modified CHANGELOG.md API diff. --- CHANGELOG.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f2f4579752..c5008299c6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,70 @@ -# #develop# +# 111.0.0 + +In this Major release we removed some deprecated action sheet themers, made visual improvements to Chips, ProgressView, Banner and Tabs. We also made links clickable in Alert Dialogs -Replace this text with a summarized description of this release's contents. ## Breaking changes -Replace this explanations for how to resolve the breaking changes. -## New deprecations +### ActionSheet + +We deleted ActionSheet Themers. Use theming extensions. -Replace this text with links to deprecation guides. ## New features -Replace this text with example code for each new feature. +### Banner + +Update sizeThatFits calculation to include layout margins. + +```objc +banner.layoutMargins = UIEdgeInsetsMake(0, 8, 0, 8); +``` + +### ButtonBar + +Add UIMenu support for MDCButtonBar. + +```objc +MDCButtonBar *buttonBar = [[MDCButtonBar alloc] init]; +UIMenu *primaryMenu = [self exampleMenuWithTitle:@"A primary action menu"]; +menuAsPrimaryActionItem = [[UIBarButtonItem alloc] initWithImage:nil menu:primaryMenu]; +menuAsPrimaryActionItem.title = @"Menu on tap"; + +buttonBar.items = @[ menuAsSecondaryActionItem, menuAsPrimaryActionItem, changingActionItem ]; + +… +- (UIMenu *)exampleMenuWithTitle:(NSString *)title API_AVAILABLE(ios(14.0)) { + UIAction *firstAction = [UIAction actionWithTitle:@"An action" + image:nil + identifier:nil + handler:^(__kindof UIAction *_Nonnull action) { + NSLog(@"First element was tapped."); + }]; + UIAction *secondAction = [UIAction actionWithTitle:@"A second action" + image:nil + identifier:nil + handler:^(__kindof UIAction *_Nonnull action) { + NSLog(@"Second element was tapped."); + }]; + + NSArray *menuElements = @[ firstAction, secondAction ]; + return [UIMenu menuWithTitle:title children:menuElements]; +} +``` + +### Tabs + +Add non fixed clustered centered layout style. + +```objc +tabBar.preferredLayoutStyle = + MDCTabBarViewLayoutStyleNonFixedClusteredCentered; +``` + ## API changes +### TabBarView + +*new* enum: `MDCTabBarViewLayoutStyleNonFixedClusteredCentered` + ## Component changes ### ActionSheet From 5a0d592bd89021eb82c0d8bbb5565c7e2cf5f9bf Mon Sep 17 00:00:00 2001 From: Randall Li Date: Mon, 20 Jul 2020 14:06:28 -0400 Subject: [PATCH 20/22] Bumped version number to 111.0.0. --- MaterialComponents.podspec | 2 +- MaterialComponentsBeta.podspec | 2 +- MaterialComponentsEarlGreyTests.podspec | 2 +- MaterialComponentsExamples.podspec | 2 +- MaterialComponentsSnapshotTests.podspec | 2 +- VERSION | 2 +- catalog/MDCCatalog/Info.plist | 4 ++-- catalog/MDCDragons/Info.plist | 4 ++-- catalog/MaterialCatalog/MaterialCatalog.podspec | 2 +- components/LibraryInfo/src/MDCLibraryInfo.m | 2 +- components/LibraryInfo/tests/unit/LibraryInfoTests.m | 2 +- demos/supplemental/RemoteImageServiceForMDCDemos.podspec | 2 +- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec index c543ad1e3b5..788c7c5dede 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 = "110.3.0" + mdc.version = "111.0.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 d6341c15a28..abdc3f5ec01 100644 --- a/MaterialComponentsBeta.podspec +++ b/MaterialComponentsBeta.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |mdc| mdc.name = "MaterialComponentsBeta" - mdc.version = "110.3.0" + mdc.version = "111.0.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 dcd8f934f07..fd722e66b6e 100644 --- a/MaterialComponentsEarlGreyTests.podspec +++ b/MaterialComponentsEarlGreyTests.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialComponentsEarlGreyTests" - s.version = "110.3.0" + s.version = "111.0.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 e402ab9c28a..a8b360c6463 100644 --- a/MaterialComponentsExamples.podspec +++ b/MaterialComponentsExamples.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialComponentsExamples" - s.version = "110.3.0" + s.version = "111.0.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 7a79b42e5e8..4b8784ea271 100644 --- a/MaterialComponentsSnapshotTests.podspec +++ b/MaterialComponentsSnapshotTests.podspec @@ -53,7 +53,7 @@ end Pod::Spec.new do |s| s.name = "MaterialComponentsSnapshotTests" - s.version = "110.3.0" + s.version = "111.0.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 30fdeda4144..12cd89fb66d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -110.3.0 +111.0.0 diff --git a/catalog/MDCCatalog/Info.plist b/catalog/MDCCatalog/Info.plist index 544a6223bce..0bdf2b1bef3 100644 --- a/catalog/MDCCatalog/Info.plist +++ b/catalog/MDCCatalog/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 110.3.0 + 111.0.0 CFBundleSignature ???? CFBundleVersion - 110.3.0 + 111.0.0 LSRequiresIPhoneOS UIAppFonts diff --git a/catalog/MDCDragons/Info.plist b/catalog/MDCDragons/Info.plist index fab5f125912..0988818b047 100644 --- a/catalog/MDCDragons/Info.plist +++ b/catalog/MDCDragons/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 110.3.0 + 111.0.0 CFBundleVersion - 110.3.0 + 111.0.0 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/catalog/MaterialCatalog/MaterialCatalog.podspec b/catalog/MaterialCatalog/MaterialCatalog.podspec index 2003fdaf99e..a102022349d 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 = "110.3.0" + s.version = "111.0.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/LibraryInfo/src/MDCLibraryInfo.m b/components/LibraryInfo/src/MDCLibraryInfo.m index 5b120f101f4..df1227ab8d1 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 kMDCLibraryInfoVersionString = @"110.3.0"; +static NSString* const kMDCLibraryInfoVersionString = @"111.0.0"; @implementation MDCLibraryInfo diff --git a/components/LibraryInfo/tests/unit/LibraryInfoTests.m b/components/LibraryInfo/tests/unit/LibraryInfoTests.m index 2f7f7b4334e..3cd91150309 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: "110.3.0", etc. + // Accept: "111.0.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/demos/supplemental/RemoteImageServiceForMDCDemos.podspec b/demos/supplemental/RemoteImageServiceForMDCDemos.podspec index 4817e3bb216..c8120b615d4 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 = "110.3.0" + s.version = "111.0.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" From 73ad7619e3a25f98dbc299847933f68e59e4e117 Mon Sep 17 00:00:00 2001 From: Randall Li Date: Mon, 20 Jul 2020 14:26:24 -0400 Subject: [PATCH 21/22] Hand-modified CHANGELOG.md API diff. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5008299c6b..9cfc211283c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ We deleted ActionSheet Themers. Use theming extensions. ### Banner -Update sizeThatFits calculation to include layout margins. +Update sizeThatFits calculation to include layout margins to ensure entire leading button can be clicked when trailing button is hidden. ```objc banner.layoutMargins = UIEdgeInsetsMake(0, 8, 0, 8); From 681d595a591a5c058ddbb434b89dd16f7446ba92 Mon Sep 17 00:00:00 2001 From: Randall Li Date: Mon, 20 Jul 2020 15:24:19 -0400 Subject: [PATCH 22/22] Hand-modified CHANGELOG.md API diff. --- CHANGELOG.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cfc211283c..6979b0784b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,14 +10,6 @@ We deleted ActionSheet Themers. Use theming extensions. ## New features -### Banner - -Update sizeThatFits calculation to include layout margins to ensure entire leading button can be clicked when trailing button is hidden. - -```objc -banner.layoutMargins = UIEdgeInsetsMake(0, 8, 0, 8); -``` - ### ButtonBar Add UIMenu support for MDCButtonBar.