diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec index 6209d0f5e33..2f188753474 100644 --- a/MaterialComponents.podspec +++ b/MaterialComponents.podspec @@ -126,6 +126,16 @@ Pod::Spec.new do |s| ss.dependency "MaterialComponents/Typography" end + s.subspec "Dialogs" do |ss| + ss.public_header_files = "components/#{ss.base_name}/src/*.h" + ss.source_files = "components/#{ss.base_name}/src/*.{h,m}", "components/#{ss.base_name}/src/private/*.{h,m}" + ss.header_mappings_dir = "components/#{ss.base_name}/src" + + ss.dependency "MaterialComponents/Buttons" + ss.dependency "MaterialComponents/ShadowElevations" + ss.dependency "MaterialComponents/ShadowLayer" + end + s.subspec "FlexibleHeader" do |ss| ss.public_header_files = "components/#{ss.base_name}/src/*.h" ss.source_files = "components/#{ss.base_name}/src/*.{h,m}", "components/#{ss.base_name}/src/private/*.{h,m}" diff --git a/catalog/MDCCatalog.xcodeproj/project.pbxproj b/catalog/MDCCatalog.xcodeproj/project.pbxproj index 39e20913efb..31216f84353 100644 --- a/catalog/MDCCatalog.xcodeproj/project.pbxproj +++ b/catalog/MDCCatalog.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 0B4A5E6D1CC9307A00D2AC5D /* MDCCatalogWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B4A5E6C1CC9307A00D2AC5D /* MDCCatalogWindow.swift */; }; + 403860EF1D24328A00FB8216 /* MDCCatalogTileDataDialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = 403860EE1D24328A00FB8216 /* MDCCatalogTileDataDialogs.m */; }; 5D090E571C9AEB8D0061344A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5D090E341C9AEB8C0061344A /* Localizable.strings */; }; 664524B71C6BA62A001ADBF8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664524B61C6BA62A001ADBF8 /* AppDelegate.swift */; }; 664524B91C6BA62A001ADBF8 /* MDCNodeListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664524B81C6BA62A001ADBF8 /* MDCNodeListViewController.swift */; }; @@ -41,6 +42,8 @@ /* Begin PBXFileReference section */ 0B4A5E6C1CC9307A00D2AC5D /* MDCCatalogWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogWindow.swift; sourceTree = ""; }; 1A55EA1CC69E40D5EF866144 /* Pods_MDCCatalog.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MDCCatalog.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 403860ED1D24328A00FB8216 /* MDCCatalogTileDataDialogs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDCCatalogTileDataDialogs.h; sourceTree = ""; }; + 403860EE1D24328A00FB8216 /* MDCCatalogTileDataDialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MDCCatalogTileDataDialogs.m; sourceTree = ""; }; 5D090E351C9AEB8C0061344A /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = ""; }; 5D090E361C9AEB8C0061344A /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = ""; }; 5D090E371C9AEB8C0061344A /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; @@ -218,6 +221,8 @@ DE19446E1CBD9E40009E0321 /* MDCCatalogTileDataButtons.m */, DE7A17C61CCAC1F700B66230 /* MDCCatalogTileDataCollections.h */, DE7A17C41CCAC1EA00B66230 /* MDCCatalogTileDataCollections.m */, + 403860ED1D24328A00FB8216 /* MDCCatalogTileDataDialogs.h */, + 403860EE1D24328A00FB8216 /* MDCCatalogTileDataDialogs.m */, DE19446F1CBD9E40009E0321 /* MDCCatalogTileDataFlexibleHeader.h */, DE1944701CBD9E40009E0321 /* MDCCatalogTileDataFlexibleHeader.m */, DE1944711CBD9E40009E0321 /* MDCCatalogTileDataHeaderStackView.h */, @@ -435,6 +440,7 @@ 664524B71C6BA62A001ADBF8 /* AppDelegate.swift in Sources */, DE1944941CBD9E40009E0321 /* MDCCatalogTileDataTypography.m in Sources */, DEF64EA41C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift in Sources */, + 403860EF1D24328A00FB8216 /* MDCCatalogTileDataDialogs.m in Sources */, DE1944861CBD9E40009E0321 /* MDCCatalogTileData.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/catalog/MDCCatalog/MDCCatalogTileDataDialogs.h b/catalog/MDCCatalog/MDCCatalogTileDataDialogs.h new file mode 100644 index 00000000000..26dec4d0009 --- /dev/null +++ b/catalog/MDCCatalog/MDCCatalogTileDataDialogs.h @@ -0,0 +1,27 @@ +/* + Copyright 2016-present Google Inc. 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 "MDCCatalogTileData.h" + +// TODO: Consider updated naming. +// https://github.com/google/material-components-ios/issues/620 +@interface MDCCatalogTileDataDialogs : MDCCatalogTileData + ++ (UIImage *)drawTileImage:(CGRect)frame; + +@end diff --git a/catalog/MDCCatalog/MDCCatalogTileDataDialogs.m b/catalog/MDCCatalog/MDCCatalogTileDataDialogs.m new file mode 100644 index 00000000000..bae25a1916f --- /dev/null +++ b/catalog/MDCCatalog/MDCCatalogTileDataDialogs.m @@ -0,0 +1,70 @@ +/* + Copyright 2016-present Google Inc. 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 "MDCCatalogTileDataDialogs.h" + +@implementation MDCCatalogTileDataDialogs + ++ (UIImage*)drawTileImage:(CGRect)frame { + void (^drawBlock)(CGRect) = ^(CGRect drawBlockFrame) { + [self draw:CGRectMake(0, 0, drawBlockFrame.size.width, drawBlockFrame.size.height)]; + }; + return [self drawImageWithFrame:frame drawBlock:drawBlock]; +} + +// TODO: Replace with Dialog-specific Tile drawing code +/* Auto-generated code using PaintCode and formatted with clang-format. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" ++ (void)draw:(CGRect)frame { + CGContextRef context = UIGraphicsGetCurrentContext(); + + UIColor* fillColor = [UIColor colorWithRed:0.077 green:0.591 blue:0.945 alpha:1]; + + CGRect group = CGRectMake( + CGRectGetMinX(frame) + floor((CGRectGetWidth(frame) - 77.75) * 0.22200 + 0.02) + 0.48, + CGRectGetMinY(frame) + floor((CGRectGetHeight(frame) - 24.25) * 0.30382 + 0.47) + 0.03, 77.75, + 24.25); + { + CGContextSaveGState(context); + CGContextSetAlpha(context, 0.95); + CGContextBeginTransparencyLayer(context, NULL); + + UIBezierPath* rectanglePath = + [UIBezierPath bezierPathWithRoundedRect:CGRectMake(CGRectGetMinX(group), + CGRectGetMinY(group), 77.75, 24.25) + cornerRadius:3.4]; + [fillColor setFill]; + [rectanglePath fill]; + + CGContextEndTransparencyLayer(context); + CGContextRestoreGState(context); + } + + UIBezierPath* ovalPath = [UIBezierPath + bezierPathWithOvalInRect:CGRectMake( + CGRectGetMinX(frame) + + floor((CGRectGetWidth(frame) - 49.8) * 0.82308 - 0.25) + + 0.75, + CGRectGetMinY(frame) + + floor((CGRectGetHeight(frame) - 49.7) * 0.25641 + 0.2) + 0.3, + 49.8, 49.7)]; + [fillColor setFill]; + [ovalPath fill]; +} +#pragma clang diagnostic pop + +@end diff --git a/catalog/Podfile.lock b/catalog/Podfile.lock index fdd82ea17bd..c2fb967ad67 100644 --- a/catalog/Podfile.lock +++ b/catalog/Podfile.lock @@ -8,6 +8,7 @@ PODS: - MaterialComponents/CollectionCells (= 12.0.1) - MaterialComponents/CollectionLayoutAttributes (= 12.0.1) - MaterialComponents/Collections (= 12.0.1) + - MaterialComponents/Dialogs (= 12.0.1) - MaterialComponents/FlexibleHeader (= 12.0.1) - MaterialComponents/FontDiskLoader (= 12.0.1) - MaterialComponents/HeaderStackView (= 12.0.1) @@ -63,6 +64,10 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography + - MaterialComponents/Dialogs (12.0.1): + - MaterialComponents/Buttons + - MaterialComponents/ShadowElevations + - MaterialComponents/ShadowLayer - MaterialComponents/FlexibleHeader (12.0.1) - MaterialComponents/FontDiskLoader (12.0.1) - MaterialComponents/HeaderStackView (12.0.1) @@ -155,7 +160,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: CatalogByConvention: 0137d280781667dd3d856a14cdf507f5ac8eedc0 - MaterialComponents: ed704822f99f7286a3e5d2d4cc7ffdeac044c211 + MaterialComponents: 3afee35d1cd8bc2196922dd9e5f33bdb89a18cd3 MaterialComponentsCatalog: c2911e8ad9f380350107f124f88a43b9fe012d19 MaterialComponentsUnitTests: 03755d2a184c1d21a66c86c0e333d7b087e2f662 diff --git a/components/Dialogs/README.md b/components/Dialogs/README.md new file mode 100644 index 00000000000..bc5502e2f2a --- /dev/null +++ b/components/Dialogs/README.md @@ -0,0 +1,109 @@ +--- +title: "Dialogs" +layout: detail +section: components +excerpt: "The Dialogs component implements the Material Design specifications for modal presentations." +--- + +# Dialogs + +Dialogs provides a presentation controller that will display a modal dialog according to the +material spec. + +### Material Design Specifications + + + +### Dialogs Classes + +#### Dialogs Presentation Controller and Transition Controller + +Dialogs is comprised of two classes: MDCDialogPresentationController and +MDCDialogTransitionController. These allow the presentation of view controllers in a material +specificed manner. MDCDialogPresentationController is a subclass of UIPresentationController +that observes the presented view controller for preferred content size. +MDCDialogTransitionController implements UIViewControllerAnimatedTransitioning and +UIViewControllerTransitioningDelegate to vend the presentation controller during the transition. + +## Installation + +### Requirements + +- Xcode 7.0 or higher. +- iOS SDK version 8.0 or higher. + +### Installation with CocoaPods + +To add this component to your Xcode project using CocoaPods, add the following to your `Podfile`: + +~~~ +pod 'MaterialComponents/Dialogs' +~~~ + +Then, run the following command: + +~~~ bash +pod install +~~~ + +- - - + +## Usage + +To display a modal using MaterialDialogs you set two properties on the view controller to be +presentented. Set modalPresentationStyle to UIModalPresentationCustom and set +transitioningDelegate to and instance of MDCDialogTransitionController. Then you present the +view controller from the root controller to display it as a modal dialog. + +### Importing + +Before using Dialogs, you'll need to import it: + +#### Objective-C + +~~~ objc +#import "MaterialDialogs.h" +~~~ + +#### Swift + +~~~ swift +import MaterialComponents +~~~ + +## Examples + +### Display a modal dialog + +#### Objective-C + +~~~ objc +// self is the presenting view controller and which has the following property +// defined to keep a reference to the transition controller. +@property(nonatomic, strong) MDCDialogTransitionController *dialogTransitionController; + +// To present the dialog +self.dialogTransitionController = [[MDCDialogTransitionController alloc] init]; +modalDialogViewController.modalPresentationStyle = UIModalPresentationCustom; +modalDialogViewController.transitioningDelegate = self.dialogTransitionController; +[self presentViewController:myDialogViewController animated:YES completion:...]; + +~~~ + +#### Swift + +~~~ swift +// The following is called from the presenting view controller and has the +// following variable defined to keep a reference to the presentation +// controller. +strong var dialogTransitionController: MDCDialogTransitionController + +// To present the dialog +dialogTransitionController = MDCDialogTransitionController() +modalDialogViewController.modalPresentationStyle = UIModalPresentationCustom +modalDialogViewController.transitioningDelegate = dialogTransitionController +presentViewController(myDialogViewController animated:YES ...) + +~~~ diff --git a/components/Dialogs/examples/DialogsTypicalUseViewController.m b/components/Dialogs/examples/DialogsTypicalUseViewController.m new file mode 100644 index 00000000000..2475770de4a --- /dev/null +++ b/components/Dialogs/examples/DialogsTypicalUseViewController.m @@ -0,0 +1,60 @@ +/* + Copyright 2016-present Google Inc. 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 "DialogsTypicalUseSupplemental.h" + +#import "MaterialButtons.h" +#import "MaterialDialogs.h" + +@interface DialogsTypicalUseViewController () + +@property(nonatomic, strong) MDCDialogTransitionController *transitionController; + +@end + +@implementation DialogsTypicalUseViewController + +- (void)viewDidLoad { + // We must create and store a strong reference to the transitionController. + // A presented view controller will set this object as its transitioning delegate. + self.transitionController = [[MDCDialogTransitionController alloc] init]; +} + +- (IBAction)didTapProgrammatic:(id)sender { + UIViewController *viewController = + [[ProgrammaticViewController alloc] initWithNibName:nil bundle:nil]; + viewController.modalPresentationStyle = UIModalPresentationCustom; + viewController.transitioningDelegate = self.transitionController; + + [self presentViewController:viewController animated:YES completion:NULL]; +} + +- (IBAction)didTapStoryboard:(id)sender { + // If you are using this code outside of the MDCCatalog in your own app, your bundle may be nil. + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + UIStoryboard *storyboard = + [UIStoryboard storyboardWithName:@"DialogWithPreferredContentSize" bundle:bundle]; + NSString *identifier = @"DialogID"; + + UIViewController *viewController = + [storyboard instantiateViewControllerWithIdentifier:identifier]; + viewController.modalPresentationStyle = UIModalPresentationCustom; + viewController.transitioningDelegate = self.transitionController; + + [self presentViewController:viewController animated:YES completion:NULL]; +} + +@end diff --git a/components/Dialogs/examples/resources/DialogWithPreferredContentSize.storyboard b/components/Dialogs/examples/resources/DialogWithPreferredContentSize.storyboard new file mode 100644 index 00000000000..95b5ade5768 --- /dev/null +++ b/components/Dialogs/examples/resources/DialogWithPreferredContentSize.storyboard @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/Dialogs/examples/resources/DialogsTypicalUseViewController.storyboard b/components/Dialogs/examples/resources/DialogsTypicalUseViewController.storyboard new file mode 100644 index 00000000000..a0a7c865e54 --- /dev/null +++ b/components/Dialogs/examples/resources/DialogsTypicalUseViewController.storyboard @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/Dialogs/examples/supplemental/DialogWithPreferredContentSizeViewController.h b/components/Dialogs/examples/supplemental/DialogWithPreferredContentSizeViewController.h new file mode 100644 index 00000000000..b2b86efeb17 --- /dev/null +++ b/components/Dialogs/examples/supplemental/DialogWithPreferredContentSizeViewController.h @@ -0,0 +1,21 @@ +/* + Copyright 2016-present Google Inc. 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 + +@interface DialogWithPreferredContentSizeViewController : UIViewController + +@end diff --git a/components/Dialogs/examples/supplemental/DialogWithPreferredContentSizeViewController.m b/components/Dialogs/examples/supplemental/DialogWithPreferredContentSizeViewController.m new file mode 100644 index 00000000000..545572077ac --- /dev/null +++ b/components/Dialogs/examples/supplemental/DialogWithPreferredContentSizeViewController.m @@ -0,0 +1,25 @@ +/* + Copyright 2016-present Google Inc. 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 "DialogWithPreferredContentSizeViewController.h" + +@implementation DialogWithPreferredContentSizeViewController + +- (IBAction)buttonPushed:(id)sender { + [self.presentingViewController dismissViewControllerAnimated:YES completion:NULL]; +} + +@end diff --git a/components/Dialogs/examples/supplemental/DialogsTypicalUseSupplemental.h b/components/Dialogs/examples/supplemental/DialogsTypicalUseSupplemental.h new file mode 100644 index 00000000000..f802112e5dc --- /dev/null +++ b/components/Dialogs/examples/supplemental/DialogsTypicalUseSupplemental.h @@ -0,0 +1,14 @@ +/* IMPORTANT: + This file contains supplemental code used to populate the demos with dummy data or instructions. + It is not necessary to import this file to implement any Material Design Components. + */ + +#import + +//@class DialogsTypicalUseViewController; + +@interface DialogsTypicalUseViewController : UIViewController +@end + +@interface ProgrammaticViewController : UIViewController +@end diff --git a/components/Dialogs/examples/supplemental/DialogsTypicalUseSupplemental.m b/components/Dialogs/examples/supplemental/DialogsTypicalUseSupplemental.m new file mode 100644 index 00000000000..ca34f0b082e --- /dev/null +++ b/components/Dialogs/examples/supplemental/DialogsTypicalUseSupplemental.m @@ -0,0 +1,71 @@ +/* IMPORTANT: + This file contains supplemental code used to populate the examples with dummy data and/or + instructions. It is not necessary to import this file to implement any Material Design Components. + */ + +#import + +#import "DialogsTypicalUseSupplemental.h" +#import "MaterialButtons.h" +#import "MaterialDialogs.h" +#import "MaterialTypography.h" + +#pragma mark - DialogsTypicalUseViewController + +@implementation DialogsTypicalUseViewController (CatalogByConvention) + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Dialogs", @"Dialogs" ]; +} + ++ (NSString *)catalogDescription { + return @"Dialogs includes a presentation controller that displays your modal interfaces in a" + "material spec defined context."; +} + ++ (BOOL)catalogIsPrimaryDemo { + return YES; +} + ++ (NSString *)catalogStoryboardName { + return @"DialogsTypicalUseViewController"; +} + +@end + +@interface ProgrammaticViewController () + +@property(nonatomic, strong) MDCRaisedButton *dismissButton; + +@end + +@implementation ProgrammaticViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor whiteColor]; + + _dismissButton = [[MDCRaisedButton alloc] init]; + _dismissButton.autoresizingMask = + UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | + UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; + [_dismissButton setTitle:@"Dismiss" forState:UIControlStateNormal]; + [_dismissButton sizeToFit]; + [_dismissButton addTarget:self + action:@selector(dismiss:) + forControlEvents:UIControlEventTouchUpInside]; + + [self.view addSubview:_dismissButton]; + _dismissButton.center = self.view.center; +} + +- (CGSize)preferredContentSize { + return CGSizeMake(200.0, 140.0); +} + +- (IBAction)dismiss:(id)sender { + [self.presentingViewController dismissViewControllerAnimated:YES completion:NULL]; +} + +@end diff --git a/components/Dialogs/src/MDCDialogPresentationController.h b/components/Dialogs/src/MDCDialogPresentationController.h new file mode 100644 index 00000000000..d3a9394c792 --- /dev/null +++ b/components/Dialogs/src/MDCDialogPresentationController.h @@ -0,0 +1,64 @@ +/* + Copyright 2016-present Google Inc. 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 + +#if !defined(__IPHONE_8_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0) +#error "This component only supports iOS 8.0 and above." +#endif + +/** + MDCDialogPresentationController will present a modal ViewController as a dialog according to the + material spec. + + https://material.google.com/components/dialogs.html + + MDCDialogPresentationController should not be used to present full-screen dialogs. + + The presented UIViewController will be asked for it's preferredContentSize to help determine + the best size for the dialog to be displayed. + + This controller will manage the background dimming view and add a material-styled shadow underneath + the presented view. + + This controller will respond to the display / dismissal of the keyboard and update the + presentedView's frame. + */ +@interface MDCDialogPresentationController : UIPresentationController + +/** + Returns the size of the specified child view controller's content. + + The size is initially based on container.preferredSize. Width is will have a minimum of 280 and a + maximum of the parentSize - 40 for the padding on the left and right size. + + Height has no minimum. Height has a maximum of the parentSize - height of any displayed + keyboards - 48 for the padding on the top and bottom. If preferredSize.height is 0, height will + be set to the maximum. + */ +- (CGSize)sizeForChildContentContainer:(nonnull id)container + withParentContainerSize:(CGSize)parentSize; + +/** + Returns the frame rectangle to assign to the presented view at the end of the animations. + + The size of the frame is determined by sizeForChildContentContainer:withParentContainerSize:. The + presentedView is centered horizontally and verically in the available space. The available space + is the size of the containerView subtracting any space taken up by the keyboard. + */ +- (CGRect)frameOfPresentedViewInContainerView; + +@end diff --git a/components/Dialogs/src/MDCDialogPresentationController.m b/components/Dialogs/src/MDCDialogPresentationController.m new file mode 100644 index 00000000000..a7d5ff58757 --- /dev/null +++ b/components/Dialogs/src/MDCDialogPresentationController.m @@ -0,0 +1,334 @@ +/* + Copyright 2016-present Google Inc. 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 "MDCDialogPresentationController.h" + +#import "MaterialKeyboardWatcher.h" +#import "private/MDCDialogShadowedView.h" + +static CGFloat MDCDialogMinimumWidth = 280.0f; +// TODO: Spec indicates 40 side margins and 280 minimum width. +// That is incompatible with a 320 wide device. +// Side margins set to 20 until we have a resolution +static UIEdgeInsets MDCDialogEdgeInsets = {24, 20, 24, 20}; + +@interface MDCDialogPresentationController () + +// View matching the container's bounds that dims the entire screen and catchs taps to dismiss. +@property(nonatomic) UIView *dimmingView; + +// Tracking view that adds a shadow under the presented view. This view's frame should always match +// the presented view's. +@property(nonatomic) UIView *trackingView; + +@end + +@implementation MDCDialogPresentationController + +#pragma mark - UIPresentationController + +- (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController + presentingViewController:(UIViewController *)presentingViewController { + self = [super initWithPresentedViewController:presentedViewController + presentingViewController:presentingViewController]; + if (self) { + _dimmingView = [[UIView alloc] initWithFrame:CGRectZero]; + _dimmingView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.4f]; + _dimmingView.alpha = 0.0f; + UITapGestureRecognizer *tap = + [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismiss:)]; + [_dimmingView addGestureRecognizer:tap]; + + _trackingView = [[MDCDialogShadowedView alloc] init]; + + [self registerKeyboardNotifications]; + } + + return self; +} + +- (void)dealloc { + [self unregisterKeyboardNotifications]; +} + +- (CGRect)frameOfPresentedViewInContainerView { + CGRect presentedViewFrame = CGRectZero; + + CGRect containerBounds = self.containerView.bounds; + containerBounds.size.height -= [MDCKeyboardWatcher sharedKeyboardWatcher].keyboardOffset; + + presentedViewFrame.size = [self sizeForChildContentContainer:self.presentedViewController + withParentContainerSize:containerBounds.size]; + + presentedViewFrame.origin.x = (containerBounds.size.width - presentedViewFrame.size.width) * 0.5f; + presentedViewFrame.origin.y = + (containerBounds.size.height - presentedViewFrame.size.height) * 0.5f; + + presentedViewFrame.origin.x = (CGFloat)floor(presentedViewFrame.origin.x); + presentedViewFrame.origin.y = (CGFloat)floor(presentedViewFrame.origin.y); + + return presentedViewFrame; +} + +- (void)presentationTransitionWillBegin { + // TODO: Follow the material spec description of Autonomous surface creation for both + // presentation and dismissal of the dialog. + // https://spec.googleplex.com/quantum/motion/choreography.html#choreography-creation + + // Set the dimming view to the container's bounds and fully transparent. + self.dimmingView.frame = self.containerView.bounds; + self.dimmingView.alpha = 0.0f; + [self.containerView addSubview:self.dimmingView]; + + // Set the shadowing view to the same frame as the presented view. + CGRect presentedFrame = [self frameOfPresentedViewInContainerView]; + self.trackingView.frame = presentedFrame; + self.trackingView.alpha = 0.0f; + [self.containerView addSubview:self.trackingView]; + + // Fade-in chrome views to be fully visible. + id transitionCoordinator = + [self.presentedViewController transitionCoordinator]; + if (transitionCoordinator) { + [transitionCoordinator + animateAlongsideTransition:^(id context) { + self.dimmingView.alpha = 1.0f; + self.trackingView.alpha = 1.0f; + } + completion:NULL]; + } else { + self.dimmingView.alpha = 1.0f; + self.trackingView.alpha = 1.0f; + } +} + +- (void)presentationTransitionDidEnd:(BOOL)completed { + if (completed) { + // Stop the presenting view from being tapped for voiceover while this view is up. + // Setting @c accessibilityViewIsModal on the presented view (or its parent) should be enough, + // but it's not. + // b/19519321 + self.presentingViewController.view.accessibilityElementsHidden = YES; + self.presentedView.accessibilityViewIsModal = YES; + UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, [self presentedView]); + } else { + // Transition was cancelled. + [self.dimmingView removeFromSuperview]; + [self.trackingView removeFromSuperview]; + } +} + +- (void)dismissalTransitionWillBegin { + // Fade-out dimmingView and trackingView to be fully transparent. + id transitionCoordinator = + [self.presentedViewController transitionCoordinator]; + if (transitionCoordinator != nil) { + [transitionCoordinator + animateAlongsideTransition:^(id context) { + self.dimmingView.alpha = 0.0f; + self.trackingView.alpha = 0.0f; + } + completion:NULL]; + } else { + self.dimmingView.alpha = 0.0f; + self.trackingView.alpha = 0.0f; + } +} + +- (void)dismissalTransitionDidEnd:(BOOL)completed { + if (completed) { + [self.dimmingView removeFromSuperview]; + [self.trackingView removeFromSuperview]; + + // Re-enable accessibilityElements on the presenting view controller. + self.presentingViewController.view.accessibilityElementsHidden = NO; + } + + [super dismissalTransitionDidEnd:completed]; +} + +/** + Indicate that the presenting view controller's view should not be removed when the presentation + animations finish. + + MDCDialogPresentationController does not cover the presenting view controller's content entirely + so we must return NO. + */ +- (BOOL)shouldRemovePresentersView { + return NO; +} + +#pragma mark - UIContentContainer + +/** + Determines the size of the presented container's view based on available space and the preferred + content size of the container. + */ +- (CGSize)sizeForChildContentContainer:(id)container + withParentContainerSize:(CGSize)parentSize { + if (CGSizeEqualToSize(parentSize, CGSizeZero)) { + return CGSizeZero; + } + + CGSize maxChildSize; + maxChildSize.width = parentSize.width - MDCDialogEdgeInsets.left - MDCDialogEdgeInsets.right; + maxChildSize.height = parentSize.height - MDCDialogEdgeInsets.top - MDCDialogEdgeInsets.bottom; + + CGSize targetSize = maxChildSize; + + const CGSize preferredContentSize = container.preferredContentSize; + if (!CGSizeEqualToSize(preferredContentSize, CGSizeZero)) { + targetSize = preferredContentSize; + + // If the targetSize.width is greater than 0.0 it must be at least MDCDialogMinimumWidth. + if (0.0f < targetSize.width && targetSize.width < MDCDialogMinimumWidth) { + targetSize.width = MDCDialogMinimumWidth; + } + // targetSize cannot exceed maxChildSize. + targetSize.width = MIN(targetSize.width, maxChildSize.width); + targetSize.height = MIN(targetSize.height, maxChildSize.height); + } + + targetSize.width = (CGFloat)ceil(targetSize.width); + targetSize.height = (CGFloat)ceil(targetSize.height); + + return targetSize; +} + +- (void)viewWillTransitionToSize:(CGSize)size + withTransitionCoordinator:(id)coordinator { + [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; + + [coordinator + animateAlongsideTransition:^(id context) { + self.dimmingView.frame = self.containerView.bounds; + CGRect presentedViewFrame = [self frameOfPresentedViewInContainerView]; + self.presentedView.frame = presentedViewFrame; + self.trackingView.frame = presentedViewFrame; + } + completion:NULL]; +} + +/** + If the container's preferred content size has changed and we are able to accommidate the new size, + update the frame of the presented view and the shadowing view. + */ +- (void)preferredContentSizeDidChangeForChildContentContainer:(id)container { + [super preferredContentSizeDidChangeForChildContentContainer:container]; + + CGSize existingSize = self.presentedView.bounds.size; + CGSize newSize = [self sizeForChildContentContainer:container + withParentContainerSize:self.containerView.bounds.size]; + + if (!CGSizeEqualToSize(existingSize, newSize)) { + CGRect presentedViewFrame = [self frameOfPresentedViewInContainerView]; + self.presentedView.frame = presentedViewFrame; + self.trackingView.frame = presentedViewFrame; + } +} + +#pragma mark - Internal + +- (void)dismiss:(UIGestureRecognizer *)gesture { + if (gesture.state == UIGestureRecognizerStateRecognized) { + [self.presentingViewController dismissViewControllerAnimated:YES completion:NULL]; + } +} + +#pragma mark - Keyboard handling + +// Convert UIViewAnimationCurve to UIViewAnimationOptions +// TODO(iangordon): Move to MDCKeyboardWatcher +static UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCurve animationCurve) { + switch (animationCurve) { + case UIViewAnimationCurveEaseInOut: + return UIViewAnimationOptionCurveEaseInOut; + case UIViewAnimationCurveEaseIn: + return UIViewAnimationOptionCurveEaseIn; + case UIViewAnimationCurveEaseOut: + return UIViewAnimationOptionCurveEaseOut; + case UIViewAnimationCurveLinear: + return UIViewAnimationOptionCurveLinear; + default: + // HACK: + // UIKeyboardWillChangeFrameNotification can happen with a curve of 7 which is not currently + // defined in UIViewAnimationCurve + if ((NSInteger)animationCurve != 7) { + NSLog(@"Unknown UIViewAnimationCurve : %ld", (long)animationCurve); + } + // animationCurve << 16 may an be acceptable workaround + return UIViewAnimationOptionCurveEaseInOut; + } +} + +- (void)registerKeyboardNotifications { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardWatcherHandler:) + name:MDCKeyboardWatcherKeyboardWillShowNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardWatcherHandler:) + name:MDCKeyboardWatcherKeyboardWillHideNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(keyboardWatcherHandler:) + name:MDCKeyboardWatcherKeyboardWillChangeFrameNotification + object:nil]; +} + +- (void)unregisterKeyboardNotifications { + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:MDCKeyboardWatcherKeyboardWillShowNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:MDCKeyboardWatcherKeyboardWillHideNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:MDCKeyboardWatcherKeyboardWillChangeFrameNotification + object:nil]; +} + +#pragma mark - KeyboardWatcher Notifications + +- (void)keyboardWatcherHandler:(NSNotification *)aNotification { + NSNumber *animationDurationNumber = + aNotification.userInfo[UIKeyboardAnimationDurationUserInfoKey]; + NSTimeInterval animationDuration = (NSTimeInterval)[animationDurationNumber doubleValue]; + + NSNumber *animationCurveNumber = aNotification.userInfo[UIKeyboardAnimationCurveUserInfoKey]; + UIViewAnimationCurve animationCurve = (UIViewAnimationCurve)[animationCurveNumber integerValue]; + UIViewAnimationOptions animationCurveOption = animationOptionsWithCurve(animationCurve); + + [UIView animateWithDuration:animationDuration + delay:0.0f + options:animationCurveOption | UIViewAnimationOptionTransitionNone + animations:^{ + CGRect presentedViewFrame = [self frameOfPresentedViewInContainerView]; + self.presentedView.frame = presentedViewFrame; + self.trackingView.frame = presentedViewFrame; + } + completion:NULL]; +} + +@end diff --git a/components/Dialogs/src/MDCDialogTransitionController.h b/components/Dialogs/src/MDCDialogTransitionController.h new file mode 100644 index 00000000000..bb3ed310b47 --- /dev/null +++ b/components/Dialogs/src/MDCDialogTransitionController.h @@ -0,0 +1,43 @@ +/* + Copyright 2016-present Google Inc. 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 + +#if !defined(__IPHONE_8_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0) +#error "This component only supports iOS 8.0 and above." +#endif + +/** + MDCDialogTransitionController is be used to setup a custom transition and animationed presentation + and dismissal for material-styled alerts, simple dialogs and confirmation dialogs. + + https://material.google.com/components/dialogs.html + + This class provides a basic implementation of UIViewControllerAnimatedTransitioning and + UIViewControllerTransitioningDelegate. + + In order to use a custom modal transition, the UIViewController to be presented must set two + properties. The UIViewControllers transitioningDelegate should be set to an instance of this class. + myDialogViewController.modalPresentationStyle = UIModalPresentationCustom; + myDialogViewController.transitioningDelegate = dialogTransitionController; + + The presenting UIViewController then calls presentViewController:animated:completion: + [rootViewController presentViewController:myDialogViewController animated:YES completion:...]; + */ +@interface MDCDialogTransitionController + : NSObject + +@end diff --git a/components/Dialogs/src/MDCDialogTransitionController.m b/components/Dialogs/src/MDCDialogTransitionController.m new file mode 100644 index 00000000000..1b1d6663ab4 --- /dev/null +++ b/components/Dialogs/src/MDCDialogTransitionController.m @@ -0,0 +1,112 @@ +/* + Copyright 2016-present Google Inc. 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 "MDCDialogTransitionController.h" + +#import "MDCDialogPresentationController.h" + +@implementation MDCDialogTransitionController + +static const NSTimeInterval MDCDialogTransitionDuration = 0.5; + +#pragma mark - UIViewControllerAnimatedTransitioning + +- (NSTimeInterval)transitionDuration:(id)transitionContext { + return MDCDialogTransitionDuration; +} + +- (void)animateTransition:(id)transitionContext { + // If a view in the transitionContext is nil, it likely hasn't been loaded by its ViewController + // yet. Ask for it directly to initiate a loadView on the ViewController. + UIViewController *fromViewController = + [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; + UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]; + if (fromView == nil) { + fromView = fromViewController.view; + } + + UIViewController *toViewController = + [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; + UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; + if (toView == nil) { + toView = toViewController.view; + } + + UIViewController *toPresentingViewController = toViewController.presentingViewController; + BOOL presenting = (toPresentingViewController == fromViewController) ? YES : NO; + + UIViewController *animatingViewController = presenting ? toViewController : fromViewController; + UIView *animatingView = presenting ? toView : fromView; + + UIView *containerView = transitionContext.containerView; + + if (presenting) { + [containerView addSubview:toView]; + } + + CGFloat startingAlpha = presenting ? 0.0f : 1.0f; + CGFloat endingAlpha = presenting ? 1.0f : 0.0f; + + animatingView.frame = [transitionContext finalFrameForViewController:animatingViewController]; + animatingView.alpha = startingAlpha; + + NSTimeInterval transitionDuration = [self transitionDuration:transitionContext]; + UIViewAnimationOptions options = + UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState; + + [UIView animateWithDuration:transitionDuration + delay:0.0 + options:options + animations:^{ + animatingView.alpha = endingAlpha; + } + completion:^(BOOL finished) { + // If we're dismissing, remove the presented view from the hierarchy + if (!presenting) { + [fromView removeFromSuperview]; + } + + // From ADC : UIViewControllerContextTransitioning + // When you do create transition animations, always call the + // completeTransition: from an appropriate completion block to let UIKit know + // when all of your animations have finished. + [transitionContext completeTransition:YES]; + }]; +} + +#pragma mark - UIViewControllerTransitioningDelegate + +- (UIPresentationController *) + presentationControllerForPresentedViewController:(UIViewController *)presented + presentingViewController:(UIViewController *)presenting + sourceViewController:(UIViewController *)source { + return [[MDCDialogPresentationController alloc] initWithPresentedViewController:presented + presentingViewController:presenting]; +} + +- (id) + animationControllerForPresentedController:(UIViewController *)presented + presentingController:(UIViewController *)presenting + sourceController:(UIViewController *)source { + return self; +} + +- (id)animationControllerForDismissedController: + (UIViewController *)dismissed { + return self; +} + +@end diff --git a/components/Dialogs/src/MaterialDialogs.h b/components/Dialogs/src/MaterialDialogs.h new file mode 100644 index 00000000000..b4d401cb1a9 --- /dev/null +++ b/components/Dialogs/src/MaterialDialogs.h @@ -0,0 +1,18 @@ +/* + Copyright 2016-present Google Inc. 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 "MDCDialogPresentationController.h" +#import "MDCDialogTransitionController.h" diff --git a/components/Dialogs/src/private/MDCDialogShadowedView.h b/components/Dialogs/src/private/MDCDialogShadowedView.h new file mode 100644 index 00000000000..fe8afff9b84 --- /dev/null +++ b/components/Dialogs/src/private/MDCDialogShadowedView.h @@ -0,0 +1,21 @@ +/* + Copyright 2016-present Google Inc. 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 + +@interface MDCDialogShadowedView : UIView + +@end diff --git a/components/Dialogs/src/private/MDCDialogShadowedView.m b/components/Dialogs/src/private/MDCDialogShadowedView.m new file mode 100644 index 00000000000..514a833a60a --- /dev/null +++ b/components/Dialogs/src/private/MDCDialogShadowedView.m @@ -0,0 +1,40 @@ +/* + Copyright 2016-present Google Inc. 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 "MDCDialogShadowedView.h" + +#import "MDCShadowElevations.h" +#import "MDCShadowLayer.h" + +@implementation MDCDialogShadowedView + ++ (Class)layerClass { + return [MDCShadowLayer class]; +} + +- (MDCShadowLayer *)shadowLayer { + return (MDCShadowLayer *)self.layer; +} + +- (instancetype)init { + self = [super init]; + if (self) { + [[self shadowLayer] setElevation:MDCShadowElevationDialog]; + } + return self; +} + +@end diff --git a/demos/Pesto/Podfile.lock b/demos/Pesto/Podfile.lock index b08934845e5..14f48b1fb76 100644 --- a/demos/Pesto/Podfile.lock +++ b/demos/Pesto/Podfile.lock @@ -7,6 +7,7 @@ PODS: - MaterialComponents/CollectionCells (= 12.0.1) - MaterialComponents/CollectionLayoutAttributes (= 12.0.1) - MaterialComponents/Collections (= 12.0.1) + - MaterialComponents/Dialogs (= 12.0.1) - MaterialComponents/FlexibleHeader (= 12.0.1) - MaterialComponents/FontDiskLoader (= 12.0.1) - MaterialComponents/HeaderStackView (= 12.0.1) @@ -62,6 +63,10 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography + - MaterialComponents/Dialogs (12.0.1): + - MaterialComponents/Buttons + - MaterialComponents/ShadowElevations + - MaterialComponents/ShadowLayer - MaterialComponents/FlexibleHeader (12.0.1) - MaterialComponents/FontDiskLoader (12.0.1) - MaterialComponents/HeaderStackView (12.0.1) @@ -140,7 +145,7 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - MaterialComponents: ed704822f99f7286a3e5d2d4cc7ffdeac044c211 + MaterialComponents: 3afee35d1cd8bc2196922dd9e5f33bdb89a18cd3 PODFILE CHECKSUM: f138be16d4835113ff672258fc7529fad3f90e91 diff --git a/demos/Shrine/Podfile.lock b/demos/Shrine/Podfile.lock index df2d42e39ee..f0979267762 100644 --- a/demos/Shrine/Podfile.lock +++ b/demos/Shrine/Podfile.lock @@ -7,6 +7,7 @@ PODS: - MaterialComponents/CollectionCells (= 12.0.1) - MaterialComponents/CollectionLayoutAttributes (= 12.0.1) - MaterialComponents/Collections (= 12.0.1) + - MaterialComponents/Dialogs (= 12.0.1) - MaterialComponents/FlexibleHeader (= 12.0.1) - MaterialComponents/FontDiskLoader (= 12.0.1) - MaterialComponents/HeaderStackView (= 12.0.1) @@ -62,6 +63,10 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography + - MaterialComponents/Dialogs (12.0.1): + - MaterialComponents/Buttons + - MaterialComponents/ShadowElevations + - MaterialComponents/ShadowLayer - MaterialComponents/FlexibleHeader (12.0.1) - MaterialComponents/FontDiskLoader (12.0.1) - MaterialComponents/HeaderStackView (12.0.1) @@ -140,7 +145,7 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - MaterialComponents: ed704822f99f7286a3e5d2d4cc7ffdeac044c211 + MaterialComponents: 3afee35d1cd8bc2196922dd9e5f33bdb89a18cd3 PODFILE CHECKSUM: b585ca32a2884e050823cc1f861e8b7246f7dcc1