From c1b9ff0e78fb85f877a12b43fbcc5a906f4868ce Mon Sep 17 00:00:00 2001 From: Adrian Secord Date: Wed, 13 Apr 2016 14:05:52 -0400 Subject: [PATCH 001/129] [Website] Changed top-level Community tab to Contributing. Summary: Changed "Community" text to "Contributing". Moved community/* to contributing/* to match top-level nav changes. Updated references everywhere. Requires matching changes to site-source that will be pushed once this is submitted. https://github.com/google/material-components-ios/issues/315 Reviewers: #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Subscribers: featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D598 --- CONTRIBUTING.md | 2 +- {community => contributing}/README.md | 6 +++--- {community => contributing}/contributor_guides/checklist.md | 0 {community => contributing}/contributor_guides/hotfixing.md | 0 {community => contributing}/contributor_guides/releasing.md | 0 .../contributor_guides/supported_versions.md | 0 .../contributor_guides/writing_readmes.md | 0 7 files changed, 4 insertions(+), 4 deletions(-) rename {community => contributing}/README.md (99%) rename {community => contributing}/contributor_guides/checklist.md (100%) rename {community => contributing}/contributor_guides/hotfixing.md (100%) rename {community => contributing}/contributor_guides/releasing.md (100%) rename {community => contributing}/contributor_guides/supported_versions.md (100%) rename {community => contributing}/contributor_guides/writing_readmes.md (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ba550743732..a0d69ebe557 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -121,7 +121,7 @@ considerations as a basis for minimizing dependencies: - dependency-less components are much easier to drop in and, most importantly, to remove from a project. -[Reach out to the team directly](community/#questions) for advice or questions on this matter. +[Reach out to the team directly](contributing/#questions) for advice or questions on this matter. Recommendations: diff --git a/community/README.md b/contributing/README.md similarity index 99% rename from community/README.md rename to contributing/README.md index 6ef69d70cca..773aa82cecb 100644 --- a/community/README.md +++ b/contributing/README.md @@ -1,11 +1,11 @@ --- -title: "Community" +title: "Contributing" layout: landing -section: community +section: contributing --- -# Community +# Contributing Material Components for iOS is intended to be a full open source project that accepts contributions from community members. We can work together to optimize Material Design on iOS. diff --git a/community/contributor_guides/checklist.md b/contributing/contributor_guides/checklist.md similarity index 100% rename from community/contributor_guides/checklist.md rename to contributing/contributor_guides/checklist.md diff --git a/community/contributor_guides/hotfixing.md b/contributing/contributor_guides/hotfixing.md similarity index 100% rename from community/contributor_guides/hotfixing.md rename to contributing/contributor_guides/hotfixing.md diff --git a/community/contributor_guides/releasing.md b/contributing/contributor_guides/releasing.md similarity index 100% rename from community/contributor_guides/releasing.md rename to contributing/contributor_guides/releasing.md diff --git a/community/contributor_guides/supported_versions.md b/contributing/contributor_guides/supported_versions.md similarity index 100% rename from community/contributor_guides/supported_versions.md rename to contributing/contributor_guides/supported_versions.md diff --git a/community/contributor_guides/writing_readmes.md b/contributing/contributor_guides/writing_readmes.md similarity index 100% rename from community/contributor_guides/writing_readmes.md rename to contributing/contributor_guides/writing_readmes.md From ac38382e86f058593de41756fb8360dc5a156a10 Mon Sep 17 00:00:00 2001 From: Adrian Secord Date: Wed, 13 Apr 2016 14:35:46 -0400 Subject: [PATCH 002/129] Updated top-level "Documentation" to "Components". Reviewers: #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D599 --- components/AppBar/README.md | 2 +- components/ButtonBar/README.md | 2 +- components/Buttons/README.md | 2 +- components/FlexibleHeader/README.md | 2 +- components/FontDiskLoader/README.md | 2 +- components/HeaderStackView/README.md | 2 +- components/Ink/README.md | 2 +- components/NavigationBar/README.md | 2 +- components/PageControl/README.md | 2 +- components/README.md | 2 +- components/RobotoFontLoader/README.md | 2 +- components/ScrollViewDelegateMultiplexer/README.md | 2 +- components/ShadowElevations/README.md | 2 +- components/ShadowLayer/README.md | 2 +- components/Slider/README.md | 2 +- components/SpritedAnimationView/README.md | 2 +- components/Switch/README.md | 2 +- components/Typography/README.md | 2 +- contributing/contributor_guides/writing_readmes.md | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/components/AppBar/README.md b/components/AppBar/README.md index 33766fa9e4a..e308849f987 100644 --- a/components/AppBar/README.md +++ b/components/AppBar/README.md @@ -1,7 +1,7 @@ --- title: "App Bar" layout: detail -section: documentation +section: components excerpt: "The App Bar is a flexible navigation bar designed to provide a typical Material Design navigation experience." --- # App Bar diff --git a/components/ButtonBar/README.md b/components/ButtonBar/README.md index fd36979f838..c982f5c7d27 100644 --- a/components/ButtonBar/README.md +++ b/components/ButtonBar/README.md @@ -1,7 +1,7 @@ --- title: "Button Bar" layout: detail -section: documentation +section: components excerpt: "The Button Bar component is a view that facilitates the creation and layout of a horizontally-aligned list of buttons." --- # Button Bar diff --git a/components/Buttons/README.md b/components/Buttons/README.md index e0fe33d678e..6832132ad05 100644 --- a/components/Buttons/README.md +++ b/components/Buttons/README.md @@ -1,7 +1,7 @@ --- title: "Buttons" layout: detail -section: documentation +section: components excerpt: "Buttons is a collection of Material Design buttons, including a flat button, a raised button and a floating action button." --- # Buttons diff --git a/components/FlexibleHeader/README.md b/components/FlexibleHeader/README.md index 4c56914e527..86dd1dc06f2 100644 --- a/components/FlexibleHeader/README.md +++ b/components/FlexibleHeader/README.md @@ -1,7 +1,7 @@ --- title: "Flexible Header" layout: detail -section: documentation +section: components excerpt: "The Flexible Header is a container view whose height and vertical offset react to UIScrollViewDelegate events." --- # Flexible Header diff --git a/components/FontDiskLoader/README.md b/components/FontDiskLoader/README.md index 2df860329d1..d3eaf92d08b 100644 --- a/components/FontDiskLoader/README.md +++ b/components/FontDiskLoader/README.md @@ -1,7 +1,7 @@ --- title: "FontDiskLoader" layout: detail -section: documentation +section: components excerpt: "Registers a single custom font asset from disk." --- #FontDiskLoader diff --git a/components/HeaderStackView/README.md b/components/HeaderStackView/README.md index 11c2cf428ed..a59ea08a472 100644 --- a/components/HeaderStackView/README.md +++ b/components/HeaderStackView/README.md @@ -1,7 +1,7 @@ --- title: "Header Stack View" layout: detail -section: documentation +section: components excerpt: "The Header Stack View component is a view that coordinates the layout of two vertically stacked bar views." --- # Header Stack View diff --git a/components/Ink/README.md b/components/Ink/README.md index d2e720be7e9..2d40bb1d0f7 100644 --- a/components/Ink/README.md +++ b/components/Ink/README.md @@ -1,7 +1,7 @@ --- title: "Ink" layout: detail -section: documentation +section: components excerpt: "The Ink component provides a radial action in the form of a visual ripple of ink expanding outward from the user's touch." --- # Ink diff --git a/components/NavigationBar/README.md b/components/NavigationBar/README.md index e0e7cd1a76c..8731ec26570 100644 --- a/components/NavigationBar/README.md +++ b/components/NavigationBar/README.md @@ -1,7 +1,7 @@ --- title: "Navigation Bar" layout: detail -section: documentation +section: components excerpt: "The Navigation Bar component is a view composed of a left and right Button Bar and either a title label or a custom title view." --- # Navigation Bar diff --git a/components/PageControl/README.md b/components/PageControl/README.md index 84e555336ed..59b3f257f34 100644 --- a/components/PageControl/README.md +++ b/components/PageControl/README.md @@ -1,7 +1,7 @@ --- title: "Page Control" layout: detail -section: documentation +section: components excerpt: "Page Control is a drop-in Material Design replacement for UIPageControl that implements Material Design animation and layout." --- # Page Control diff --git a/components/README.md b/components/README.md index 48a70f1fd8c..1d3f25bc142 100644 --- a/components/README.md +++ b/components/README.md @@ -1,7 +1,7 @@ --- title: "Material Components Documentation" layout: landing -section: documentation +section: components --- # Component Documentation diff --git a/components/RobotoFontLoader/README.md b/components/RobotoFontLoader/README.md index 4f9276c2a59..895304b0af4 100644 --- a/components/RobotoFontLoader/README.md +++ b/components/RobotoFontLoader/README.md @@ -1,7 +1,7 @@ --- title: "TODO: Roboto Font Loader" layout: detail -section: documentation +section: components excerpt: "The Roboto Font Loader lazy loads the Robot font." --- # Roboto Font Loader diff --git a/components/ScrollViewDelegateMultiplexer/README.md b/components/ScrollViewDelegateMultiplexer/README.md index 55cea8b5093..75198afa11f 100644 --- a/components/ScrollViewDelegateMultiplexer/README.md +++ b/components/ScrollViewDelegateMultiplexer/README.md @@ -1,7 +1,7 @@ --- title: "ScrollViewDelegateMultiplexer" layout: detail -section: documentation +section: components excerpt: "ScrollViewDelegateMultiplexer acts as a proxy object for UIScrollViewDelegate events and forwards all received events to an ordered list of registered observers." --- # ScrollViewDelegateMultiplexer diff --git a/components/ShadowElevations/README.md b/components/ShadowElevations/README.md index 67b708ee5ff..df275cb7782 100644 --- a/components/ShadowElevations/README.md +++ b/components/ShadowElevations/README.md @@ -1,7 +1,7 @@ --- title: "Shadow Elevations" layout: detail -section: documentation +section: components excerpt: "The Shadow Elevations component provides the most commonly-used Material Design elevations." --- # Shadow Elevations diff --git a/components/ShadowLayer/README.md b/components/ShadowLayer/README.md index 92a346e4339..3ef052ffa9d 100644 --- a/components/ShadowLayer/README.md +++ b/components/ShadowLayer/README.md @@ -1,7 +1,7 @@ --- title: "Shadow Layer" layout: detail -section: documentation +section: components excerpt: "The Shadow Layer component implements the Material Design specifications for elevation and shadows." --- diff --git a/components/Slider/README.md b/components/Slider/README.md index 48cd02c449f..2adebf1190d 100644 --- a/components/Slider/README.md +++ b/components/Slider/README.md @@ -1,7 +1,7 @@ --- title: "Slider" layout: detail -section: documentation +section: components excerpt: "The Slider component provides a Material Design control for selecting a value from a continuous range or discrete set of values." --- # Slider diff --git a/components/SpritedAnimationView/README.md b/components/SpritedAnimationView/README.md index ba4ab20d17c..f930fdc9fa8 100644 --- a/components/SpritedAnimationView/README.md +++ b/components/SpritedAnimationView/README.md @@ -1,7 +1,7 @@ --- title: "Sprited Animation View" layout: detail -section: documentation +section: components excerpt: "The Sprited Animation View component provides an alternative to animating an array of images with an UIImageView." --- # SpritedAnimationView diff --git a/components/Switch/README.md b/components/Switch/README.md index b0ba197d836..99e5ff0fb4c 100644 --- a/components/Switch/README.md +++ b/components/Switch/README.md @@ -1,7 +1,7 @@ --- title: "Switch" layout: detail -section: documentation +section: components excerpt: "The Switch component provides an Material Design on/off switch control with an interface similar to UISwitch." --- diff --git a/components/Typography/README.md b/components/Typography/README.md index 333f503cd90..657cf51c549 100644 --- a/components/Typography/README.md +++ b/components/Typography/README.md @@ -1,7 +1,7 @@ --- title: "Typography" layout: detail -section: documentation +section: components excerpt: "The Typography component provides methods for displaying text using the type sizes and opacities from the Material Design specifications." --- # Typography diff --git a/contributing/contributor_guides/writing_readmes.md b/contributing/contributor_guides/writing_readmes.md index 242aca752ad..aa23a31a901 100644 --- a/contributing/contributor_guides/writing_readmes.md +++ b/contributing/contributor_guides/writing_readmes.md @@ -11,7 +11,7 @@ fill out have been marked with `TODO` statements. --- title: "TODO: ComponentName" layout: detail - section: documentation + section: components excerpt: "TODO: Single sentence description of the component." --- # TODO: ComponentName From 2ab08f41337d1ef1392bec46f534fe95fb2ac79e Mon Sep 17 00:00:00 2001 From: Will Larche Date: Wed, 13 Apr 2016 14:54:47 -0400 Subject: [PATCH 003/129] [Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic. Summary: Merge branch 'develop' of https://github.com/google/material-components-ios into develop Reviewers: ajsecord, featherless, #mdc_ios_owners Reviewed By: ajsecord, featherless, #mdc_ios_owners Subscribers: featherless, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D587 --- .../src/CBCCatalogExample.h | 6 ++ .../src/CBCNodeViewController.h | 3 + .../src/CBCNodeViewController.m | 4 ++ .../src/private/CBCRuntime.h | 3 + .../src/private/CBCRuntime.m | 17 ++++++ catalog/MDCCatalog.xcodeproj/project.pbxproj | 4 +- catalog/MDCCatalog/NodeViewController.swift | 4 +- .../AppBar/examples/AppBarTypicalUseExample.m | 2 +- .../examples/ButtonBarTypicalUseExample.m | 2 +- .../ButtonsSimpleExampleViewController.m | 2 +- .../FlexibleHeaderTypicalUseExample.m | 2 +- .../FlexibleHeaderConfiguratorSupplemental.h | 18 +++++- .../FlexibleHeaderConfiguratorSupplemental.m | 18 +++++- .../FlexibleHeaderTypicalUseSupplemental.h | 18 +++++- .../FlexibleHeaderTypicalUseSupplemental.m | 20 ++++++- .../HeaderStackViewTypicalUseViewController.m | 2 +- .../Ink/examples/InkTypicalUseExample.m | 2 +- .../examples/NavigationBarTypicalUseExample.m | 2 +- .../NavigationBarTypicalUseExample.swift | 59 +++++++++++++++++++ ...vigationBarTypicalUseExampleSupplemental.h | 18 +++++- ...vigationBarTypicalUseExampleSupplemental.m | 16 ++++- ...tionBarTypicalUseExampleSupplemental.swift | 58 ++++++++++++++++++ .../examples/PageControlTypicalUseExample.m | 2 +- .../RobotoVsSystemExampleViewController.m | 2 +- .../ShadowElevationsTypicalUseExample.m | 2 +- ...tedAnimationViewTypicalUseViewController.m | 2 +- .../SwitchSimpleExampleViewController.m | 2 +- 27 files changed, 266 insertions(+), 24 deletions(-) create mode 100644 components/NavigationBar/examples/NavigationBarTypicalUseExample.swift create mode 100644 components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.swift diff --git a/catalog/CatalogByConvention/src/CBCCatalogExample.h b/catalog/CatalogByConvention/src/CBCCatalogExample.h index 0ea2dfd7f62..00eed851edc 100644 --- a/catalog/CatalogByConvention/src/CBCCatalogExample.h +++ b/catalog/CatalogByConvention/src/CBCCatalogExample.h @@ -26,6 +26,12 @@ /** Return a list of breadcrumbs defining the navigation path taken to reach this example. */ + (nonnull NSArray *)catalogBreadcrumbs; +/** + Return a BOOL stating whether this example should be treated as the main "Demo" in the Catalog + for this component + */ ++ (BOOL)catalogIsPrimaryDemo; + @optional /** diff --git a/catalog/CatalogByConvention/src/CBCNodeViewController.h b/catalog/CatalogByConvention/src/CBCNodeViewController.h index 20961e52f8f..372cd15e1fe 100644 --- a/catalog/CatalogByConvention/src/CBCNodeViewController.h +++ b/catalog/CatalogByConvention/src/CBCNodeViewController.h @@ -65,6 +65,9 @@ FOUNDATION_EXTERN CBCNode *__nonnull CBCCreateNavigationTree(void); /** The children of this node. */ @property(nonatomic, strong, nonnull) NSArray *children; +/** Is this the most important or default demo for this component? */ +- (BOOL)isPrimaryDemo; + /** Returns YES if this is an example node. */ - (BOOL)isExample; diff --git a/catalog/CatalogByConvention/src/CBCNodeViewController.m b/catalog/CatalogByConvention/src/CBCNodeViewController.m index e8d733a9b4a..26880f16d47 100644 --- a/catalog/CatalogByConvention/src/CBCNodeViewController.m +++ b/catalog/CatalogByConvention/src/CBCNodeViewController.m @@ -77,6 +77,10 @@ - (NSString *)createExampleDescription { return CBCDescriptionFromClass(_exampleClass); } +- (BOOL)isPrimaryDemo { + return CBCCatalogIsPrimaryDemoFromClass(_exampleClass); +} + @end @implementation CBCNodeListViewController diff --git a/catalog/CatalogByConvention/src/private/CBCRuntime.h b/catalog/CatalogByConvention/src/private/CBCRuntime.h index d0b5cf80269..49af35599fa 100644 --- a/catalog/CatalogByConvention/src/private/CBCRuntime.h +++ b/catalog/CatalogByConvention/src/private/CBCRuntime.h @@ -22,6 +22,9 @@ /** Invokes +catalogBreadcrumbs on the class and returns the corresponding array of strings. */ FOUNDATION_EXTERN NSArray *CBCCatalogBreadcrumbsFromClass(Class aClass); +/** Invokes +catalogIsPrimaryDemo on the class and returns the BOOL value. */ +FOUNDATION_EXTERN BOOL CBCCatalogIsPrimaryDemoFromClass(Class aClass); + #pragma mark Runtime enumeration /** Returns all Objective-C and Swift classes available to the runtime. */ diff --git a/catalog/CatalogByConvention/src/private/CBCRuntime.m b/catalog/CatalogByConvention/src/private/CBCRuntime.m index 325377325b7..dad10b11045 100644 --- a/catalog/CatalogByConvention/src/private/CBCRuntime.m +++ b/catalog/CatalogByConvention/src/private/CBCRuntime.m @@ -26,6 +26,23 @@ return [aClass performSelector:@selector(catalogBreadcrumbs)]; } +#pragma mark Primary demo check + +BOOL CBCCatalogIsPrimaryDemoFromClass(Class aClass) { + BOOL isPrimaryDemo = NO; + + if ([aClass respondsToSelector:@selector(catalogIsPrimaryDemo)]) { + NSMethodSignature *signature = [aClass methodSignatureForSelector:@selector(catalogIsPrimaryDemo)]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; + invocation.selector = @selector(catalogIsPrimaryDemo); + invocation.target = aClass; + [invocation invoke]; + [invocation getReturnValue:&isPrimaryDemo]; + } + + return isPrimaryDemo; +} + #pragma mark Runtime enumeration NSArray *CBCGetAllClasses(void) { diff --git a/catalog/MDCCatalog.xcodeproj/project.pbxproj b/catalog/MDCCatalog.xcodeproj/project.pbxproj index 9dbad87b251..13d8032470a 100644 --- a/catalog/MDCCatalog.xcodeproj/project.pbxproj +++ b/catalog/MDCCatalog.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ 0F092FBA334F9A6817C4B9ED /* Pods-MDCCatalog-MDCCatalogUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MDCCatalog-MDCCatalogUnitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MDCCatalog-MDCCatalogUnitTests/Pods-MDCCatalog-MDCCatalogUnitTests.debug.xcconfig"; sourceTree = ""; }; 12970BE3EA0028A12E1427AD /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; 1B8F1AA8D28612EA405B5C1A /* Pods_MDCCatalog.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MDCCatalog.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3640411C1CBECABD00C962B2 /* CatalogByConvention.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CatalogByConvention.h; sourceTree = ""; }; 39A3426A088E725ED84D45D4 /* Pods-MDCCatalog-MDCCatalog.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MDCCatalog-MDCCatalog.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MDCCatalog-MDCCatalog/Pods-MDCCatalog-MDCCatalog.debug.xcconfig"; sourceTree = ""; }; 51A574C94F29C2B3F2198B9E /* libPods-MDCCatalog-MDCCatalogUnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MDCCatalog-MDCCatalogUnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 5434D10FB7D7598D60B5F56C /* Pods-MDCCatalogUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MDCCatalogUnitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MDCCatalogUnitTests/Pods-MDCCatalogUnitTests.release.xcconfig"; sourceTree = ""; }; @@ -90,7 +91,6 @@ 664524C01C6BA62A001ADBF8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 664524C21C6BA62A001ADBF8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 665A34D91C6BD01900962055 /* MDCCatalog-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MDCCatalog-Bridging-Header.h"; sourceTree = ""; }; - 666CA7071CAEBCA9001B1884 /* CatalogByConvention.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CatalogByConvention.h; sourceTree = ""; }; 666CA7081CAEBCA9001B1884 /* CBCCatalogExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBCCatalogExample.h; sourceTree = ""; }; 666CA7091CAEBCA9001B1884 /* CBCNodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBCNodeViewController.h; sourceTree = ""; }; 666CA70A1CAEBCA9001B1884 /* CBCNodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CBCNodeViewController.m; sourceTree = ""; }; @@ -220,7 +220,7 @@ 666CA7061CAEBC98001B1884 /* CatalogByConvention */ = { isa = PBXGroup; children = ( - 666CA7071CAEBCA9001B1884 /* CatalogByConvention.h */, + 3640411C1CBECABD00C962B2 /* CatalogByConvention.h */, 666CA7081CAEBCA9001B1884 /* CBCCatalogExample.h */, 666CA7091CAEBCA9001B1884 /* CBCNodeViewController.h */, 666CA70A1CAEBCA9001B1884 /* CBCNodeViewController.m */, diff --git a/catalog/MDCCatalog/NodeViewController.swift b/catalog/MDCCatalog/NodeViewController.swift index a1266ef1c3a..b470f091b21 100644 --- a/catalog/MDCCatalog/NodeViewController.swift +++ b/catalog/MDCCatalog/NodeViewController.swift @@ -54,8 +54,8 @@ class NodeViewController: CBCNodeListViewController { let orderedNodes = NSMutableArray() for childNode in node.children { if childNode.isExample() { - let contentVC = childNode.createExampleViewController() - if contentVC.respondsToSelector("catalogIsPrimaryDemo") { + let isPrimaryDemo = childNode.isPrimaryDemo() + if isPrimaryDemo { orderedNodes.insertObject(childNode, atIndex: 0) componentDescription = childNode.createExampleDescription() } else { diff --git a/components/AppBar/examples/AppBarTypicalUseExample.m b/components/AppBar/examples/AppBarTypicalUseExample.m index eff634e061e..1b5fbdc7369 100644 --- a/components/AppBar/examples/AppBarTypicalUseExample.m +++ b/components/AppBar/examples/AppBarTypicalUseExample.m @@ -94,7 +94,7 @@ + (NSString *)catalogDescription { " navigation experience."; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } diff --git a/components/ButtonBar/examples/ButtonBarTypicalUseExample.m b/components/ButtonBar/examples/ButtonBarTypicalUseExample.m index 974c766a06e..7e18486334e 100644 --- a/components/ButtonBar/examples/ButtonBarTypicalUseExample.m +++ b/components/ButtonBar/examples/ButtonBarTypicalUseExample.m @@ -92,7 +92,7 @@ + (NSArray *)catalogBreadcrumbs { return @[ @"Button Bar", @"Button Bar" ]; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } diff --git a/components/Buttons/examples/ButtonsSimpleExampleViewController.m b/components/Buttons/examples/ButtonsSimpleExampleViewController.m index eb25f27432f..4fc215404de 100644 --- a/components/Buttons/examples/ButtonsSimpleExampleViewController.m +++ b/components/Buttons/examples/ButtonsSimpleExampleViewController.m @@ -226,7 +226,7 @@ + (NSString *)catalogDescription { " button and a floating action button."; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } diff --git a/components/FlexibleHeader/examples/FlexibleHeaderTypicalUseExample.m b/components/FlexibleHeader/examples/FlexibleHeaderTypicalUseExample.m index b900bced49a..99671613b11 100644 --- a/components/FlexibleHeader/examples/FlexibleHeaderTypicalUseExample.m +++ b/components/FlexibleHeader/examples/FlexibleHeaderTypicalUseExample.m @@ -1,5 +1,5 @@ /* - Copyright 2015-present Google Inc. All Rights Reserved. + 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. diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.h b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.h index 11e19897e70..37a72abe598 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.h +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.h @@ -1,4 +1,20 @@ -/* IMPORTANT: +/* + 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. + */ + +/** 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. */ diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m index 74babc6e9e3..906961c8c12 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m @@ -1,4 +1,20 @@ -/* IMPORTANT: +/* + 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. + */ + +/** 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. */ diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.h b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.h index f01a071e3c6..6bb99c135bf 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.h +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.h @@ -1,4 +1,20 @@ -/* IMPORTANT: +/* + 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. + */ + +/** 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. */ diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m index 3d2521af09c..c0c6298b19c 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m @@ -1,4 +1,20 @@ -/* IMPORTANT: +/* + 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. + */ + +/** 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. */ @@ -28,7 +44,7 @@ + (NSString *)catalogDescription { " UIScrollViewDelegate events."; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } diff --git a/components/HeaderStackView/examples/HeaderStackViewTypicalUseViewController.m b/components/HeaderStackView/examples/HeaderStackViewTypicalUseViewController.m index cb7ce5a8f02..72b0f0b091c 100644 --- a/components/HeaderStackView/examples/HeaderStackViewTypicalUseViewController.m +++ b/components/HeaderStackView/examples/HeaderStackViewTypicalUseViewController.m @@ -36,7 +36,7 @@ + (NSString *)catalogDescription { " vertically-stacked bar views."; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } diff --git a/components/Ink/examples/InkTypicalUseExample.m b/components/Ink/examples/InkTypicalUseExample.m index 1a7f5efc700..f6bdb2b5eaf 100644 --- a/components/Ink/examples/InkTypicalUseExample.m +++ b/components/Ink/examples/InkTypicalUseExample.m @@ -66,7 +66,7 @@ + (NSString *)catalogDescription { " expanding outward from the user's touch."; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } diff --git a/components/NavigationBar/examples/NavigationBarTypicalUseExample.m b/components/NavigationBar/examples/NavigationBarTypicalUseExample.m index 80927aed941..70cf67b66b3 100644 --- a/components/NavigationBar/examples/NavigationBarTypicalUseExample.m +++ b/components/NavigationBar/examples/NavigationBarTypicalUseExample.m @@ -1,5 +1,5 @@ /* - Copyright 2015-present Google Inc. All Rights Reserved. + 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. diff --git a/components/NavigationBar/examples/NavigationBarTypicalUseExample.swift b/components/NavigationBar/examples/NavigationBarTypicalUseExample.swift new file mode 100644 index 00000000000..154d736eeb3 --- /dev/null +++ b/components/NavigationBar/examples/NavigationBarTypicalUseExample.swift @@ -0,0 +1,59 @@ +/* +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 UIKit +import MaterialComponents.MaterialNavigationBar + +public class NavigationBarTypicalUseSwiftExample: NavigationBarTypicalUseExample { + + override public func viewDidLoad() { + + super.viewDidLoad() + view.backgroundColor = .whiteColor() + + title = "Navigation Bar (Swift)" + + navBar = MDCNavigationBar() + navBar!.observeNavigationItem(navigationItem) + navBar!.tintColor = .whiteColor() + + // Light blue 500 + navBar!.backgroundColor = UIColor.init(red: 0.012, green: 0.663, blue: 0.957, alpha: 1) + + view.addSubview(navBar!) + + navBar!.translatesAutoresizingMaskIntoConstraints = false + + let viewBindings = ["navBar" : navBar!] + + var constraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|[navBar]|", + options: [], metrics: nil, views: viewBindings) + constraints += NSLayoutConstraint.constraintsWithVisualFormat("V:|[navBar]", + options: [], metrics: nil, views: viewBindings) + + view.addConstraints(constraints) + self.setupExampleViews() + } + + override public func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + navigationController?.setNavigationBarHidden(true, animated: animated) + } + + override public func prefersStatusBarHidden() -> Bool { + return true + } +} diff --git a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.h b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.h index b8d9ed03fc4..d55c7af8590 100644 --- a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.h +++ b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.h @@ -1,3 +1,17 @@ +/*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. +*/ /* 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. @@ -10,8 +24,8 @@ @interface NavigationBarTypicalUseExample : UIViewController -@property(nonatomic) ExampleInstructionsViewNavigationBarTypicalUseExample *exampleView; -@property(nonatomic) MDCNavigationBar *navBar; +@property(nonatomic) ExampleInstructionsViewNavigationBarTypicalUseExample *_Nullable exampleView; +@property(nonatomic) MDCNavigationBar *_Nullable navBar; @end diff --git a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.m b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.m index 9917ee32966..e28dd472f7c 100644 --- a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.m +++ b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.m @@ -1,3 +1,17 @@ +/*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. + */ /* 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. @@ -58,7 +72,7 @@ + (NSString *)catalogDescription { " either a title label or a custom title view."; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } diff --git a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.swift b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.swift new file mode 100644 index 00000000000..70250dc492c --- /dev/null +++ b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.swift @@ -0,0 +1,58 @@ +/* +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. +*/ + +/* 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 Foundation +import MaterialComponents + +extension NavigationBarTypicalUseSwiftExample { + + // (CatalogByConvention) + + public class func catalogBreadcrumbs() -> [String] { + return [ "Navigation Bar", "Typical use (Swift)" ] + } + + public class func catalogDescription() -> String { + return "The Navigation Bar component is a view composed of a left and right Button Bar and" + " either a title label or a custom title view." + } + + public class func catalogIsPrimaryDemo() -> Bool { + return false + } + + public func catalogShouldHideNavigation() -> Bool { + return true + } + + override public func setupExampleViews() { + /// Both self.viewDidLoad() and super.viewDidLoad() will add NavigationBars to the hierarchy. + /// We only want to keep one. + + for subview in view.subviews { + if let navBarSubview = subview as? MDCNavigationBar where navBarSubview != self.navBar { + navBarSubview.removeFromSuperview() + } + } + super.setupExampleViews() + } + +} diff --git a/components/PageControl/examples/PageControlTypicalUseExample.m b/components/PageControl/examples/PageControlTypicalUseExample.m index 5683586dcd4..ea3d7a708bd 100644 --- a/components/PageControl/examples/PageControlTypicalUseExample.m +++ b/components/PageControl/examples/PageControlTypicalUseExample.m @@ -39,7 +39,7 @@ + (NSString *)catalogDescription { " experience influenced by Material Design."; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } diff --git a/components/RobotoFontLoader/examples/RobotoVsSystemExampleViewController.m b/components/RobotoFontLoader/examples/RobotoVsSystemExampleViewController.m index a0f9c5cf711..d556848d158 100644 --- a/components/RobotoFontLoader/examples/RobotoVsSystemExampleViewController.m +++ b/components/RobotoFontLoader/examples/RobotoVsSystemExampleViewController.m @@ -45,7 +45,7 @@ + (NSString *)catalogDescription { return @"The Roboto Font Loader lazy loads the Roboto font."; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } diff --git a/components/ShadowElevations/examples/ShadowElevationsTypicalUseExample.m b/components/ShadowElevations/examples/ShadowElevationsTypicalUseExample.m index 6a62a934af5..d724caf228b 100644 --- a/components/ShadowElevations/examples/ShadowElevationsTypicalUseExample.m +++ b/components/ShadowElevations/examples/ShadowElevationsTypicalUseExample.m @@ -147,7 +147,7 @@ + (NSString *)catalogDescription { return @"This component provides the most commonly-used Material Design elevations."; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } diff --git a/components/SpritedAnimationView/examples/SpritedAnimationViewTypicalUseViewController.m b/components/SpritedAnimationView/examples/SpritedAnimationViewTypicalUseViewController.m index a7f13a5af49..697733f534a 100644 --- a/components/SpritedAnimationView/examples/SpritedAnimationViewTypicalUseViewController.m +++ b/components/SpritedAnimationView/examples/SpritedAnimationViewTypicalUseViewController.m @@ -36,7 +36,7 @@ + (NSString *)catalogDescription { " UIImageView."; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } diff --git a/components/Switch/examples/SwitchSimpleExampleViewController.m b/components/Switch/examples/SwitchSimpleExampleViewController.m index f2199e0e261..6ebd97b6f03 100644 --- a/components/Switch/examples/SwitchSimpleExampleViewController.m +++ b/components/Switch/examples/SwitchSimpleExampleViewController.m @@ -76,7 +76,7 @@ + (NSString *)catalogDescription { " UISwitch."; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } From ed2cefc36953b7a720db589ee598a524d98108e1 Mon Sep 17 00:00:00 2001 From: Will Larche Date: Wed, 13 Apr 2016 15:20:14 -0400 Subject: [PATCH 004/129] [Catalog] Fix for cells staying selected when swiping for navigation Reviewers: ajsecord, #mdc_ios_owners Reviewed By: ajsecord, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D600 --- catalog/MDCCatalog/NodeViewController.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/catalog/MDCCatalog/NodeViewController.swift b/catalog/MDCCatalog/NodeViewController.swift index b470f091b21..6311c3c0ded 100644 --- a/catalog/MDCCatalog/NodeViewController.swift +++ b/catalog/MDCCatalog/NodeViewController.swift @@ -97,6 +97,17 @@ class NodeViewController: CBCNodeListViewController { super.viewWillAppear(animated) self.navigationController?.setNavigationBarHidden(true, animated: animated) + + if let selectedRowIndexPath = tableView.indexPathForSelectedRow, let _ = transitionCoordinator() { + transitionCoordinator()?.animateAlongsideTransition({ (context) -> Void in + self.tableView.deselectRowAtIndexPath(selectedRowIndexPath, animated: true) + }, completion: { (context) -> Void in + if context.isCancelled() { + self.tableView.selectRowAtIndexPath(selectedRowIndexPath, animated: false, + scrollPosition: .None) + } + }) + } } // MARK: UIScrollViewDelegate From a9a28c4cde35e476e886c1f90c44b96988c72f73 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Wed, 13 Apr 2016 15:41:13 -0400 Subject: [PATCH 005/129] [Catalog] Make catalogIsPrimaryDemo method in slider demo static to match other examples Reviewers: ajsecord, #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Subscribers: larche Projects: #material_components_ios Differential Revision: http://codereview.cc/D601 --- .../Slider/examples/supplemental/SliderTypicalUseSupplemental.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m b/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m index 2e856b59e71..d52d4cf8cb1 100644 --- a/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m +++ b/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m @@ -21,7 +21,7 @@ + (NSString *)catalogDescription { " continuous range or discrete set of values."; } -- (BOOL)catalogIsPrimaryDemo { ++ (BOOL)catalogIsPrimaryDemo { return YES; } From 7034aad77b980ab1c8706ad81422687af53715f5 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 13 Apr 2016 12:24:24 -0400 Subject: [PATCH 006/129] [ScrollViewDelegateMultiplexer] Disable deprecation warnings. Summary: Closes https://github.com/google/material-components-ios/issues/343. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D597 --- .../examples/SVDMTypicalUseExample.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/ScrollViewDelegateMultiplexer/examples/SVDMTypicalUseExample.m b/components/ScrollViewDelegateMultiplexer/examples/SVDMTypicalUseExample.m index 1912138cb36..0b9ad1fc884 100644 --- a/components/ScrollViewDelegateMultiplexer/examples/SVDMTypicalUseExample.m +++ b/components/ScrollViewDelegateMultiplexer/examples/SVDMTypicalUseExample.m @@ -30,7 +30,10 @@ @implementation SVDMTypicalUseViewController { UIPageControl *_pageControl; NSArray *_pages; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" MDCScrollViewDelegateMultiplexer *_multiplexer; +#pragma clang diagnostic pop } + (NSArray *)catalogBreadcrumbs { @@ -101,9 +104,12 @@ - (void)viewDidLoad { [self.view addSubview:_scrollView]; [self.view addSubview:pageControl]; - // Create scrollView delegate multiplexer and register observers +// Create scrollView delegate multiplexer and register observers +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" _multiplexer = [[MDCScrollViewDelegateMultiplexer alloc] init]; +#pragma clang diagnostic pop _scrollView.delegate = _multiplexer; [_multiplexer addObservingDelegate:self]; [_multiplexer addObservingDelegate:pageControl]; From 5187441e7d42f8006372dbd245e0f51ce1803c53 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 13 Apr 2016 11:49:00 -0400 Subject: [PATCH 007/129] [NavigationBar] Explain exception for UINavigationBar/MDCNavigationBar comparison. Summary: Closes https://github.com/google/material-components-ios/issues/286. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D593 --- components/NavigationBar/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/NavigationBar/README.md b/components/NavigationBar/README.md index 8731ec26570..ce0daf1f956 100644 --- a/components/NavigationBar/README.md +++ b/components/NavigationBar/README.md @@ -54,7 +54,11 @@ $ pod install ## Overview -Navigation Bar is designed to be a drop-in replacement for UINavigationBar. +Navigation Bar is a drop-in replacement for UINavigationBar with a few notable exceptions: + +- No navigationItem stack. Instances of MDCNavigationBar must be explicitly provided with a back + button. TODO(featherless): Explain how to create a back button with Navigation Bar once + https://github.com/google/material-components-ios/issues/340 lands. The MDCNavigationBar class is a composition of two [Button Bars](../ButtonBar) and a title label or title view. The left and right Button Bars are provided with the navigation item's corresponding bar From 0f2bbc3bda380051b96c5fb73344f32c093025bd Mon Sep 17 00:00:00 2001 From: Will Larche Date: Wed, 13 Apr 2016 15:44:25 -0400 Subject: [PATCH 008/129] [Pesto] Adding back in light content status bar Reviewers: #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D603 --- demos/Pesto/Pesto/Info.plist | 2 ++ demos/Pesto/Pesto/PestoAppDelegate.m | 7 ------- demos/Pesto/Pesto/PestoCollectionViewController.m | 5 ----- .../Pesto/PestoFlexibleHeaderContainerViewController.m | 5 ----- demos/Pesto/Pesto/PestoSettingsViewController.m | 4 ---- 5 files changed, 2 insertions(+), 21 deletions(-) diff --git a/demos/Pesto/Pesto/Info.plist b/demos/Pesto/Pesto/Info.plist index 95f195bad59..862232fff95 100644 --- a/demos/Pesto/Pesto/Info.plist +++ b/demos/Pesto/Pesto/Info.plist @@ -30,6 +30,8 @@ UIRequiresFullScreen + UIStatusBarStyle + UIStatusBarStyleLightContent UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/demos/Pesto/Pesto/PestoAppDelegate.m b/demos/Pesto/Pesto/PestoAppDelegate.m index 87ec7775d2d..c477ed12faa 100644 --- a/demos/Pesto/Pesto/PestoAppDelegate.m +++ b/demos/Pesto/Pesto/PestoAppDelegate.m @@ -25,13 +25,6 @@ @implementation PestoAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { -// setStatusBarHidden:withAnimation: was deprecated in iOS 9. -// Silence the related warning. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [[UIApplication sharedApplication] setStatusBarHidden:YES - withAnimation:NO]; -#pragma clang diagnostic pop PestoFlexibleHeaderContainerViewController *flexHeadContainerVC = [[PestoFlexibleHeaderContainerViewController alloc] init]; diff --git a/demos/Pesto/Pesto/PestoCollectionViewController.m b/demos/Pesto/Pesto/PestoCollectionViewController.m index 6b2f6827cb5..04205331e59 100644 --- a/demos/Pesto/Pesto/PestoCollectionViewController.m +++ b/demos/Pesto/Pesto/PestoCollectionViewController.m @@ -46,7 +46,6 @@ - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout { [self.collectionView registerClass:[PestoCardCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass([PestoCardCollectionViewCell class])]; _pestoData = [[PestoData alloc] init]; - [self setNeedsStatusBarAppearanceUpdate]; } return self; } @@ -74,10 +73,6 @@ - (NSInteger)collectionView:(UICollectionView *)view return (NSInteger)[self.pestoData.imageFileNames count]; } -- (BOOL)prefersStatusBarHidden { - return YES; -} - - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { [self.collectionView.collectionViewLayout invalidateLayout]; diff --git a/demos/Pesto/Pesto/PestoFlexibleHeaderContainerViewController.m b/demos/Pesto/Pesto/PestoFlexibleHeaderContainerViewController.m index bffdc6a8fb7..b5cbc584b77 100644 --- a/demos/Pesto/Pesto/PestoFlexibleHeaderContainerViewController.m +++ b/demos/Pesto/Pesto/PestoFlexibleHeaderContainerViewController.m @@ -57,7 +57,6 @@ - (instancetype)init { _collectionViewController = collectionVC; _collectionViewController.flexHeaderContainerVC = self; _collectionViewController.delegate = self; - [self setNeedsStatusBarAppearanceUpdate]; } return self; } @@ -93,10 +92,6 @@ - (void)viewDidLoad { [self.view insertSubview:self.zoomableView belowSubview:self.animatedMenuArrow]; } -- (BOOL)prefersStatusBarHidden { - return YES; -} - - (void)showMenu { self.sideView.hidden = NO; [self.sideView showSideView]; diff --git a/demos/Pesto/Pesto/PestoSettingsViewController.m b/demos/Pesto/Pesto/PestoSettingsViewController.m index 96dcfd1b7b7..b6044576100 100644 --- a/demos/Pesto/Pesto/PestoSettingsViewController.m +++ b/demos/Pesto/Pesto/PestoSettingsViewController.m @@ -178,10 +178,6 @@ - (void)viewDidLoad { self.appBar.headerViewController.view.backgroundColor = teal; self.appBar.headerViewController.headerView.trackingScrollView = self.settingsTableView; self.appBar.headerViewController.headerView.tintColor = [UIColor whiteColor]; - - // This app has a forced-hidden status bar. The headerView needs to compensate. - self.appBar.headerViewController.headerView.maximumHeight -= 20; - self.appBar.headerViewController.headerView.minimumHeight -= 20; } + (UIColor *)tableViewSeparatorColor { From c7a3891fe9754962cbbb14157cd5595ca5b0abec Mon Sep 17 00:00:00 2001 From: randallli Date: Wed, 13 Apr 2016 16:54:49 -0400 Subject: [PATCH 009/129] [AppBar]? Added NSLog to ensure that addChildViewController: is called before addSubviewsToParent Summary: First half of: https://github.com/google/material-components-ios/issues/341 Warning changes: NSLog is now in place and will attempt to explain why no App Bar displays. Reviewers: ajsecord, #mdc_ios_owners, featherless Reviewed By: ajsecord, #mdc_ios_owners, featherless Subscribers: featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D596 --- components/AppBar/src/MDCAppBar.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/AppBar/src/MDCAppBar.m b/components/AppBar/src/MDCAppBar.m index f27217a4344..8ece08dfa02 100644 --- a/components/AppBar/src/MDCAppBar.m +++ b/components/AppBar/src/MDCAppBar.m @@ -78,6 +78,13 @@ - (void)addHeaderViewControllerToParentViewController:(nonnull UIViewController - (void)addSubviewsToParent { MDCFlexibleHeaderViewController *fhvc = self.headerViewController; +#if DEBUG + if (!fhvc.parentViewController) { + NSLog(@"headerViewController does not have a parentViewController. " + @"Use [self addChildViewController:appBar.headerViewController]. " + @"This warning only appears in DEBUG builds"); + } +#endif //DEBUG if (fhvc.view.superview == fhvc.parentViewController.view) { return; } From 6d5406baac2d0bb484fff90882c39309a68bc744 Mon Sep 17 00:00:00 2001 From: Will Larche Date: Thu, 14 Apr 2016 08:49:48 -0400 Subject: [PATCH 010/129] [Examples] Correcting scope modifier of functions in Swift Reviewers: featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D607 --- .../NavigationBarTypicalUseExampleSupplemental.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.swift b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.swift index 70250dc492c..d1f30842d10 100644 --- a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.swift +++ b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.swift @@ -26,20 +26,20 @@ extension NavigationBarTypicalUseSwiftExample { // (CatalogByConvention) - public class func catalogBreadcrumbs() -> [String] { + class func catalogBreadcrumbs() -> [String] { return [ "Navigation Bar", "Typical use (Swift)" ] } - public class func catalogDescription() -> String { + class func catalogDescription() -> String { return "The Navigation Bar component is a view composed of a left and right Button Bar and" " either a title label or a custom title view." } - public class func catalogIsPrimaryDemo() -> Bool { + class func catalogIsPrimaryDemo() -> Bool { return false } - public func catalogShouldHideNavigation() -> Bool { + func catalogShouldHideNavigation() -> Bool { return true } From c0bd1de8e5520102f59e9b92d8a1085b285be38e Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Thu, 14 Apr 2016 10:53:10 -0400 Subject: [PATCH 011/129] [Icons] Replace + with _ in icon names Reviewers: ajsecord, featherless, #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D610 --- components/AppBar/src/MDCAppBar.m | 2 +- ...erialIcons+ic_arrow_back.h => MaterialIcons_ic_arrow_back.h} | 0 ...erialIcons+ic_arrow_back.m => MaterialIcons_ic_arrow_back.m} | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename components/private/Icons/icons/ic_arrow_back/src/{MaterialIcons+ic_arrow_back.h => MaterialIcons_ic_arrow_back.h} (100%) rename components/private/Icons/icons/ic_arrow_back/src/{MaterialIcons+ic_arrow_back.m => MaterialIcons_ic_arrow_back.m} (96%) diff --git a/components/AppBar/src/MDCAppBar.m b/components/AppBar/src/MDCAppBar.m index 8ece08dfa02..ae718af4310 100644 --- a/components/AppBar/src/MDCAppBar.m +++ b/components/AppBar/src/MDCAppBar.m @@ -23,7 +23,7 @@ #import "MDCAppBarContainerViewController.h" #import "MaterialFlexibleHeader.h" -#import "MaterialIcons+ic_arrow_back.h" +#import "MaterialIcons_ic_arrow_back.h" #import "MaterialShadowElevations.h" #import "MaterialShadowLayer.h" #import "MaterialTypography.h" diff --git a/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons+ic_arrow_back.h b/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons_ic_arrow_back.h similarity index 100% rename from components/private/Icons/icons/ic_arrow_back/src/MaterialIcons+ic_arrow_back.h rename to components/private/Icons/icons/ic_arrow_back/src/MaterialIcons_ic_arrow_back.h diff --git a/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons+ic_arrow_back.m b/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons_ic_arrow_back.m similarity index 96% rename from components/private/Icons/icons/ic_arrow_back/src/MaterialIcons+ic_arrow_back.m rename to components/private/Icons/icons/ic_arrow_back/src/MaterialIcons_ic_arrow_back.m index 9b7581ee40a..d56943dd10c 100644 --- a/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons+ic_arrow_back.m +++ b/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons_ic_arrow_back.m @@ -17,7 +17,7 @@ // This file was automatically generated by running scripts/sync_icons.sh // Do not modify directly. -#import "MaterialIcons+ic_arrow_back.h" +#import "MaterialIcons_ic_arrow_back.h" #import "MDCIcons+BundleLoader.h" From 9f45a92a41914f8f720194228d67aa26291a5895 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Thu, 14 Apr 2016 11:01:13 -0400 Subject: [PATCH 012/129] Hand-modified CHANGELOG.md API diff. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dec3b8ec10d..070fa84e3f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## release-candidate + ## 4.0.0 # 4.0.0 From 080ca78d6766b082bab0f7c4ec50273fb2dd8ab5 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Thu, 14 Apr 2016 11:03:27 -0400 Subject: [PATCH 013/129] Hand-modified CHANGELOG.md API diff. --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 070fa84e3f4..e7423ef4e3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ ## release-candidate -## 4.0.0 +# 4.1.0 + +Updated arrow back icon names. No API changes. + +Auto-generated by running: + + scripts/api_diff -o 1542251633905c3c2089b38f1c01a5010a8894f1 -n c0bd1de8e5520102f59e9b92d8a1085b285be38e # 4.0.0 From f8f157b9c2b54435897484dcc82ce906ff9f4e05 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Thu, 14 Apr 2016 11:04:45 -0400 Subject: [PATCH 014/129] Bumped version number to 4.1.0 --- CHANGELOG.md | 2 +- MaterialComponents.podspec | 2 +- MaterialComponentsCatalog.podspec | 2 +- MaterialComponentsUnitTests.podspec | 2 +- catalog/Podfile.lock | 104 ++++++++++++++-------------- demos/Pesto/Podfile.lock | 96 ++++++++++++------------- demos/Shrine/Podfile.lock | 96 ++++++++++++------------- 7 files changed, 152 insertions(+), 152 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7423ef4e3f..cac361e0872 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## release-candidate +## release-candidate 4.1.0 # 4.1.0 diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec index 0005a1bd18b..fe8ea29e6e9 100644 --- a/MaterialComponents.podspec +++ b/MaterialComponents.podspec @@ -2,7 +2,7 @@ load 'scripts/generated/icons.rb' Pod::Spec.new do |s| s.name = "MaterialComponents" - s.version = "4.0.0" + s.version = "4.1.0" s.authors = { 'Apple platform engineering at Google' => 'appleplatforms@google.com' } s.summary = "A collection of stand-alone production-ready UI libraries focused on design details." s.homepage = "https://github.com/google/material-components-ios" diff --git a/MaterialComponentsCatalog.podspec b/MaterialComponentsCatalog.podspec index f5e87144f43..8c1bdaee451 100644 --- a/MaterialComponentsCatalog.podspec +++ b/MaterialComponentsCatalog.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialComponentsCatalog" - s.version = "4.0.0" + s.version = "4.1.0" s.authors = { 'Apple platform engineering at Google' => 'appleplatforms@google.com' } s.summary = "A collection of stand-alone production-ready UI libraries focused on design details." s.homepage = "https://github.com/google/material-components-ios" diff --git a/MaterialComponentsUnitTests.podspec b/MaterialComponentsUnitTests.podspec index 651d662bf7b..c124c0ad206 100644 --- a/MaterialComponentsUnitTests.podspec +++ b/MaterialComponentsUnitTests.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialComponentsUnitTests" - s.version = "4.0.0" + s.version = "4.1.0" s.authors = { 'Apple platform engineering at Google' => 'appleplatforms@google.com' } s.summary = "A collection of stand-alone production-ready UI libraries focused on design details." s.homepage = "https://github.com/google/material-components-ios" diff --git a/catalog/Podfile.lock b/catalog/Podfile.lock index afddaf6b80b..8b0f7265bb1 100644 --- a/catalog/Podfile.lock +++ b/catalog/Podfile.lock @@ -1,24 +1,24 @@ PODS: - - MaterialComponents (4.0.0): - - MaterialComponents/AppBar (= 4.0.0) - - MaterialComponents/ButtonBar (= 4.0.0) - - MaterialComponents/Buttons (= 4.0.0) - - MaterialComponents/FlexibleHeader (= 4.0.0) - - MaterialComponents/FontDiskLoader (= 4.0.0) - - MaterialComponents/HeaderStackView (= 4.0.0) - - MaterialComponents/Ink (= 4.0.0) - - MaterialComponents/NavigationBar (= 4.0.0) - - MaterialComponents/PageControl (= 4.0.0) - - MaterialComponents/private (= 4.0.0) - - MaterialComponents/RobotoFontLoader (= 4.0.0) - - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.0.0) - - MaterialComponents/ShadowElevations (= 4.0.0) - - MaterialComponents/ShadowLayer (= 4.0.0) - - MaterialComponents/Slider (= 4.0.0) - - MaterialComponents/SpritedAnimationView (= 4.0.0) - - MaterialComponents/Switch (= 4.0.0) - - MaterialComponents/Typography (= 4.0.0) - - MaterialComponents/AppBar (4.0.0): + - MaterialComponents (4.1.0): + - MaterialComponents/AppBar (= 4.1.0) + - MaterialComponents/ButtonBar (= 4.1.0) + - MaterialComponents/Buttons (= 4.1.0) + - MaterialComponents/FlexibleHeader (= 4.1.0) + - MaterialComponents/FontDiskLoader (= 4.1.0) + - MaterialComponents/HeaderStackView (= 4.1.0) + - MaterialComponents/Ink (= 4.1.0) + - MaterialComponents/NavigationBar (= 4.1.0) + - MaterialComponents/PageControl (= 4.1.0) + - MaterialComponents/private (= 4.1.0) + - MaterialComponents/RobotoFontLoader (= 4.1.0) + - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.1.0) + - MaterialComponents/ShadowElevations (= 4.1.0) + - MaterialComponents/ShadowLayer (= 4.1.0) + - MaterialComponents/Slider (= 4.1.0) + - MaterialComponents/SpritedAnimationView (= 4.1.0) + - MaterialComponents/Switch (= 4.1.0) + - MaterialComponents/Typography (= 4.1.0) + - MaterialComponents/AppBar (4.1.0): - MaterialComponents/FlexibleHeader - MaterialComponents/HeaderStackView - MaterialComponents/NavigationBar @@ -26,52 +26,52 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/ButtonBar (4.0.0): + - MaterialComponents/ButtonBar (4.1.0): - MaterialComponents/Buttons - - MaterialComponents/Buttons (4.0.0): + - MaterialComponents/Buttons (4.1.0): - MaterialComponents/Ink - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/FlexibleHeader (4.0.0) - - MaterialComponents/FontDiskLoader (4.0.0) - - MaterialComponents/HeaderStackView (4.0.0) - - MaterialComponents/Ink (4.0.0) - - MaterialComponents/NavigationBar (4.0.0): + - MaterialComponents/FlexibleHeader (4.1.0) + - MaterialComponents/FontDiskLoader (4.1.0) + - MaterialComponents/HeaderStackView (4.1.0) + - MaterialComponents/Ink (4.1.0) + - MaterialComponents/NavigationBar (4.1.0): - MaterialComponents/ButtonBar - MaterialComponents/Typography - - MaterialComponents/PageControl (4.0.0) - - MaterialComponents/private (4.0.0): - - MaterialComponents/private/Color (= 4.0.0) - - MaterialComponents/private/Icons (= 4.0.0) - - MaterialComponents/private/ThumbTrack (= 4.0.0) - - MaterialComponents/private/Color (4.0.0) - - MaterialComponents/private/Icons (4.0.0): - - MaterialComponents/private/Icons/Base (= 4.0.0) - - MaterialComponents/private/Icons/ic_arrow_back (= 4.0.0) - - MaterialComponents/private/Icons/Base (4.0.0) - - MaterialComponents/private/Icons/ic_arrow_back (4.0.0): + - MaterialComponents/PageControl (4.1.0) + - MaterialComponents/private (4.1.0): + - MaterialComponents/private/Color (= 4.1.0) + - MaterialComponents/private/Icons (= 4.1.0) + - MaterialComponents/private/ThumbTrack (= 4.1.0) + - MaterialComponents/private/Color (4.1.0) + - MaterialComponents/private/Icons (4.1.0): + - MaterialComponents/private/Icons/Base (= 4.1.0) + - MaterialComponents/private/Icons/ic_arrow_back (= 4.1.0) + - MaterialComponents/private/Icons/Base (4.1.0) + - MaterialComponents/private/Icons/ic_arrow_back (4.1.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/ThumbTrack (4.0.0): + - MaterialComponents/private/ThumbTrack (4.1.0): - MaterialComponents/Ink - MaterialComponents/private/Color - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - - MaterialComponents/RobotoFontLoader (4.0.0): + - MaterialComponents/RobotoFontLoader (4.1.0): - MaterialComponents/FontDiskLoader - MaterialComponents/Typography - - MaterialComponents/ScrollViewDelegateMultiplexer (4.0.0) - - MaterialComponents/ShadowElevations (4.0.0) - - MaterialComponents/ShadowLayer (4.0.0) - - MaterialComponents/Slider (4.0.0): + - MaterialComponents/ScrollViewDelegateMultiplexer (4.1.0) + - MaterialComponents/ShadowElevations (4.1.0) + - MaterialComponents/ShadowLayer (4.1.0) + - MaterialComponents/Slider (4.1.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/SpritedAnimationView (4.0.0) - - MaterialComponents/Switch (4.0.0): + - MaterialComponents/SpritedAnimationView (4.1.0) + - MaterialComponents/Switch (4.1.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/Typography (4.0.0) - - MaterialComponentsCatalog (4.0.0): + - MaterialComponents/Typography (4.1.0) + - MaterialComponentsCatalog (4.1.0): - MaterialComponents - - MaterialComponentsUnitTests (4.0.0): + - MaterialComponentsUnitTests (4.1.0): - MaterialComponents DEPENDENCIES: @@ -88,8 +88,8 @@ EXTERNAL SOURCES: :path: ../ SPEC CHECKSUMS: - MaterialComponents: b9f68b528c394b14b774591be1de18e25648c38a - MaterialComponentsCatalog: 82dc510292fedfd8a6c296b2d82da20dc24f0ef9 - MaterialComponentsUnitTests: 4cfd5c80aaf7b3f8155fc4052ad2ee153bac74fe + MaterialComponents: 909ae0687c67a89e70cfece537a752fac5a5efa2 + MaterialComponentsCatalog: 1e150e604b7143d4853f54da0e268a5c886d2f7c + MaterialComponentsUnitTests: 28f0e603d1b643f958bddb6608c75afb81c0c6c0 COCOAPODS: 0.39.0 diff --git a/demos/Pesto/Podfile.lock b/demos/Pesto/Podfile.lock index db4ecdc5e0b..d4bd0d48667 100644 --- a/demos/Pesto/Podfile.lock +++ b/demos/Pesto/Podfile.lock @@ -1,24 +1,24 @@ PODS: - - MaterialComponents (4.0.0): - - MaterialComponents/AppBar (= 4.0.0) - - MaterialComponents/ButtonBar (= 4.0.0) - - MaterialComponents/Buttons (= 4.0.0) - - MaterialComponents/FlexibleHeader (= 4.0.0) - - MaterialComponents/FontDiskLoader (= 4.0.0) - - MaterialComponents/HeaderStackView (= 4.0.0) - - MaterialComponents/Ink (= 4.0.0) - - MaterialComponents/NavigationBar (= 4.0.0) - - MaterialComponents/PageControl (= 4.0.0) - - MaterialComponents/private (= 4.0.0) - - MaterialComponents/RobotoFontLoader (= 4.0.0) - - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.0.0) - - MaterialComponents/ShadowElevations (= 4.0.0) - - MaterialComponents/ShadowLayer (= 4.0.0) - - MaterialComponents/Slider (= 4.0.0) - - MaterialComponents/SpritedAnimationView (= 4.0.0) - - MaterialComponents/Switch (= 4.0.0) - - MaterialComponents/Typography (= 4.0.0) - - MaterialComponents/AppBar (4.0.0): + - MaterialComponents (4.1.0): + - MaterialComponents/AppBar (= 4.1.0) + - MaterialComponents/ButtonBar (= 4.1.0) + - MaterialComponents/Buttons (= 4.1.0) + - MaterialComponents/FlexibleHeader (= 4.1.0) + - MaterialComponents/FontDiskLoader (= 4.1.0) + - MaterialComponents/HeaderStackView (= 4.1.0) + - MaterialComponents/Ink (= 4.1.0) + - MaterialComponents/NavigationBar (= 4.1.0) + - MaterialComponents/PageControl (= 4.1.0) + - MaterialComponents/private (= 4.1.0) + - MaterialComponents/RobotoFontLoader (= 4.1.0) + - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.1.0) + - MaterialComponents/ShadowElevations (= 4.1.0) + - MaterialComponents/ShadowLayer (= 4.1.0) + - MaterialComponents/Slider (= 4.1.0) + - MaterialComponents/SpritedAnimationView (= 4.1.0) + - MaterialComponents/Switch (= 4.1.0) + - MaterialComponents/Typography (= 4.1.0) + - MaterialComponents/AppBar (4.1.0): - MaterialComponents/FlexibleHeader - MaterialComponents/HeaderStackView - MaterialComponents/NavigationBar @@ -26,49 +26,49 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/ButtonBar (4.0.0): + - MaterialComponents/ButtonBar (4.1.0): - MaterialComponents/Buttons - - MaterialComponents/Buttons (4.0.0): + - MaterialComponents/Buttons (4.1.0): - MaterialComponents/Ink - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/FlexibleHeader (4.0.0) - - MaterialComponents/FontDiskLoader (4.0.0) - - MaterialComponents/HeaderStackView (4.0.0) - - MaterialComponents/Ink (4.0.0) - - MaterialComponents/NavigationBar (4.0.0): + - MaterialComponents/FlexibleHeader (4.1.0) + - MaterialComponents/FontDiskLoader (4.1.0) + - MaterialComponents/HeaderStackView (4.1.0) + - MaterialComponents/Ink (4.1.0) + - MaterialComponents/NavigationBar (4.1.0): - MaterialComponents/ButtonBar - MaterialComponents/Typography - - MaterialComponents/PageControl (4.0.0) - - MaterialComponents/private (4.0.0): - - MaterialComponents/private/Color (= 4.0.0) - - MaterialComponents/private/Icons (= 4.0.0) - - MaterialComponents/private/ThumbTrack (= 4.0.0) - - MaterialComponents/private/Color (4.0.0) - - MaterialComponents/private/Icons (4.0.0): - - MaterialComponents/private/Icons/Base (= 4.0.0) - - MaterialComponents/private/Icons/ic_arrow_back (= 4.0.0) - - MaterialComponents/private/Icons/Base (4.0.0) - - MaterialComponents/private/Icons/ic_arrow_back (4.0.0): + - MaterialComponents/PageControl (4.1.0) + - MaterialComponents/private (4.1.0): + - MaterialComponents/private/Color (= 4.1.0) + - MaterialComponents/private/Icons (= 4.1.0) + - MaterialComponents/private/ThumbTrack (= 4.1.0) + - MaterialComponents/private/Color (4.1.0) + - MaterialComponents/private/Icons (4.1.0): + - MaterialComponents/private/Icons/Base (= 4.1.0) + - MaterialComponents/private/Icons/ic_arrow_back (= 4.1.0) + - MaterialComponents/private/Icons/Base (4.1.0) + - MaterialComponents/private/Icons/ic_arrow_back (4.1.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/ThumbTrack (4.0.0): + - MaterialComponents/private/ThumbTrack (4.1.0): - MaterialComponents/Ink - MaterialComponents/private/Color - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - - MaterialComponents/RobotoFontLoader (4.0.0): + - MaterialComponents/RobotoFontLoader (4.1.0): - MaterialComponents/FontDiskLoader - MaterialComponents/Typography - - MaterialComponents/ScrollViewDelegateMultiplexer (4.0.0) - - MaterialComponents/ShadowElevations (4.0.0) - - MaterialComponents/ShadowLayer (4.0.0) - - MaterialComponents/Slider (4.0.0): + - MaterialComponents/ScrollViewDelegateMultiplexer (4.1.0) + - MaterialComponents/ShadowElevations (4.1.0) + - MaterialComponents/ShadowLayer (4.1.0) + - MaterialComponents/Slider (4.1.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/SpritedAnimationView (4.0.0) - - MaterialComponents/Switch (4.0.0): + - MaterialComponents/SpritedAnimationView (4.1.0) + - MaterialComponents/Switch (4.1.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/Typography (4.0.0) + - MaterialComponents/Typography (4.1.0) DEPENDENCIES: - MaterialComponents (from `../../`) @@ -78,6 +78,6 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - MaterialComponents: b9f68b528c394b14b774591be1de18e25648c38a + MaterialComponents: 909ae0687c67a89e70cfece537a752fac5a5efa2 COCOAPODS: 0.39.0 diff --git a/demos/Shrine/Podfile.lock b/demos/Shrine/Podfile.lock index db4ecdc5e0b..d4bd0d48667 100644 --- a/demos/Shrine/Podfile.lock +++ b/demos/Shrine/Podfile.lock @@ -1,24 +1,24 @@ PODS: - - MaterialComponents (4.0.0): - - MaterialComponents/AppBar (= 4.0.0) - - MaterialComponents/ButtonBar (= 4.0.0) - - MaterialComponents/Buttons (= 4.0.0) - - MaterialComponents/FlexibleHeader (= 4.0.0) - - MaterialComponents/FontDiskLoader (= 4.0.0) - - MaterialComponents/HeaderStackView (= 4.0.0) - - MaterialComponents/Ink (= 4.0.0) - - MaterialComponents/NavigationBar (= 4.0.0) - - MaterialComponents/PageControl (= 4.0.0) - - MaterialComponents/private (= 4.0.0) - - MaterialComponents/RobotoFontLoader (= 4.0.0) - - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.0.0) - - MaterialComponents/ShadowElevations (= 4.0.0) - - MaterialComponents/ShadowLayer (= 4.0.0) - - MaterialComponents/Slider (= 4.0.0) - - MaterialComponents/SpritedAnimationView (= 4.0.0) - - MaterialComponents/Switch (= 4.0.0) - - MaterialComponents/Typography (= 4.0.0) - - MaterialComponents/AppBar (4.0.0): + - MaterialComponents (4.1.0): + - MaterialComponents/AppBar (= 4.1.0) + - MaterialComponents/ButtonBar (= 4.1.0) + - MaterialComponents/Buttons (= 4.1.0) + - MaterialComponents/FlexibleHeader (= 4.1.0) + - MaterialComponents/FontDiskLoader (= 4.1.0) + - MaterialComponents/HeaderStackView (= 4.1.0) + - MaterialComponents/Ink (= 4.1.0) + - MaterialComponents/NavigationBar (= 4.1.0) + - MaterialComponents/PageControl (= 4.1.0) + - MaterialComponents/private (= 4.1.0) + - MaterialComponents/RobotoFontLoader (= 4.1.0) + - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.1.0) + - MaterialComponents/ShadowElevations (= 4.1.0) + - MaterialComponents/ShadowLayer (= 4.1.0) + - MaterialComponents/Slider (= 4.1.0) + - MaterialComponents/SpritedAnimationView (= 4.1.0) + - MaterialComponents/Switch (= 4.1.0) + - MaterialComponents/Typography (= 4.1.0) + - MaterialComponents/AppBar (4.1.0): - MaterialComponents/FlexibleHeader - MaterialComponents/HeaderStackView - MaterialComponents/NavigationBar @@ -26,49 +26,49 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/ButtonBar (4.0.0): + - MaterialComponents/ButtonBar (4.1.0): - MaterialComponents/Buttons - - MaterialComponents/Buttons (4.0.0): + - MaterialComponents/Buttons (4.1.0): - MaterialComponents/Ink - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/FlexibleHeader (4.0.0) - - MaterialComponents/FontDiskLoader (4.0.0) - - MaterialComponents/HeaderStackView (4.0.0) - - MaterialComponents/Ink (4.0.0) - - MaterialComponents/NavigationBar (4.0.0): + - MaterialComponents/FlexibleHeader (4.1.0) + - MaterialComponents/FontDiskLoader (4.1.0) + - MaterialComponents/HeaderStackView (4.1.0) + - MaterialComponents/Ink (4.1.0) + - MaterialComponents/NavigationBar (4.1.0): - MaterialComponents/ButtonBar - MaterialComponents/Typography - - MaterialComponents/PageControl (4.0.0) - - MaterialComponents/private (4.0.0): - - MaterialComponents/private/Color (= 4.0.0) - - MaterialComponents/private/Icons (= 4.0.0) - - MaterialComponents/private/ThumbTrack (= 4.0.0) - - MaterialComponents/private/Color (4.0.0) - - MaterialComponents/private/Icons (4.0.0): - - MaterialComponents/private/Icons/Base (= 4.0.0) - - MaterialComponents/private/Icons/ic_arrow_back (= 4.0.0) - - MaterialComponents/private/Icons/Base (4.0.0) - - MaterialComponents/private/Icons/ic_arrow_back (4.0.0): + - MaterialComponents/PageControl (4.1.0) + - MaterialComponents/private (4.1.0): + - MaterialComponents/private/Color (= 4.1.0) + - MaterialComponents/private/Icons (= 4.1.0) + - MaterialComponents/private/ThumbTrack (= 4.1.0) + - MaterialComponents/private/Color (4.1.0) + - MaterialComponents/private/Icons (4.1.0): + - MaterialComponents/private/Icons/Base (= 4.1.0) + - MaterialComponents/private/Icons/ic_arrow_back (= 4.1.0) + - MaterialComponents/private/Icons/Base (4.1.0) + - MaterialComponents/private/Icons/ic_arrow_back (4.1.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/ThumbTrack (4.0.0): + - MaterialComponents/private/ThumbTrack (4.1.0): - MaterialComponents/Ink - MaterialComponents/private/Color - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - - MaterialComponents/RobotoFontLoader (4.0.0): + - MaterialComponents/RobotoFontLoader (4.1.0): - MaterialComponents/FontDiskLoader - MaterialComponents/Typography - - MaterialComponents/ScrollViewDelegateMultiplexer (4.0.0) - - MaterialComponents/ShadowElevations (4.0.0) - - MaterialComponents/ShadowLayer (4.0.0) - - MaterialComponents/Slider (4.0.0): + - MaterialComponents/ScrollViewDelegateMultiplexer (4.1.0) + - MaterialComponents/ShadowElevations (4.1.0) + - MaterialComponents/ShadowLayer (4.1.0) + - MaterialComponents/Slider (4.1.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/SpritedAnimationView (4.0.0) - - MaterialComponents/Switch (4.0.0): + - MaterialComponents/SpritedAnimationView (4.1.0) + - MaterialComponents/Switch (4.1.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/Typography (4.0.0) + - MaterialComponents/Typography (4.1.0) DEPENDENCIES: - MaterialComponents (from `../../`) @@ -78,6 +78,6 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - MaterialComponents: b9f68b528c394b14b774591be1de18e25648c38a + MaterialComponents: 909ae0687c67a89e70cfece537a752fac5a5efa2 COCOAPODS: 0.39.0 From 1b35fe48b4f411c049689b398711682513b954d1 Mon Sep 17 00:00:00 2001 From: Louis Romero Date: Sat, 9 Apr 2016 02:33:35 -0400 Subject: [PATCH 015/129] Remove mention of deprecated API. Summary: Doc mentioned a deprecated API. Reviewers: featherless, #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D552 --- components/AppBar/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/AppBar/README.md b/components/AppBar/README.md index e308849f987..982c932184c 100644 --- a/components/AppBar/README.md +++ b/components/AppBar/README.md @@ -202,7 +202,7 @@ Flexible Header view's background color. Learn more by reading the section on ### UINavigationItem and the App Bar The App Bar begins mirroring the state of your view controller's `navigationItem` in the provided -`navigationBar` once you call `MDCAppBarAddViews`. +`navigationBar` once you call `addSubviewsToParent`. Learn more by reading the Navigation Bar section on [Observing UINavigationItem instances](../NavigationBar/#observing-uinavigationitem-instances). @@ -233,7 +233,7 @@ background image. This is not trivial to do with the App Bar APIs due to considerations being discussed in [Issue #184](https://github.com/google/material-components-ios/issues/184). -The heart of the limitation is that we're using a view (`headerStackView) to lay out the Navigation +The heart of the limitation is that we're using a view (`headerStackView`) to lay out the Navigation Bar. If you add a background view behind the `headerStackView` instance then `headerStackView` will end up eating all of your touch events. From 784e9a57be01dfa8f93528aa4a92de0b772b4605 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Thu, 14 Apr 2016 11:34:06 -0400 Subject: [PATCH 016/129] [Catalog] Use slider in variable names in example layout code Reviewers: featherless, ajsecord, #mdc_ios_owners Reviewed By: ajsecord, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D609 --- .../SliderTypicalUseSupplemental.m | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m b/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m index d52d4cf8cb1..0013f37b23b 100644 --- a/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m +++ b/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m @@ -30,33 +30,33 @@ + (BOOL)catalogIsPrimaryDemo { @implementation SliderTypicalUseViewController (Supplemental) - (void)setupExampleViews { - UILabel *raisedButtonLabel = [[UILabel alloc] init]; - raisedButtonLabel.text = @"Slider"; - raisedButtonLabel.font = [MDCTypography captionFont]; - raisedButtonLabel.alpha = [MDCTypography captionFontOpacity]; - [raisedButtonLabel sizeToFit]; - raisedButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:raisedButtonLabel]; - - UILabel *disabledSliderButtonLabel = [[UILabel alloc] init]; - disabledSliderButtonLabel.text = @"Slider Disabled"; - disabledSliderButtonLabel.font = [MDCTypography captionFont]; - disabledSliderButtonLabel.alpha = [MDCTypography captionFontOpacity]; - [disabledSliderButtonLabel sizeToFit]; - disabledSliderButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:disabledSliderButtonLabel]; + UILabel *sliderLabel = [[UILabel alloc] init]; + sliderLabel.text = @"Slider"; + sliderLabel.font = [MDCTypography captionFont]; + sliderLabel.alpha = [MDCTypography captionFontOpacity]; + [sliderLabel sizeToFit]; + sliderLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:sliderLabel]; + + UILabel *disabledSliderLabel = [[UILabel alloc] init]; + disabledSliderLabel.text = @"Slider Disabled"; + disabledSliderLabel.font = [MDCTypography captionFont]; + disabledSliderLabel.alpha = [MDCTypography captionFontOpacity]; + [disabledSliderLabel sizeToFit]; + disabledSliderLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:disabledSliderLabel]; NSDictionary *views = @{ @"slider" : self.slider, - @"label" : raisedButtonLabel, + @"label" : sliderLabel, @"disabledSlider" : self.disabledSlider, - @"disabledLabel" : disabledSliderButtonLabel + @"disabledSliderLabel" : disabledSliderLabel }; NSDictionary *metrics = @{ @"smallVMargin" : @24.0, @"largeVMargin" : @56.0, @"smallHMargin" : @24.0, - @"buttonHeight" : @(self.slider.bounds.size.height) }; + @"sliderHeight" : @(self.slider.bounds.size.height) }; // Vertical column of sliders NSString *sliderLayoutConstraints = @@ -64,7 +64,7 @@ - (void)setupExampleViews { // Vertical column of labels NSString *labelLayoutConstraints = - @"V:[label(buttonHeight)]-smallVMargin-[disabledLabel(buttonHeight)]"; + @"V:[label(sliderHeight)]-smallVMargin-[disabledSliderLabel(sliderHeight)]"; // Horizontal alignment between the two columns NSString *columnConstraints = @"[label(100)]-smallHMargin-[slider]"; @@ -79,7 +79,7 @@ - (void)setupExampleViews { multiplier:1.f constant:12.f]]; - // Center view vertically on the flat button (it's the middlemost) + // Center view vertically [self.view addConstraint: [NSLayoutConstraint constraintWithItem:self.disabledSlider attribute:NSLayoutAttributeBottom @@ -108,7 +108,7 @@ - (void)setupExampleViews { [NSLayoutConstraint constraintWithItem:self.slider attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual - toItem:raisedButtonLabel + toItem:sliderLabel attribute:NSLayoutAttributeCenterY multiplier:1.f constant:0.f]]; From e700bf58ee43e35d34b565b9b04f0af4b03f1288 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Thu, 14 Apr 2016 11:34:47 -0400 Subject: [PATCH 017/129] [Catalog and Switch] Update switch demo, move layout code to supplemental, update switch color Summary: Screenshot http://codereview.cc/M22 Reviewers: featherless, #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Subscribers: larche Projects: #material_components_ios Differential Revision: http://codereview.cc/D608 --- .../SwitchSimpleExampleViewController.m | 11 +- components/Switch/examples/SwitchTypicalUse.m | 69 +++++++++ .../SwitchTypicalUseSupplemental.h | 23 +++ .../SwitchTypicalUseSupplemental.m | 134 ++++++++++++++++++ components/Switch/src/MDCSwitch.m | 2 +- 5 files changed, 228 insertions(+), 11 deletions(-) create mode 100644 components/Switch/examples/SwitchTypicalUse.m create mode 100644 components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.h create mode 100644 components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m diff --git a/components/Switch/examples/SwitchSimpleExampleViewController.m b/components/Switch/examples/SwitchSimpleExampleViewController.m index 6ebd97b6f03..374aaf6e8bc 100644 --- a/components/Switch/examples/SwitchSimpleExampleViewController.m +++ b/components/Switch/examples/SwitchSimpleExampleViewController.m @@ -68,16 +68,7 @@ - (void)didChangeSliderValue:(id)sender { #pragma mark catalg by convention + (NSArray *)catalogBreadcrumbs { - return @[ @"Switch", @"Switch" ]; -} - -+ (NSString *)catalogDescription { - return @"Switch provides an Material Design on/off switch control with an interface similar to" - " UISwitch."; -} - -+ (BOOL)catalogIsPrimaryDemo { - return YES; + return @[ @"Switch", @"MDCSwitch and UISwitch Compared" ]; } @end diff --git a/components/Switch/examples/SwitchTypicalUse.m b/components/Switch/examples/SwitchTypicalUse.m new file mode 100644 index 00000000000..80ec9a491e1 --- /dev/null +++ b/components/Switch/examples/SwitchTypicalUse.m @@ -0,0 +1,69 @@ +/* + Copyright 2015-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 "MaterialSwitch.h" + +#import "SwitchTypicalUseSupplemental.h" + +@implementation SwitchTypicalUseViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + self.view.backgroundColor = [UIColor whiteColor]; + + // Switch + + self.switchComponent = [[MDCSwitch alloc] init]; + [self.switchComponent setOn:YES]; + [self.switchComponent addTarget:self + action:@selector(didChangeSliderValue:) + forControlEvents:UIControlEventValueChanged]; + [self.view addSubview:self.switchComponent]; + + // Switch custom color + + self.colorSwitchComponent = [[MDCSwitch alloc] init]; + self.colorSwitchComponent.onTintColor = [UIColor colorWithRed:0 green:0.47f blue:0.9f alpha:1]; + [self.colorSwitchComponent setOn:YES]; + [self.colorSwitchComponent addTarget:self + action:@selector(didChangeSliderValue:) + forControlEvents:UIControlEventValueChanged]; + [self.view addSubview:self.colorSwitchComponent]; + + // Switch disabled + + self.disabledSwitchComponent = [[MDCSwitch alloc] init]; + [self.disabledSwitchComponent addTarget:self + action:@selector(didChangeSliderValue:) + forControlEvents:UIControlEventValueChanged]; + self.disabledSwitchComponent.enabled = NO; + [self.view addSubview:self.disabledSwitchComponent]; + + self.switchComponent.translatesAutoresizingMaskIntoConstraints = NO; + self.colorSwitchComponent.translatesAutoresizingMaskIntoConstraints = NO; + self.disabledSwitchComponent.translatesAutoresizingMaskIntoConstraints = NO; + + [self setupExampleViews]; +} + +- (void)didChangeSliderValue:(id)sender { + UISwitch *slider = sender; + NSLog(@"did change %@ value: %d", NSStringFromClass([sender class]), slider.isOn); +} + +@end diff --git a/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.h b/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.h new file mode 100644 index 00000000000..25d6681a12d --- /dev/null +++ b/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.h @@ -0,0 +1,23 @@ +/* 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 MDCSwitch; +@class SwitchTypicalUseViewController; + +@interface SwitchTypicalUseViewController : UIViewController + +@property(nonatomic) MDCSwitch *switchComponent; +@property(nonatomic) MDCSwitch *colorSwitchComponent; +@property(nonatomic) MDCSwitch *disabledSwitchComponent; + +@end + +@interface SwitchTypicalUseViewController (Supplemental) + +- (void)setupExampleViews; + +@end diff --git a/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m b/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m new file mode 100644 index 00000000000..f6eeca02927 --- /dev/null +++ b/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m @@ -0,0 +1,134 @@ +/* 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 "SwitchTypicalUseSupplemental.h" +#import "MaterialTypography.h" + +#pragma mark - SwitchTypicalUseViewController + +@implementation SwitchTypicalUseViewController (CatalogByConvention) + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Switch", @"Switch" ]; +} + ++ (NSString *)catalogDescription { + return @"The MDCSlider object is a Material Design control used to select a value from a" + " continuous range or discrete set of values."; +} + ++ (BOOL)catalogIsPrimaryDemo { + return YES; +} + +@end + +@implementation SwitchTypicalUseViewController (Supplemental) + +- (void)setupExampleViews { + UILabel *switchLabel = [[UILabel alloc] init]; + switchLabel.text = @"Slider"; + switchLabel.font = [MDCTypography captionFont]; + switchLabel.alpha = [MDCTypography captionFontOpacity]; + [switchLabel sizeToFit]; + switchLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:switchLabel]; + + UILabel *colorSwitchButtonLabel = [[UILabel alloc] init]; + colorSwitchButtonLabel.text = @"Custom Color"; + colorSwitchButtonLabel.font = [MDCTypography captionFont]; + colorSwitchButtonLabel.alpha = [MDCTypography captionFontOpacity]; + [colorSwitchButtonLabel sizeToFit]; + colorSwitchButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:colorSwitchButtonLabel]; + + UILabel *disabledSwitchButtonLabel = [[UILabel alloc] init]; + disabledSwitchButtonLabel.text = @"Disabled"; + disabledSwitchButtonLabel.font = [MDCTypography captionFont]; + disabledSwitchButtonLabel.alpha = [MDCTypography captionFontOpacity]; + [disabledSwitchButtonLabel sizeToFit]; + disabledSwitchButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:disabledSwitchButtonLabel]; + + NSDictionary *views = @{ + @"slider" : self.switchComponent, + @"label" : switchLabel, + @"colorSlider" : self.colorSwitchComponent, + @"colorLabel" : colorSwitchButtonLabel, + @"disabledSlider" : self.disabledSwitchComponent, + @"disabledLabel" : disabledSwitchButtonLabel + }; + + NSDictionary *metrics = @{ @"smallVMargin" : @24.0, + @"smallHMargin" : @24.0, + @"buttonHeight" : @(self.switchComponent.bounds.size.height) }; + + // Vertical column of switches + NSString *sliderLayoutConstraints = + @"V:[slider]-smallVMargin-[colorSlider]-smallVMargin-[disabledSlider]"; + + // Vertical column of labels + NSString *labelLayoutConstraints = + @"V:[label(buttonHeight)]-smallVMargin-[colorLabel(buttonHeight)]-smallVMargin-" + "[disabledLabel(buttonHeight)]"; + + // Horizontal alignment between the two columns + NSString *columnConstraints = @"[label(100)]-smallHMargin-[slider]"; + + // Center view horizontally on the left edge of one of the switches + [self.view addConstraint: + [NSLayoutConstraint constraintWithItem:self.colorSwitchComponent + attribute:NSLayoutAttributeLeft + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeCenterX + multiplier:1.f + constant:40.f]]; + + // Center view vertically + [self.view addConstraint: + [NSLayoutConstraint constraintWithItem:self.colorSwitchComponent + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeCenterY + multiplier:1.f + constant:0]]; + + // Center switches in their column + [self.view addConstraints: + [NSLayoutConstraint constraintsWithVisualFormat:sliderLayoutConstraints + options:NSLayoutFormatAlignAllCenterX + metrics:metrics + views:views]]; + + // Left align labels in their column + [self.view addConstraints: + [NSLayoutConstraint constraintsWithVisualFormat:labelLayoutConstraints + options:NSLayoutFormatAlignAllLeft + metrics:metrics + views:views]]; + + // Vertically align first element in label column to first element in switch column + [self.view addConstraint: + [NSLayoutConstraint constraintWithItem:self.switchComponent + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:switchLabel + attribute:NSLayoutAttributeCenterY + multiplier:1.f + constant:0.f]]; + + // Position label column left of slider column, wide enough to accommodate label text + [self.view addConstraints: + [NSLayoutConstraint constraintsWithVisualFormat:columnConstraints + options:0 + metrics:metrics + views:views]]; +} + +@end diff --git a/components/Switch/src/MDCSwitch.m b/components/Switch/src/MDCSwitch.m index e4de2786592..5e3ee62a275 100644 --- a/components/Switch/src/MDCSwitch.m +++ b/components/Switch/src/MDCSwitch.m @@ -295,7 +295,7 @@ + (NSString *)a11yHintString { static const CGFloat kMDCSwitchLightThemeTrackDisabledAlpha = 0.12f; + (UIColor *)defaultOnTintColor { - return [UIColor greenColor]; + return [UIColor colorWithRed:0.32f green:0.87f blue:0 alpha:1]; } + (UIColor *)defaultOffThumbColor { From 76084a449b9a127f378d7b469fa8374f3bb1378f Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 13 Apr 2016 14:25:29 -0400 Subject: [PATCH 018/129] Removed extra release marker in CHANGELOG.md. Reviewers: ajsecord, #mdc_ios_owners Reviewed By: ajsecord, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D602 --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cac361e0872..95e946733a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,3 @@ -## release-candidate 4.1.0 - # 4.1.0 Updated arrow back icon names. No API changes. From 183a8cb3195386f5523748ac424e8f6b64486f3c Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 13 Apr 2016 16:02:03 -0400 Subject: [PATCH 019/129] [Pesto] Remove use of deprecated contentView API. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D604 --- demos/Pesto/Pesto/PestoCollectionViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/Pesto/Pesto/PestoCollectionViewController.m b/demos/Pesto/Pesto/PestoCollectionViewController.m index 04205331e59..625bdc5f863 100644 --- a/demos/Pesto/Pesto/PestoCollectionViewController.m +++ b/demos/Pesto/Pesto/PestoCollectionViewController.m @@ -56,7 +56,7 @@ - (void)setFlexHeaderContainerVC:(MDCFlexibleHeaderContainerViewController *)fle headerView.trackingScrollView = self.collectionView; headerView.maximumHeight = kPestoCollectionViewControllerDefaultHeaderHeight; headerView.minimumHeight = kPestoCollectionViewControllerSmallHeaderHeight; - [headerView.contentView addSubview:[self pestoHeaderView]]; + [headerView addSubview:[self pestoHeaderView]]; // Use a custom shadow under the flexible header. MDCShadowLayer *shadowLayer = [MDCShadowLayer layer]; From 552d66f3fb2f258446f1b41951457ee494e7bc06 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Thu, 14 Apr 2016 12:29:03 -0400 Subject: [PATCH 020/129] Revert replace + with _ in icon names Summary: Using autogenerated file Reviewers: randallli, #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D612 --- components/AppBar/src/MDCAppBar.m | 2 +- ...erialIcons_ic_arrow_back.h => MaterialIcons+ic_arrow_back.h} | 0 ...erialIcons_ic_arrow_back.m => MaterialIcons+ic_arrow_back.m} | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename components/private/Icons/icons/ic_arrow_back/src/{MaterialIcons_ic_arrow_back.h => MaterialIcons+ic_arrow_back.h} (100%) rename components/private/Icons/icons/ic_arrow_back/src/{MaterialIcons_ic_arrow_back.m => MaterialIcons+ic_arrow_back.m} (96%) diff --git a/components/AppBar/src/MDCAppBar.m b/components/AppBar/src/MDCAppBar.m index ae718af4310..8ece08dfa02 100644 --- a/components/AppBar/src/MDCAppBar.m +++ b/components/AppBar/src/MDCAppBar.m @@ -23,7 +23,7 @@ #import "MDCAppBarContainerViewController.h" #import "MaterialFlexibleHeader.h" -#import "MaterialIcons_ic_arrow_back.h" +#import "MaterialIcons+ic_arrow_back.h" #import "MaterialShadowElevations.h" #import "MaterialShadowLayer.h" #import "MaterialTypography.h" diff --git a/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons_ic_arrow_back.h b/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons+ic_arrow_back.h similarity index 100% rename from components/private/Icons/icons/ic_arrow_back/src/MaterialIcons_ic_arrow_back.h rename to components/private/Icons/icons/ic_arrow_back/src/MaterialIcons+ic_arrow_back.h diff --git a/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons_ic_arrow_back.m b/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons+ic_arrow_back.m similarity index 96% rename from components/private/Icons/icons/ic_arrow_back/src/MaterialIcons_ic_arrow_back.m rename to components/private/Icons/icons/ic_arrow_back/src/MaterialIcons+ic_arrow_back.m index d56943dd10c..9b7581ee40a 100644 --- a/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons_ic_arrow_back.m +++ b/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons+ic_arrow_back.m @@ -17,7 +17,7 @@ // This file was automatically generated by running scripts/sync_icons.sh // Do not modify directly. -#import "MaterialIcons_ic_arrow_back.h" +#import "MaterialIcons+ic_arrow_back.h" #import "MDCIcons+BundleLoader.h" From fffb75e91e8ab5b979dba7a7fec661d1a058bb11 Mon Sep 17 00:00:00 2001 From: Yiran Mao Date: Thu, 14 Apr 2016 15:30:57 -0400 Subject: [PATCH 021/129] Remove obsolete jazzy.yaml files. --- components/AppBar/.jazzy.yaml | 4 ---- components/ButtonBar/.jazzy.yaml | 4 ---- components/Buttons/.jazzy.yaml | 4 ---- components/FlexibleHeader/.jazzy.yaml | 4 ---- components/HeaderStackView/.jazzy.yaml | 4 ---- components/Ink/.jazzy.yaml | 4 ---- components/NavigationBar/.jazzy.yaml | 4 ---- components/PageControl/.jazzy.yaml | 4 ---- components/RobotoFontLoader/.jazzy.yaml | 4 ---- components/ScrollViewDelegateMultiplexer/.jazzy.yaml | 4 ---- components/ShadowElevations/.jazzy.yaml | 4 ---- components/ShadowLayer/.jazzy.yaml | 4 ---- components/Slider/.jazzy.yaml | 4 ---- components/SpritedAnimationView/.jazzy.yaml | 4 ---- components/Switch/.jazzy.yaml | 4 ---- components/Typography/.jazzy.yaml | 4 ---- 16 files changed, 64 deletions(-) delete mode 100644 components/AppBar/.jazzy.yaml delete mode 100644 components/ButtonBar/.jazzy.yaml delete mode 100644 components/Buttons/.jazzy.yaml delete mode 100644 components/FlexibleHeader/.jazzy.yaml delete mode 100644 components/HeaderStackView/.jazzy.yaml delete mode 100644 components/Ink/.jazzy.yaml delete mode 100644 components/NavigationBar/.jazzy.yaml delete mode 100644 components/PageControl/.jazzy.yaml delete mode 100644 components/RobotoFontLoader/.jazzy.yaml delete mode 100644 components/ScrollViewDelegateMultiplexer/.jazzy.yaml delete mode 100644 components/ShadowElevations/.jazzy.yaml delete mode 100644 components/ShadowLayer/.jazzy.yaml delete mode 100644 components/Slider/.jazzy.yaml delete mode 100644 components/SpritedAnimationView/.jazzy.yaml delete mode 100644 components/Switch/.jazzy.yaml delete mode 100644 components/Typography/.jazzy.yaml diff --git a/components/AppBar/.jazzy.yaml b/components/AppBar/.jazzy.yaml deleted file mode 100644 index d9a45a8d6c9..00000000000 --- a/components/AppBar/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: AppBar -umbrella_header: src/MaterialAppBar.h -objc: true -sdk: iphonesimulator diff --git a/components/ButtonBar/.jazzy.yaml b/components/ButtonBar/.jazzy.yaml deleted file mode 100644 index 968497ec668..00000000000 --- a/components/ButtonBar/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: ButtonBar -umbrella_header: src/MaterialButtonBar.h -objc: true -sdk: iphonesimulator diff --git a/components/Buttons/.jazzy.yaml b/components/Buttons/.jazzy.yaml deleted file mode 100644 index 713c075a245..00000000000 --- a/components/Buttons/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: Buttons -umbrella_header: src/MaterialButtons.h -objc: true -sdk: iphonesimulator diff --git a/components/FlexibleHeader/.jazzy.yaml b/components/FlexibleHeader/.jazzy.yaml deleted file mode 100644 index 750bd5e4036..00000000000 --- a/components/FlexibleHeader/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: FlexibleHeader -umbrella_header: src/MaterialFlexibleHeader.h -objc: true -sdk: iphonesimulator diff --git a/components/HeaderStackView/.jazzy.yaml b/components/HeaderStackView/.jazzy.yaml deleted file mode 100644 index 1111b18f3ea..00000000000 --- a/components/HeaderStackView/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: HeaderStackView -umbrella_header: src/MaterialHeaderStackView.h -objc: true -sdk: iphonesimulator diff --git a/components/Ink/.jazzy.yaml b/components/Ink/.jazzy.yaml deleted file mode 100644 index 5481ddfdf2c..00000000000 --- a/components/Ink/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: Ink -umbrella_header: src/MaterialInk.h -objc: true -sdk: iphonesimulator diff --git a/components/NavigationBar/.jazzy.yaml b/components/NavigationBar/.jazzy.yaml deleted file mode 100644 index 281a3602d63..00000000000 --- a/components/NavigationBar/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: NavigationBar -umbrella_header: src/MaterialNavigationBar.h -objc: true -sdk: iphonesimulator diff --git a/components/PageControl/.jazzy.yaml b/components/PageControl/.jazzy.yaml deleted file mode 100644 index fb0f2614321..00000000000 --- a/components/PageControl/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: PageControl -umbrella_header: src/MaterialPageControl.h -objc: true -sdk: iphonesimulator diff --git a/components/RobotoFontLoader/.jazzy.yaml b/components/RobotoFontLoader/.jazzy.yaml deleted file mode 100644 index 6fcb7d31d93..00000000000 --- a/components/RobotoFontLoader/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: RobotoFontLoader -umbrella_header: src/MaterialRobotoFontLoader.h -objc: true -sdk: iphonesimulator diff --git a/components/ScrollViewDelegateMultiplexer/.jazzy.yaml b/components/ScrollViewDelegateMultiplexer/.jazzy.yaml deleted file mode 100644 index f73e43fea00..00000000000 --- a/components/ScrollViewDelegateMultiplexer/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: ScrollViewDelegateMultiplexer -umbrella_header: src/MaterialScrollViewDelegateMultiplexer.h -objc: true -sdk: iphonesimulator diff --git a/components/ShadowElevations/.jazzy.yaml b/components/ShadowElevations/.jazzy.yaml deleted file mode 100644 index f691f1a1ca9..00000000000 --- a/components/ShadowElevations/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: ShadowElevations -umbrella_header: src/MaterialShadowElevations.h -objc: true -sdk: iphonesimulator diff --git a/components/ShadowLayer/.jazzy.yaml b/components/ShadowLayer/.jazzy.yaml deleted file mode 100644 index 72154fae6a1..00000000000 --- a/components/ShadowLayer/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: ShadowLayer -umbrella_header: src/MaterialShadowLayer.h -objc: true -sdk: iphonesimulator diff --git a/components/Slider/.jazzy.yaml b/components/Slider/.jazzy.yaml deleted file mode 100644 index c3e13d04646..00000000000 --- a/components/Slider/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: Slider -umbrella_header: src/MaterialSlider.h -objc: true -sdk: iphonesimulator diff --git a/components/SpritedAnimationView/.jazzy.yaml b/components/SpritedAnimationView/.jazzy.yaml deleted file mode 100644 index f61882e660e..00000000000 --- a/components/SpritedAnimationView/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: SpritedAnimationView -umbrella_header: src/MaterialSpritedAnimationView.h -objc: true -sdk: iphonesimulator diff --git a/components/Switch/.jazzy.yaml b/components/Switch/.jazzy.yaml deleted file mode 100644 index b5627b69b82..00000000000 --- a/components/Switch/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: Switch -umbrella_header: src/MaterialSwitch.h -objc: true -sdk: iphonesimulator diff --git a/components/Typography/.jazzy.yaml b/components/Typography/.jazzy.yaml deleted file mode 100644 index 653647b1ef0..00000000000 --- a/components/Typography/.jazzy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -module_name: Typography -umbrella_header: src/MaterialTypography.h -objc: true -sdk: iphonesimulator From c2f7f50cf835d9ab1df91a085c8e640347f12f63 Mon Sep 17 00:00:00 2001 From: Ian Gordon Date: Thu, 14 Apr 2016 16:01:29 -0400 Subject: [PATCH 022/129] [Pesto] Remove outdated offset Reviewers: larche, junius, #mdc_ios_owners Reviewed By: larche, junius, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D615 --- demos/Pesto/Pesto/PestoSettingsViewController.m | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/demos/Pesto/Pesto/PestoSettingsViewController.m b/demos/Pesto/Pesto/PestoSettingsViewController.m index b6044576100..7dae7ba72e8 100644 --- a/demos/Pesto/Pesto/PestoSettingsViewController.m +++ b/demos/Pesto/Pesto/PestoSettingsViewController.m @@ -20,8 +20,6 @@ #import "MaterialSwitch.h" #import "MaterialTypography.h" -static CGFloat kPestoSettingsTableViewOffsetTop = 0.f; - static NSString *const kPestoSettingsTableViewCellReuseIdentifier = @"PestoSettingsTableViewCell"; static NSString *const kPestoSettingsTableViewHeaderViewReuseIdentifier = @"PestoSettingsTableViewHeaderView"; @@ -150,9 +148,9 @@ - (void)viewDidLoad { CGRect settingsTableViewFrame = CGRectMake(0, - kPestoSettingsTableViewOffsetTop, + 0, self.view.bounds.size.width, - self.view.bounds.size.height - kPestoSettingsTableViewOffsetTop); + self.view.bounds.size.height); self.settingsTableView = [[UITableView alloc] initWithFrame:settingsTableViewFrame style:UITableViewStylePlain]; self.settingsTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth; From 89d096c45c3d8fd077d52d7b5a2482c7cabc82d8 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 14 Apr 2016 20:17:09 -0400 Subject: [PATCH 023/129] Iterations to the releasing.md document and process. Summary: - Add TODO in release/cut's CHANGELOG modification to ensure that the version number is provided. - Add build_all_pod_projects. Added to releasing.md: - Section on "Send the release out for review" - Building and running the pod projects. - Broke out the version bumping scripts to minimize copy-pasting errors. - Add list of clients that must confirm a release before it lands. Closes https://github.com/google/material-components-ios/issues/353 Closes https://github.com/google/material-components-ios/issues/352 Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D617 --- contributing/contributor_guides/releasing.md | 41 ++++++++++++++++---- scripts/build_all_pod_projects | 33 ++++++++++++++++ scripts/release/cut | 4 +- scripts/release/release_checklist.txt | 12 ++++++ 4 files changed, 81 insertions(+), 9 deletions(-) create mode 100755 scripts/build_all_pod_projects create mode 100644 scripts/release/release_checklist.txt diff --git a/contributing/contributor_guides/releasing.md b/contributing/contributor_guides/releasing.md index a882a47e555..2f759a87faf 100644 --- a/contributing/contributor_guides/releasing.md +++ b/contributing/contributor_guides/releasing.md @@ -43,6 +43,12 @@ To test the branch locally you can run: Verify that the unit tests do not fail. +Build and run the catalog and demo applications: + + scripts/build_all_pod_projects + +Identify why any failures occurred and resolve them before continuing. + ### Push the release branch early and often Push `release-candidate` to GitHub as you make necessary changes. This allows other people and @@ -184,11 +190,14 @@ To see all changes that are part of this release, run: ### Classify the release type -Based on the information at your disposal you should now be able to identify the release number. +Based on the information at your disposal you should now be able to identify the release type. +Use the `next` script to generate the next version number: -Bump the release by running `bump` with the next version number: + scripts/release/next {major|minor|patch} - scripts/release/bump $(scripts/release/next {major|minor|patch}) +Bump the release by running `bump` with the release's version number: + + scripts/release/bump Also rename CHANGELOG.md's "release-candidate" section with the name of this release. @@ -196,13 +205,31 @@ Also rename CHANGELOG.md's "release-candidate" section with the name of this rel Commit the results to your branch. - git commit -am "Bumped version number to $(scripts/release/next {major|minor|patch})." + git commit -am "Bumped version number to $(pod ipc spec MaterialComponents.podspec | grep '"version"' | cut -d'"' -f4)." git push origin release-candidate -## Merge the release branch +## Send the release out for review + +Sent the release-candidate branch out for review: + + git fetch + git checkout release-candidate + arc diff origin/master --message-file scripts/release/release_checklist.txt + +Check off each item in the diff's checklist before merging the release candidate branch. + +## Do not land the release candidate + +Before you can merge the release branch into either develop or master you **must** get the release +go-ahead from the following clients: + +- Google: must verify that the release branch passes all internal tests. If you are a Googler, see + the internal "mirroring" document for further instructions. + +## Merge the release candidate branch -Once the release has been fully tested and clients have had a reasonable opportunity to test it, you -may merge the release into the `develop` and `master` branches. +Once the release has passed all tests by clients, you may merge the release into the `develop` and +`master` branches. scripts/release/merge diff --git a/scripts/build_all_pod_projects b/scripts/build_all_pod_projects new file mode 100755 index 00000000000..6a5b7470f33 --- /dev/null +++ b/scripts/build_all_pod_projects @@ -0,0 +1,33 @@ +#!/bin/bash +# +# 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. + +find . -name 'Podfile' | while read path; do + project_dir=$(dirname $path) + + echo "Entering $project_dir..." + + pushd $project_dir >> /dev/null + + pod install || { echo "Failed to run pod install."; exit 1; } + + workspace=$(find . -d 1 -name '*.xcworkspace') + scheme=$(xcodebuild build -workspace $workspace -list | grep "Information about workspace" | cut -d'"' -f2) + xcodebuild clean -workspace $workspace -scheme $scheme + xcodebuild build -workspace $workspace -scheme $scheme \ + || { echo "Failed to build $workspace."; exit 1; } + + popd >> /dev/null +done diff --git a/scripts/release/cut b/scripts/release/cut index 8e58bd6b1a6..70d7bcf1a79 100755 --- a/scripts/release/cut +++ b/scripts/release/cut @@ -26,8 +26,8 @@ else fi # Prepare the CHANGELOG for updates. -if ! grep "## release-candidate" CHANGELOG.md >> /dev/null; then - echo -e "## release-candidate\n" | cat - CHANGELOG.md > /tmp/out && mv /tmp/out CHANGELOG.md +if ! grep "# release-candidate" CHANGELOG.md >> /dev/null; then + echo -e "# release-candidate TODO: Replace me with version number. \n" | cat - CHANGELOG.md > /tmp/out && mv /tmp/out CHANGELOG.md fi RELEASE_SHA=$(git merge-base --fork-point release-candidate origin/develop) diff --git a/scripts/release/release_checklist.txt b/scripts/release/release_checklist.txt new file mode 100644 index 00000000000..77175dffa71 --- /dev/null +++ b/scripts/release/release_checklist.txt @@ -0,0 +1,12 @@ +Release candidate. + +Checklist: + +- [ ] Ran `arc unit --everything`. +- [ ] Ran `scripts/build_all_pod_projects`. +- [ ] Ran `scripts/release/api_diff` and pasted the results into CHANGELOG.md. +- [ ] Ran `scripts/release/changes` and pasted the results into CHANGELOG.md. +- [ ] Visually inspected the API diff to ensure it accurately reflects the release's changes. +- [ ] Ran `scripts/release/diff components/*/src/` and visually inspected the changes. +- [ ] Ran `scripts/release/bump` with the new version number. +- [ ] Updated CHANGELOG.md's latest section header to match the release's version number. From ad228a154455835c3e871109f12670387b9d926d Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 14 Apr 2016 20:29:05 -0400 Subject: [PATCH 024/129] Revert "Remove obsolete jazzy.yaml files." This reverts commit fffb75e91e8ab5b979dba7a7fec661d1a058bb11. --- components/AppBar/.jazzy.yaml | 4 ++++ components/ButtonBar/.jazzy.yaml | 4 ++++ components/Buttons/.jazzy.yaml | 4 ++++ components/FlexibleHeader/.jazzy.yaml | 4 ++++ components/FontDiskLoader/.jazzy.yaml | 4 ++++ components/HeaderStackView/.jazzy.yaml | 4 ++++ components/Ink/.jazzy.yaml | 4 ++++ components/NavigationBar/.jazzy.yaml | 4 ++++ components/PageControl/.jazzy.yaml | 4 ++++ components/RobotoFontLoader/.jazzy.yaml | 4 ++++ components/ScrollViewDelegateMultiplexer/.jazzy.yaml | 4 ++++ components/ShadowElevations/.jazzy.yaml | 4 ++++ components/ShadowLayer/.jazzy.yaml | 4 ++++ components/Slider/.jazzy.yaml | 4 ++++ components/SpritedAnimationView/.jazzy.yaml | 4 ++++ components/Switch/.jazzy.yaml | 4 ++++ components/Typography/.jazzy.yaml | 4 ++++ 17 files changed, 68 insertions(+) create mode 100644 components/AppBar/.jazzy.yaml create mode 100644 components/ButtonBar/.jazzy.yaml create mode 100644 components/Buttons/.jazzy.yaml create mode 100644 components/FlexibleHeader/.jazzy.yaml create mode 100644 components/FontDiskLoader/.jazzy.yaml create mode 100644 components/HeaderStackView/.jazzy.yaml create mode 100644 components/Ink/.jazzy.yaml create mode 100644 components/NavigationBar/.jazzy.yaml create mode 100644 components/PageControl/.jazzy.yaml create mode 100644 components/RobotoFontLoader/.jazzy.yaml create mode 100644 components/ScrollViewDelegateMultiplexer/.jazzy.yaml create mode 100644 components/ShadowElevations/.jazzy.yaml create mode 100644 components/ShadowLayer/.jazzy.yaml create mode 100644 components/Slider/.jazzy.yaml create mode 100644 components/SpritedAnimationView/.jazzy.yaml create mode 100644 components/Switch/.jazzy.yaml create mode 100644 components/Typography/.jazzy.yaml diff --git a/components/AppBar/.jazzy.yaml b/components/AppBar/.jazzy.yaml new file mode 100644 index 00000000000..d9a45a8d6c9 --- /dev/null +++ b/components/AppBar/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: AppBar +umbrella_header: src/MaterialAppBar.h +objc: true +sdk: iphonesimulator diff --git a/components/ButtonBar/.jazzy.yaml b/components/ButtonBar/.jazzy.yaml new file mode 100644 index 00000000000..968497ec668 --- /dev/null +++ b/components/ButtonBar/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: ButtonBar +umbrella_header: src/MaterialButtonBar.h +objc: true +sdk: iphonesimulator diff --git a/components/Buttons/.jazzy.yaml b/components/Buttons/.jazzy.yaml new file mode 100644 index 00000000000..713c075a245 --- /dev/null +++ b/components/Buttons/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: Buttons +umbrella_header: src/MaterialButtons.h +objc: true +sdk: iphonesimulator diff --git a/components/FlexibleHeader/.jazzy.yaml b/components/FlexibleHeader/.jazzy.yaml new file mode 100644 index 00000000000..750bd5e4036 --- /dev/null +++ b/components/FlexibleHeader/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: FlexibleHeader +umbrella_header: src/MaterialFlexibleHeader.h +objc: true +sdk: iphonesimulator diff --git a/components/FontDiskLoader/.jazzy.yaml b/components/FontDiskLoader/.jazzy.yaml new file mode 100644 index 00000000000..db996deb192 --- /dev/null +++ b/components/FontDiskLoader/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: FontDiskLoader +umbrella_header: src/MaterialFontDiskLoader.h +objc: true +sdk: iphonesimulator diff --git a/components/HeaderStackView/.jazzy.yaml b/components/HeaderStackView/.jazzy.yaml new file mode 100644 index 00000000000..1111b18f3ea --- /dev/null +++ b/components/HeaderStackView/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: HeaderStackView +umbrella_header: src/MaterialHeaderStackView.h +objc: true +sdk: iphonesimulator diff --git a/components/Ink/.jazzy.yaml b/components/Ink/.jazzy.yaml new file mode 100644 index 00000000000..5481ddfdf2c --- /dev/null +++ b/components/Ink/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: Ink +umbrella_header: src/MaterialInk.h +objc: true +sdk: iphonesimulator diff --git a/components/NavigationBar/.jazzy.yaml b/components/NavigationBar/.jazzy.yaml new file mode 100644 index 00000000000..281a3602d63 --- /dev/null +++ b/components/NavigationBar/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: NavigationBar +umbrella_header: src/MaterialNavigationBar.h +objc: true +sdk: iphonesimulator diff --git a/components/PageControl/.jazzy.yaml b/components/PageControl/.jazzy.yaml new file mode 100644 index 00000000000..fb0f2614321 --- /dev/null +++ b/components/PageControl/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: PageControl +umbrella_header: src/MaterialPageControl.h +objc: true +sdk: iphonesimulator diff --git a/components/RobotoFontLoader/.jazzy.yaml b/components/RobotoFontLoader/.jazzy.yaml new file mode 100644 index 00000000000..6fcb7d31d93 --- /dev/null +++ b/components/RobotoFontLoader/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: RobotoFontLoader +umbrella_header: src/MaterialRobotoFontLoader.h +objc: true +sdk: iphonesimulator diff --git a/components/ScrollViewDelegateMultiplexer/.jazzy.yaml b/components/ScrollViewDelegateMultiplexer/.jazzy.yaml new file mode 100644 index 00000000000..f73e43fea00 --- /dev/null +++ b/components/ScrollViewDelegateMultiplexer/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: ScrollViewDelegateMultiplexer +umbrella_header: src/MaterialScrollViewDelegateMultiplexer.h +objc: true +sdk: iphonesimulator diff --git a/components/ShadowElevations/.jazzy.yaml b/components/ShadowElevations/.jazzy.yaml new file mode 100644 index 00000000000..f691f1a1ca9 --- /dev/null +++ b/components/ShadowElevations/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: ShadowElevations +umbrella_header: src/MaterialShadowElevations.h +objc: true +sdk: iphonesimulator diff --git a/components/ShadowLayer/.jazzy.yaml b/components/ShadowLayer/.jazzy.yaml new file mode 100644 index 00000000000..72154fae6a1 --- /dev/null +++ b/components/ShadowLayer/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: ShadowLayer +umbrella_header: src/MaterialShadowLayer.h +objc: true +sdk: iphonesimulator diff --git a/components/Slider/.jazzy.yaml b/components/Slider/.jazzy.yaml new file mode 100644 index 00000000000..c3e13d04646 --- /dev/null +++ b/components/Slider/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: Slider +umbrella_header: src/MaterialSlider.h +objc: true +sdk: iphonesimulator diff --git a/components/SpritedAnimationView/.jazzy.yaml b/components/SpritedAnimationView/.jazzy.yaml new file mode 100644 index 00000000000..f61882e660e --- /dev/null +++ b/components/SpritedAnimationView/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: SpritedAnimationView +umbrella_header: src/MaterialSpritedAnimationView.h +objc: true +sdk: iphonesimulator diff --git a/components/Switch/.jazzy.yaml b/components/Switch/.jazzy.yaml new file mode 100644 index 00000000000..b5627b69b82 --- /dev/null +++ b/components/Switch/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: Switch +umbrella_header: src/MaterialSwitch.h +objc: true +sdk: iphonesimulator diff --git a/components/Typography/.jazzy.yaml b/components/Typography/.jazzy.yaml new file mode 100644 index 00000000000..653647b1ef0 --- /dev/null +++ b/components/Typography/.jazzy.yaml @@ -0,0 +1,4 @@ +module_name: Typography +umbrella_header: src/MaterialTypography.h +objc: true +sdk: iphonesimulator From 7bb181a150bc1cf707637fdf112d3a949638c809 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Fri, 15 Apr 2016 09:30:43 -0400 Subject: [PATCH 025/129] [Catalog] Make catalogIsPrimaryDemo static method in demos Reviewers: ajsecord, #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D611 --- .../examples/ShadowDragSquareExampleViewController.swift | 2 +- .../Typography/examples/TypographyExamplesViewController.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/ShadowLayer/examples/ShadowDragSquareExampleViewController.swift b/components/ShadowLayer/examples/ShadowDragSquareExampleViewController.swift index 9d5d59c4a0e..bb1a3f340d5 100644 --- a/components/ShadowLayer/examples/ShadowDragSquareExampleViewController.swift +++ b/components/ShadowLayer/examples/ShadowDragSquareExampleViewController.swift @@ -83,7 +83,7 @@ class ShadowDragSquareExampleViewController: UIViewController { return "Shadow Layer implements the Material Design specifications for elevation and shadows." } - func catalogIsPrimaryDemo() -> Bool { + class func catalogIsPrimaryDemo() -> Bool { return true } diff --git a/components/Typography/examples/TypographyExamplesViewController.swift b/components/Typography/examples/TypographyExamplesViewController.swift index 32c95ae518f..1dca4fdb22a 100644 --- a/components/Typography/examples/TypographyExamplesViewController.swift +++ b/components/Typography/examples/TypographyExamplesViewController.swift @@ -211,7 +211,7 @@ class TypographyExamplesViewController: UICollectionViewController { + " opacities from the Material Design specifications." } - func catalogIsPrimaryDemo() -> Bool { + class func catalogIsPrimaryDemo() -> Bool { return true } From 977626313e702783835f7ad1aba220b99316ba3f Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Fri, 15 Apr 2016 11:30:41 -0400 Subject: [PATCH 026/129] Update .jazzy.yaml module property. Summary: This property name changed in jazzy 0.6.0 from `module_name` to `module`. Reviewers: ajsecord, #mdc_ios_owners Reviewed By: ajsecord, #mdc_ios_owners Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D619 --- components/AppBar/.jazzy.yaml | 3 ++- components/ButtonBar/.jazzy.yaml | 3 ++- components/Buttons/.jazzy.yaml | 3 ++- components/FlexibleHeader/.jazzy.yaml | 3 ++- components/FontDiskLoader/.jazzy.yaml | 3 ++- components/HeaderStackView/.jazzy.yaml | 3 ++- components/Ink/.jazzy.yaml | 3 ++- components/NavigationBar/.jazzy.yaml | 3 ++- components/PageControl/.jazzy.yaml | 3 ++- components/RobotoFontLoader/.jazzy.yaml | 3 ++- components/ScrollViewDelegateMultiplexer/.jazzy.yaml | 3 ++- components/ShadowElevations/.jazzy.yaml | 3 ++- components/ShadowLayer/.jazzy.yaml | 3 ++- components/Slider/.jazzy.yaml | 3 ++- components/SpritedAnimationView/.jazzy.yaml | 3 ++- components/Switch/.jazzy.yaml | 3 ++- components/Typography/.jazzy.yaml | 3 ++- scripts/generate_jazzy_yamls.sh | 3 ++- 18 files changed, 36 insertions(+), 18 deletions(-) diff --git a/components/AppBar/.jazzy.yaml b/components/AppBar/.jazzy.yaml index d9a45a8d6c9..73e0cd1ece1 100644 --- a/components/AppBar/.jazzy.yaml +++ b/components/AppBar/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: AppBar +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: AppBar umbrella_header: src/MaterialAppBar.h objc: true sdk: iphonesimulator diff --git a/components/ButtonBar/.jazzy.yaml b/components/ButtonBar/.jazzy.yaml index 968497ec668..04760b74d4b 100644 --- a/components/ButtonBar/.jazzy.yaml +++ b/components/ButtonBar/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: ButtonBar +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: ButtonBar umbrella_header: src/MaterialButtonBar.h objc: true sdk: iphonesimulator diff --git a/components/Buttons/.jazzy.yaml b/components/Buttons/.jazzy.yaml index 713c075a245..4ae5b0524f6 100644 --- a/components/Buttons/.jazzy.yaml +++ b/components/Buttons/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: Buttons +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: Buttons umbrella_header: src/MaterialButtons.h objc: true sdk: iphonesimulator diff --git a/components/FlexibleHeader/.jazzy.yaml b/components/FlexibleHeader/.jazzy.yaml index 750bd5e4036..be07c16d24c 100644 --- a/components/FlexibleHeader/.jazzy.yaml +++ b/components/FlexibleHeader/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: FlexibleHeader +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: FlexibleHeader umbrella_header: src/MaterialFlexibleHeader.h objc: true sdk: iphonesimulator diff --git a/components/FontDiskLoader/.jazzy.yaml b/components/FontDiskLoader/.jazzy.yaml index db996deb192..6fa1baca600 100644 --- a/components/FontDiskLoader/.jazzy.yaml +++ b/components/FontDiskLoader/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: FontDiskLoader +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: FontDiskLoader umbrella_header: src/MaterialFontDiskLoader.h objc: true sdk: iphonesimulator diff --git a/components/HeaderStackView/.jazzy.yaml b/components/HeaderStackView/.jazzy.yaml index 1111b18f3ea..ed368b2b9e5 100644 --- a/components/HeaderStackView/.jazzy.yaml +++ b/components/HeaderStackView/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: HeaderStackView +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: HeaderStackView umbrella_header: src/MaterialHeaderStackView.h objc: true sdk: iphonesimulator diff --git a/components/Ink/.jazzy.yaml b/components/Ink/.jazzy.yaml index 5481ddfdf2c..ac8dbd76f17 100644 --- a/components/Ink/.jazzy.yaml +++ b/components/Ink/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: Ink +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: Ink umbrella_header: src/MaterialInk.h objc: true sdk: iphonesimulator diff --git a/components/NavigationBar/.jazzy.yaml b/components/NavigationBar/.jazzy.yaml index 281a3602d63..6b4740527d6 100644 --- a/components/NavigationBar/.jazzy.yaml +++ b/components/NavigationBar/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: NavigationBar +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: NavigationBar umbrella_header: src/MaterialNavigationBar.h objc: true sdk: iphonesimulator diff --git a/components/PageControl/.jazzy.yaml b/components/PageControl/.jazzy.yaml index fb0f2614321..4ac3c38b82c 100644 --- a/components/PageControl/.jazzy.yaml +++ b/components/PageControl/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: PageControl +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: PageControl umbrella_header: src/MaterialPageControl.h objc: true sdk: iphonesimulator diff --git a/components/RobotoFontLoader/.jazzy.yaml b/components/RobotoFontLoader/.jazzy.yaml index 6fcb7d31d93..647121122ef 100644 --- a/components/RobotoFontLoader/.jazzy.yaml +++ b/components/RobotoFontLoader/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: RobotoFontLoader +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: RobotoFontLoader umbrella_header: src/MaterialRobotoFontLoader.h objc: true sdk: iphonesimulator diff --git a/components/ScrollViewDelegateMultiplexer/.jazzy.yaml b/components/ScrollViewDelegateMultiplexer/.jazzy.yaml index f73e43fea00..758194ab8a2 100644 --- a/components/ScrollViewDelegateMultiplexer/.jazzy.yaml +++ b/components/ScrollViewDelegateMultiplexer/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: ScrollViewDelegateMultiplexer +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: ScrollViewDelegateMultiplexer umbrella_header: src/MaterialScrollViewDelegateMultiplexer.h objc: true sdk: iphonesimulator diff --git a/components/ShadowElevations/.jazzy.yaml b/components/ShadowElevations/.jazzy.yaml index f691f1a1ca9..ccc0f2925e0 100644 --- a/components/ShadowElevations/.jazzy.yaml +++ b/components/ShadowElevations/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: ShadowElevations +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: ShadowElevations umbrella_header: src/MaterialShadowElevations.h objc: true sdk: iphonesimulator diff --git a/components/ShadowLayer/.jazzy.yaml b/components/ShadowLayer/.jazzy.yaml index 72154fae6a1..d96d0b65b13 100644 --- a/components/ShadowLayer/.jazzy.yaml +++ b/components/ShadowLayer/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: ShadowLayer +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: ShadowLayer umbrella_header: src/MaterialShadowLayer.h objc: true sdk: iphonesimulator diff --git a/components/Slider/.jazzy.yaml b/components/Slider/.jazzy.yaml index c3e13d04646..4d9c582b966 100644 --- a/components/Slider/.jazzy.yaml +++ b/components/Slider/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: Slider +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: Slider umbrella_header: src/MaterialSlider.h objc: true sdk: iphonesimulator diff --git a/components/SpritedAnimationView/.jazzy.yaml b/components/SpritedAnimationView/.jazzy.yaml index f61882e660e..88788009c3a 100644 --- a/components/SpritedAnimationView/.jazzy.yaml +++ b/components/SpritedAnimationView/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: SpritedAnimationView +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: SpritedAnimationView umbrella_header: src/MaterialSpritedAnimationView.h objc: true sdk: iphonesimulator diff --git a/components/Switch/.jazzy.yaml b/components/Switch/.jazzy.yaml index b5627b69b82..fd438042c65 100644 --- a/components/Switch/.jazzy.yaml +++ b/components/Switch/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: Switch +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: Switch umbrella_header: src/MaterialSwitch.h objc: true sdk: iphonesimulator diff --git a/components/Typography/.jazzy.yaml b/components/Typography/.jazzy.yaml index 653647b1ef0..1e13afea8cd 100644 --- a/components/Typography/.jazzy.yaml +++ b/components/Typography/.jazzy.yaml @@ -1,4 +1,5 @@ -module_name: Typography +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: Typography umbrella_header: src/MaterialTypography.h objc: true sdk: iphonesimulator diff --git a/scripts/generate_jazzy_yamls.sh b/scripts/generate_jazzy_yamls.sh index 8ff97e8741b..017ea985b63 100755 --- a/scripts/generate_jazzy_yamls.sh +++ b/scripts/generate_jazzy_yamls.sh @@ -21,7 +21,8 @@ for d in components/*/README.md; do component=$(basename $folder) cat > "$folder/.jazzy.yaml" < Date: Fri, 15 Apr 2016 11:14:15 -0400 Subject: [PATCH 027/129] [FlexibleHeader] When changing min/max height, update the opposite property to match the new bounds. Summary: Closes https://github.com/google/material-components-ios/issues/324. Test Plan: Verified that the new unit tests pass. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Subscribers: randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D620 --- .../src/MDCFlexibleHeaderView.h | 17 +++++- .../src/MDCFlexibleHeaderView.m | 12 +++- .../unit/FlexibleHeaderView324Tests.swift | 59 +++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 components/FlexibleHeader/tests/unit/FlexibleHeaderView324Tests.swift diff --git a/components/FlexibleHeader/src/MDCFlexibleHeaderView.h b/components/FlexibleHeader/src/MDCFlexibleHeaderView.h index fd38221692b..ef06d051c87 100644 --- a/components/FlexibleHeader/src/MDCFlexibleHeaderView.h +++ b/components/FlexibleHeader/src/MDCFlexibleHeaderView.h @@ -289,8 +289,21 @@ typedef NS_ENUM(NSInteger, MDCFlexibleHeaderScrollPhase) { #pragma mark Bounding Dimensions -@property(nonatomic) CGFloat minimumHeight; ///< The minimum height that this header can shrink to. -@property(nonatomic) CGFloat maximumHeight; ///< The maximum height that this header can expand to. +/** + The minimum height that this header can shrink to. + + If you change the value of this property and the maximumHeight of the receiver is below the new + minimumHeight, maximumHeight will be adjusted to match the new minimum value. + */ +@property(nonatomic) CGFloat minimumHeight; + +/** + The maximum height that this header can expand to. + + If you change the value of this property and the minimumHeight of the receiver is above the new + maximumHeight, minimumHeight will be adjusted to match the new maximumHeight. + */ +@property(nonatomic) CGFloat maximumHeight; #pragma mark Behaviors diff --git a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m index 61f68b58e22..d1f8bbddd32 100644 --- a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m +++ b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m @@ -966,7 +966,11 @@ - (void)setMinimumHeight:(CGFloat)minimumHeight { _minimumHeight = minimumHeight; - [self fhv_updateLayout]; + if (_minimumHeight > _maximumHeight) { + [self setMaximumHeight:_minimumHeight]; + } else { + [self fhv_updateLayout]; + } } - (void)setMaximumHeight:(CGFloat)maximumHeight { @@ -991,7 +995,11 @@ - (void)setMaximumHeight:(CGFloat)maximumHeight { _trackingScrollView.contentOffset = originalOffset; } - [self fhv_updateLayout]; + if (_maximumHeight < _minimumHeight) { + [self setMinimumHeight:_maximumHeight]; + } else { + [self fhv_updateLayout]; + } } - (void)setInFrontOfInfiniteContent:(BOOL)inFrontOfInfiniteContent { diff --git a/components/FlexibleHeader/tests/unit/FlexibleHeaderView324Tests.swift b/components/FlexibleHeader/tests/unit/FlexibleHeaderView324Tests.swift new file mode 100644 index 00000000000..e61ac8482b1 --- /dev/null +++ b/components/FlexibleHeader/tests/unit/FlexibleHeaderView324Tests.swift @@ -0,0 +1,59 @@ +/* +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 XCTest +import MaterialComponents + +// Tests confirming that setting the flexible header view's min or max height will bound the +// corresponding value. +// +// Based on issue https://github.com/google/material-components-ios/issues/324 +class FlexibleHeaderView324Tests: XCTestCase { + + var headerView: MDCFlexibleHeaderView! + + override func setUp() { + headerView = MDCFlexibleHeaderView() + } + + // Side effect cases + + func testSettingMinAboveMax() { + headerView.minimumHeight = headerView.maximumHeight + 10 + + XCTAssertEqual(headerView.maximumHeight, headerView.minimumHeight) + } + + func testSettingMaxBelowMin() { + headerView.maximumHeight = headerView.minimumHeight - 10 + + XCTAssertEqual(headerView.maximumHeight, headerView.minimumHeight) + } + + // No side effect cases + + func testSettingMinBelowMax() { + headerView.minimumHeight = headerView.maximumHeight - 10 + + XCTAssertGreaterThan(headerView.maximumHeight, headerView.minimumHeight) + } + + func testSettingMaxAboveMin() { + headerView.maximumHeight = headerView.minimumHeight + 10 + + XCTAssertGreaterThan(headerView.maximumHeight, headerView.minimumHeight) + } +} From 6b38608a33de7227143faa80933df7c2152c509f Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Fri, 15 Apr 2016 14:56:35 -0400 Subject: [PATCH 028/129] [FlexibleHeader] Consolidate frame projection logic. Summary: Use this logic in fhv_isDetachedFromTopOfContent in order to correctly calculate detachment when the header view is a descendant of the tracking scroll view. Closes https://github.com/google/material-components-ios/issues/361. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D622 --- .../src/MDCFlexibleHeaderView.m | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m index d1f8bbddd32..009e8a92fe1 100644 --- a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m +++ b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m @@ -368,6 +368,17 @@ - (CGFloat)fhv_contentBottomEdge { return _trackingScrollView.contentSize.height; } +// Returns a value indicating how much the header is overlapping the tracking scroll view's content. +// > 0 overlapping the content +// = 0 attached to top of content +// < 0 the content is below the header +- (CGFloat)fhv_projectedHeaderBottomEdge { + CGFloat offsetWithoutInset = [self fhv_contentOffsetWithoutInjectedTopInset]; + CGRect projectedFrame = [self convertRect:self.bounds toView:self.trackingScrollView.superview]; + CGFloat frameBottomEdge = CGRectGetMaxY(projectedFrame); + return frameBottomEdge + offsetWithoutInset; +} + - (CGFloat)fhv_accumulatorMax { return (self.hidesStatusBarWhenCollapsed ? _minimumHeight : _minimumHeight - kExpectedStatusBarHeight); @@ -388,7 +399,8 @@ - (BOOL)fhv_isPartiallyShifted { // The flexible header is "in front of" the content. - (BOOL)fhv_isDetachedFromTopOfContent { - return [self fhv_contentOffsetWithoutInjectedTopInset] >= 0; + // Epsilon here is somewhat large in order to be visually-forgiving for sub-point situations. + return [self fhv_projectedHeaderBottomEdge] > (CGFloat)0.5; } - (BOOL)fhv_isOverExtendingBottom { @@ -513,16 +525,9 @@ - (void)fhv_accumulatorDidChange { return; } - CGFloat frameBottomEdge; - CGRect frame = self.frame; - // Calculate the frame's bottom edge in visual relation to the tracking scroll view. - CGRect projectedFrame = [self convertRect:self.bounds toView:self.trackingScrollView.superview]; - frameBottomEdge = (float)CGRectGetMaxY(projectedFrame); - - CGFloat offsetWithoutInset = [self fhv_contentOffsetWithoutInjectedTopInset]; - frameBottomEdge = (float)(frameBottomEdge + offsetWithoutInset); + CGFloat frameBottomEdge = [self fhv_projectedHeaderBottomEdge]; frameBottomEdge = MAX(0, MIN(kShadowScaleLength, frameBottomEdge)); CGFloat boundedAccumulator = MIN([self fhv_accumulatorMax], _shiftOffscreenAccumulator); From 8100c088133fd5799eb9f692ab76f463acaab715 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Fri, 15 Apr 2016 15:09:21 -0400 Subject: [PATCH 029/129] [AppBar] Remove the internal MDCAppBarContainerViewController contentViewController setter. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D623 --- .../src/MDCAppBarContainerViewController.m | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/components/AppBar/src/MDCAppBarContainerViewController.m b/components/AppBar/src/MDCAppBarContainerViewController.m index ef6f9d60332..6af9de3bba0 100644 --- a/components/AppBar/src/MDCAppBarContainerViewController.m +++ b/components/AppBar/src/MDCAppBarContainerViewController.m @@ -35,7 +35,8 @@ - (instancetype)initWithContentViewController:(UIViewController *)contentViewCon [self addChildViewController:_appBar.headerViewController]; - self.contentViewController = contentViewController; + _contentViewController = contentViewController; + [self addChildViewController:contentViewController]; } return self; } @@ -80,30 +81,4 @@ - (MDCFlexibleHeaderViewController *)headerViewController { return self.appBar.headerViewController; } -- (void)setContentViewController:(UIViewController *)contentViewController { - if (_contentViewController == contentViewController) { - return; - } - - // Teardown of the old controller - - [_contentViewController willMoveToParentViewController:nil]; - if ([_contentViewController isViewLoaded]) { - [_contentViewController.view removeFromSuperview]; - } - [_contentViewController removeFromParentViewController]; - - // Setup of the new controller - - _contentViewController = contentViewController; - - [self addChildViewController:contentViewController]; - - NSAssert(![self isViewLoaded], - @"View should not have been loaded at this point." - @" Verify that the view is not being accessed anywhere in %@ or %@.", - NSStringFromSelector(_cmd), - NSStringFromSelector(@selector(initWithContentViewController:))); -} - @end From cdc18fb8ccda075e1f8c56fd638c3a66df6f4a93 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Fri, 15 Apr 2016 16:05:19 -0400 Subject: [PATCH 030/129] [Catalog] Update Header Stack View demo visuals, move layout code into supplemental Summary: Screenshot http://codereview.cc/M23 Reviewers: ajsecord, featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D624 --- .../examples/HeaderStackViewTypicalUse.m | 43 ++++ .../HeaderStackViewTypicalUseViewController.m | 91 -------- .../examples/resources/mdc_theme.png | Bin 0 -> 11404 bytes .../HeaderStackViewTypicalUseSupplemental.h | 42 ++++ .../HeaderStackViewTypicalUseSupplemental.m | 203 ++++++++++++++++++ 5 files changed, 288 insertions(+), 91 deletions(-) create mode 100644 components/HeaderStackView/examples/HeaderStackViewTypicalUse.m delete mode 100644 components/HeaderStackView/examples/HeaderStackViewTypicalUseViewController.m create mode 100644 components/HeaderStackView/examples/resources/mdc_theme.png create mode 100644 components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.h create mode 100644 components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m diff --git a/components/HeaderStackView/examples/HeaderStackViewTypicalUse.m b/components/HeaderStackView/examples/HeaderStackViewTypicalUse.m new file mode 100644 index 00000000000..dd65a51d9fd --- /dev/null +++ b/components/HeaderStackView/examples/HeaderStackViewTypicalUse.m @@ -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 + +#import "HeaderStackViewTypicalUseSupplemental.h" +#import "MaterialHeaderStackView.h" + +@interface HeaderStackViewTypicalUse () + +@end + +@implementation HeaderStackViewTypicalUse + +- (void)viewDidLoad { + [super viewDidLoad]; + [self setupExampleViews]; + + self.stackView = [[MDCHeaderStackView alloc] init]; + self.stackView.topBar = self.topView; + self.stackView.bottomBar = self.navBar; + + CGRect frame = self.view.bounds; + self.stackView.frame = frame; + [self.stackView sizeToFit]; + + [self.view addSubview:self.stackView]; +} + +@end diff --git a/components/HeaderStackView/examples/HeaderStackViewTypicalUseViewController.m b/components/HeaderStackView/examples/HeaderStackViewTypicalUseViewController.m deleted file mode 100644 index 72b0f0b091c..00000000000 --- a/components/HeaderStackView/examples/HeaderStackViewTypicalUseViewController.m +++ /dev/null @@ -1,91 +0,0 @@ -/* - Copyright 2015-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 "MaterialHeaderStackView.h" - -@interface HeaderStackViewTypicalUseViewController : UIViewController -@end - -@implementation HeaderStackViewTypicalUseViewController { - BOOL _toggled; - MDCHeaderStackView *_stackView; -} - -// TODO: Support other categorizational methods. -+ (NSArray *)catalogBreadcrumbs { - return @[ @"Header Stack View", @"Header Stack View" ]; -} - -+ (NSString *)catalogDescription { - return @"The Header Stack View component is a view that coordinates the layout of two" - " vertically-stacked bar views."; -} - -+ (BOOL)catalogIsPrimaryDemo { - return YES; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - - self.view.backgroundColor = [UIColor whiteColor]; - - UIColor *blueColor = [UIColor colorWithRed:0.012 green:0.663 blue:0.957 alpha:0.2]; - _stackView = [[MDCHeaderStackView alloc] init]; - _stackView.backgroundColor = blueColor; - - UINavigationBar *topBar = [[UINavigationBar alloc] init]; - [topBar pushNavigationItem:self.navigationItem animated:NO]; - _stackView.topBar = topBar; - - UIToolbar *bottomBar = [[UIToolbar alloc] init]; - bottomBar.items = - @[ [[UIBarButtonItem alloc] initWithTitle:@"Toggle" - style:UIBarButtonItemStylePlain - target:self - action:@selector(didTapToggleButton:)] ]; - _stackView.bottomBar = bottomBar; - - CGRect frame = self.view.bounds; - frame.origin.y = 150; - _stackView.frame = frame; - [_stackView sizeToFit]; - - [self.view addSubview:_stackView]; -} - -#pragma mark User actions - -- (void)didTapToggleButton:(id)sender { - _toggled = !_toggled; - - [UIView animateWithDuration:0.4 - animations:^{ - CGRect frame = _stackView.frame; - if (_toggled) { - frame.size.height = 200; - } else { - frame.size = [_stackView sizeThatFits:_stackView.bounds.size]; - } - - _stackView.frame = frame; - [_stackView layoutIfNeeded]; - }]; -} - -@end diff --git a/components/HeaderStackView/examples/resources/mdc_theme.png b/components/HeaderStackView/examples/resources/mdc_theme.png new file mode 100644 index 0000000000000000000000000000000000000000..2aa056b38ebc5b0940dd1869b379529f6cadb00b GIT binary patch literal 11404 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 GEB_CKS)4!s literal 0 HcmV?d00001 diff --git a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.h b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.h new file mode 100644 index 00000000000..44b5ed390ac --- /dev/null +++ b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.h @@ -0,0 +1,42 @@ +/*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. +*/ +/* 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 + +@class ExampleInstructionsViewHeaderStackViewTypicalUse; +@class HeaderStackViewTypicalUse; +@class MDCHeaderStackView; +@class MDCNavigationBar; + +@interface HeaderStackViewTypicalUse : UIViewController + +@property(nonatomic) BOOL toggled; +@property(nonatomic) ExampleInstructionsViewHeaderStackViewTypicalUse *_Nullable exampleView; +@property(nonatomic) MDCHeaderStackView *_Nullable stackView; +@property(nonatomic) MDCNavigationBar *_Nullable navBar; +@property(nonatomic) UIBarButtonItem *_Nullable moreButton; +@property(nonatomic) UIView *_Nullable topView; + +@end + +@interface HeaderStackViewTypicalUse (Supplemental) + +- (void)setupExampleViews; + +@end diff --git a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m new file mode 100644 index 00000000000..e3852c98e2d --- /dev/null +++ b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m @@ -0,0 +1,203 @@ +/*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. + */ +/* 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 "HeaderStackViewTypicalUseSupplemental.h" + +#import "MaterialNavigationBar.h" +#import "MaterialHeaderStackView.h" + +@interface ExampleInstructionsViewHeaderStackViewTypicalUse : UIView + +@end + +@implementation HeaderStackViewTypicalUse (Supplemental) + +- (void)setupExampleViews { + self.exampleView = + [[ExampleInstructionsViewHeaderStackViewTypicalUse alloc] initWithFrame:self.view.bounds]; + [self.view addSubview:self.exampleView]; + + self.view.backgroundColor = [UIColor whiteColor]; + + self.topView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 100)]; + + UIImageView *imageView = [[UIImageView alloc] initWithImage:[self headerBackgroundImage]]; + imageView.frame = self.topView.bounds; + imageView.contentMode = UIViewContentModeScaleAspectFill; + imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + self.topView.clipsToBounds = YES; + [self.topView addSubview:imageView]; + + self.navBar = [[MDCNavigationBar alloc] initWithFrame:CGRectZero]; + self.navBar.tintColor = [UIColor whiteColor]; + self.navBar.title = @"Header Stack View"; + + // Light blue 500 + [self.navBar setBackgroundColor:[UIColor colorWithRed:0.012 green:0.663 blue:0.957 alpha:1]]; + + self.moreButton = [[UIBarButtonItem alloc] initWithTitle:@"More" + style:UIBarButtonItemStylePlain + target:self + action:@selector(didTapToggleButton:)]; + + self.navBar.rightBarButtonItems = @[ self.moreButton ]; +} + +- (void)didTapToggleButton:(id)sender { + self.toggled = !self.toggled; + + [UIView animateWithDuration:0.4 + animations:^{ + CGRect frame = self.stackView.frame; + if (self.toggled) { + frame.size.height = 200; + self.moreButton.title = @"Less"; + } else { + frame.size = [self.stackView sizeThatFits:self.stackView.bounds.size]; + self.moreButton.title = @"More"; + } + self.stackView.frame = frame; + [self.stackView layoutIfNeeded]; + }]; +} + +- (UIImage *)headerBackgroundImage { + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *imagePath = [bundle pathForResource:@"mdc_theme" ofType:@"png"]; + return [UIImage imageWithContentsOfFile:imagePath]; +} + +- (BOOL)prefersStatusBarHidden { + return YES; +} + +@end + +@implementation HeaderStackViewTypicalUse (CatalogByConvention) + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Header Stack View", @"Header Stack View" ]; +} + +- (BOOL)catalogShouldHideNavigation { + return YES; +} + ++ (NSString *)catalogDescription { + return @"The Header Stack View component is a view that coordinates the layout of two" + " vertically-stacked bar views."; +} + ++ (BOOL)catalogIsPrimaryDemo { + return YES; +} + +@end + +@implementation HeaderStackViewTypicalUse (Rotation) + +- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation + duration:(NSTimeInterval)duration { +} + +@end + +@implementation ExampleInstructionsViewHeaderStackViewTypicalUse + +- (void)drawRect:(CGRect)rect { + [[UIColor whiteColor] setFill]; + [[UIBezierPath bezierPathWithRect:rect] fill]; + + CGSize textSize = [self textSizeForRect:rect]; + CGRect rectForText = CGRectMake(rect.origin.x + rect.size.width / 2.f - textSize.width / 2.f, + rect.origin.y + rect.size.height / 2.f - textSize.height / 2.f, + textSize.width, textSize.height); + [[self instructionsString] drawInRect:rectForText]; + [self drawArrowWithFrame:CGRectMake(rect.size.width / 2.f - 12.f, + rect.size.height / 2.f - 58.f - 12.f, 24.f, 24.f)]; +} + +- (CGSize)textSizeForRect:(CGRect)frame { + return [[self instructionsString] + boundingRectWithSize:frame.size + options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading + context:nil] + .size; +} + +- (NSAttributedString *)instructionsString { + NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; + [style setAlignment:NSTextAlignmentCenter]; + [style setLineBreakMode:NSLineBreakByWordWrapping]; + + NSDictionary *instructionAttributes1 = + @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline], + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; + + NSDictionary *instructionAttributes2 = + @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline], + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; + + NSString *instructionText = @"SWIPE RIGHT\n\n\n\nto go back\n\n\n\n\n\n"; + NSMutableAttributedString *instructionsAttributedString = [[NSMutableAttributedString alloc] + initWithString:instructionText]; + [instructionsAttributedString setAttributes:instructionAttributes1 + range:NSMakeRange(0, 11)]; + [instructionsAttributedString setAttributes:instructionAttributes2 + range:NSMakeRange(11, instructionText.length - 11)]; + + return instructionsAttributedString; +} + +- (void)drawArrowWithFrame:(CGRect)frame { + UIBezierPath *bezierPath = [UIBezierPath bezierPath]; + [bezierPath moveToPoint:CGPointMake(CGRectGetMinX(frame) + 12, CGRectGetMinY(frame) + 4)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 10.59, + CGRectGetMinY(frame) + 5.41)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 16.17, CGRectGetMinY(frame) + 11)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 4, CGRectGetMinY(frame) + 11)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 4, CGRectGetMinY(frame) + 13)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 16.17, CGRectGetMinY(frame) + 13)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 10.59, + CGRectGetMinY(frame) + 18.59)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 12, CGRectGetMinY(frame) + 20)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 20, CGRectGetMinY(frame) + 12)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 12, CGRectGetMinY(frame) + 4)]; + [bezierPath closePath]; + bezierPath.miterLimit = 4; + + [[UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f] setFill]; + [bezierPath fill]; +} + +@end From 0b5851008432b68917425693613c5ec62b0768be Mon Sep 17 00:00:00 2001 From: randallli Date: Fri, 15 Apr 2016 17:31:02 -0400 Subject: [PATCH 031/129] [Abandon 4.1.0] Revert changes that somehow made it into develop. Reviewers: junius, featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D628 --- CHANGELOG.md | 8 --- MaterialComponents.podspec | 2 +- MaterialComponentsCatalog.podspec | 2 +- MaterialComponentsUnitTests.podspec | 2 +- catalog/Podfile.lock | 104 ++++++++++++++-------------- demos/Pesto/Podfile.lock | 96 ++++++++++++------------- demos/Shrine/Podfile.lock | 96 ++++++++++++------------- 7 files changed, 151 insertions(+), 159 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95e946733a9..d21e2ada55c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,3 @@ -# 4.1.0 - -Updated arrow back icon names. No API changes. - -Auto-generated by running: - - scripts/api_diff -o 1542251633905c3c2089b38f1c01a5010a8894f1 -n c0bd1de8e5520102f59e9b92d8a1085b285be38e - # 4.0.0 ## API diffs diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec index fe8ea29e6e9..0005a1bd18b 100644 --- a/MaterialComponents.podspec +++ b/MaterialComponents.podspec @@ -2,7 +2,7 @@ load 'scripts/generated/icons.rb' Pod::Spec.new do |s| s.name = "MaterialComponents" - s.version = "4.1.0" + s.version = "4.0.0" s.authors = { 'Apple platform engineering at Google' => 'appleplatforms@google.com' } s.summary = "A collection of stand-alone production-ready UI libraries focused on design details." s.homepage = "https://github.com/google/material-components-ios" diff --git a/MaterialComponentsCatalog.podspec b/MaterialComponentsCatalog.podspec index 8c1bdaee451..f5e87144f43 100644 --- a/MaterialComponentsCatalog.podspec +++ b/MaterialComponentsCatalog.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialComponentsCatalog" - s.version = "4.1.0" + s.version = "4.0.0" s.authors = { 'Apple platform engineering at Google' => 'appleplatforms@google.com' } s.summary = "A collection of stand-alone production-ready UI libraries focused on design details." s.homepage = "https://github.com/google/material-components-ios" diff --git a/MaterialComponentsUnitTests.podspec b/MaterialComponentsUnitTests.podspec index c124c0ad206..651d662bf7b 100644 --- a/MaterialComponentsUnitTests.podspec +++ b/MaterialComponentsUnitTests.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialComponentsUnitTests" - s.version = "4.1.0" + s.version = "4.0.0" s.authors = { 'Apple platform engineering at Google' => 'appleplatforms@google.com' } s.summary = "A collection of stand-alone production-ready UI libraries focused on design details." s.homepage = "https://github.com/google/material-components-ios" diff --git a/catalog/Podfile.lock b/catalog/Podfile.lock index 8b0f7265bb1..afddaf6b80b 100644 --- a/catalog/Podfile.lock +++ b/catalog/Podfile.lock @@ -1,24 +1,24 @@ PODS: - - MaterialComponents (4.1.0): - - MaterialComponents/AppBar (= 4.1.0) - - MaterialComponents/ButtonBar (= 4.1.0) - - MaterialComponents/Buttons (= 4.1.0) - - MaterialComponents/FlexibleHeader (= 4.1.0) - - MaterialComponents/FontDiskLoader (= 4.1.0) - - MaterialComponents/HeaderStackView (= 4.1.0) - - MaterialComponents/Ink (= 4.1.0) - - MaterialComponents/NavigationBar (= 4.1.0) - - MaterialComponents/PageControl (= 4.1.0) - - MaterialComponents/private (= 4.1.0) - - MaterialComponents/RobotoFontLoader (= 4.1.0) - - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.1.0) - - MaterialComponents/ShadowElevations (= 4.1.0) - - MaterialComponents/ShadowLayer (= 4.1.0) - - MaterialComponents/Slider (= 4.1.0) - - MaterialComponents/SpritedAnimationView (= 4.1.0) - - MaterialComponents/Switch (= 4.1.0) - - MaterialComponents/Typography (= 4.1.0) - - MaterialComponents/AppBar (4.1.0): + - MaterialComponents (4.0.0): + - MaterialComponents/AppBar (= 4.0.0) + - MaterialComponents/ButtonBar (= 4.0.0) + - MaterialComponents/Buttons (= 4.0.0) + - MaterialComponents/FlexibleHeader (= 4.0.0) + - MaterialComponents/FontDiskLoader (= 4.0.0) + - MaterialComponents/HeaderStackView (= 4.0.0) + - MaterialComponents/Ink (= 4.0.0) + - MaterialComponents/NavigationBar (= 4.0.0) + - MaterialComponents/PageControl (= 4.0.0) + - MaterialComponents/private (= 4.0.0) + - MaterialComponents/RobotoFontLoader (= 4.0.0) + - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.0.0) + - MaterialComponents/ShadowElevations (= 4.0.0) + - MaterialComponents/ShadowLayer (= 4.0.0) + - MaterialComponents/Slider (= 4.0.0) + - MaterialComponents/SpritedAnimationView (= 4.0.0) + - MaterialComponents/Switch (= 4.0.0) + - MaterialComponents/Typography (= 4.0.0) + - MaterialComponents/AppBar (4.0.0): - MaterialComponents/FlexibleHeader - MaterialComponents/HeaderStackView - MaterialComponents/NavigationBar @@ -26,52 +26,52 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/ButtonBar (4.1.0): + - MaterialComponents/ButtonBar (4.0.0): - MaterialComponents/Buttons - - MaterialComponents/Buttons (4.1.0): + - MaterialComponents/Buttons (4.0.0): - MaterialComponents/Ink - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/FlexibleHeader (4.1.0) - - MaterialComponents/FontDiskLoader (4.1.0) - - MaterialComponents/HeaderStackView (4.1.0) - - MaterialComponents/Ink (4.1.0) - - MaterialComponents/NavigationBar (4.1.0): + - MaterialComponents/FlexibleHeader (4.0.0) + - MaterialComponents/FontDiskLoader (4.0.0) + - MaterialComponents/HeaderStackView (4.0.0) + - MaterialComponents/Ink (4.0.0) + - MaterialComponents/NavigationBar (4.0.0): - MaterialComponents/ButtonBar - MaterialComponents/Typography - - MaterialComponents/PageControl (4.1.0) - - MaterialComponents/private (4.1.0): - - MaterialComponents/private/Color (= 4.1.0) - - MaterialComponents/private/Icons (= 4.1.0) - - MaterialComponents/private/ThumbTrack (= 4.1.0) - - MaterialComponents/private/Color (4.1.0) - - MaterialComponents/private/Icons (4.1.0): - - MaterialComponents/private/Icons/Base (= 4.1.0) - - MaterialComponents/private/Icons/ic_arrow_back (= 4.1.0) - - MaterialComponents/private/Icons/Base (4.1.0) - - MaterialComponents/private/Icons/ic_arrow_back (4.1.0): + - MaterialComponents/PageControl (4.0.0) + - MaterialComponents/private (4.0.0): + - MaterialComponents/private/Color (= 4.0.0) + - MaterialComponents/private/Icons (= 4.0.0) + - MaterialComponents/private/ThumbTrack (= 4.0.0) + - MaterialComponents/private/Color (4.0.0) + - MaterialComponents/private/Icons (4.0.0): + - MaterialComponents/private/Icons/Base (= 4.0.0) + - MaterialComponents/private/Icons/ic_arrow_back (= 4.0.0) + - MaterialComponents/private/Icons/Base (4.0.0) + - MaterialComponents/private/Icons/ic_arrow_back (4.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/ThumbTrack (4.1.0): + - MaterialComponents/private/ThumbTrack (4.0.0): - MaterialComponents/Ink - MaterialComponents/private/Color - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - - MaterialComponents/RobotoFontLoader (4.1.0): + - MaterialComponents/RobotoFontLoader (4.0.0): - MaterialComponents/FontDiskLoader - MaterialComponents/Typography - - MaterialComponents/ScrollViewDelegateMultiplexer (4.1.0) - - MaterialComponents/ShadowElevations (4.1.0) - - MaterialComponents/ShadowLayer (4.1.0) - - MaterialComponents/Slider (4.1.0): + - MaterialComponents/ScrollViewDelegateMultiplexer (4.0.0) + - MaterialComponents/ShadowElevations (4.0.0) + - MaterialComponents/ShadowLayer (4.0.0) + - MaterialComponents/Slider (4.0.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/SpritedAnimationView (4.1.0) - - MaterialComponents/Switch (4.1.0): + - MaterialComponents/SpritedAnimationView (4.0.0) + - MaterialComponents/Switch (4.0.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/Typography (4.1.0) - - MaterialComponentsCatalog (4.1.0): + - MaterialComponents/Typography (4.0.0) + - MaterialComponentsCatalog (4.0.0): - MaterialComponents - - MaterialComponentsUnitTests (4.1.0): + - MaterialComponentsUnitTests (4.0.0): - MaterialComponents DEPENDENCIES: @@ -88,8 +88,8 @@ EXTERNAL SOURCES: :path: ../ SPEC CHECKSUMS: - MaterialComponents: 909ae0687c67a89e70cfece537a752fac5a5efa2 - MaterialComponentsCatalog: 1e150e604b7143d4853f54da0e268a5c886d2f7c - MaterialComponentsUnitTests: 28f0e603d1b643f958bddb6608c75afb81c0c6c0 + MaterialComponents: b9f68b528c394b14b774591be1de18e25648c38a + MaterialComponentsCatalog: 82dc510292fedfd8a6c296b2d82da20dc24f0ef9 + MaterialComponentsUnitTests: 4cfd5c80aaf7b3f8155fc4052ad2ee153bac74fe COCOAPODS: 0.39.0 diff --git a/demos/Pesto/Podfile.lock b/demos/Pesto/Podfile.lock index d4bd0d48667..db4ecdc5e0b 100644 --- a/demos/Pesto/Podfile.lock +++ b/demos/Pesto/Podfile.lock @@ -1,24 +1,24 @@ PODS: - - MaterialComponents (4.1.0): - - MaterialComponents/AppBar (= 4.1.0) - - MaterialComponents/ButtonBar (= 4.1.0) - - MaterialComponents/Buttons (= 4.1.0) - - MaterialComponents/FlexibleHeader (= 4.1.0) - - MaterialComponents/FontDiskLoader (= 4.1.0) - - MaterialComponents/HeaderStackView (= 4.1.0) - - MaterialComponents/Ink (= 4.1.0) - - MaterialComponents/NavigationBar (= 4.1.0) - - MaterialComponents/PageControl (= 4.1.0) - - MaterialComponents/private (= 4.1.0) - - MaterialComponents/RobotoFontLoader (= 4.1.0) - - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.1.0) - - MaterialComponents/ShadowElevations (= 4.1.0) - - MaterialComponents/ShadowLayer (= 4.1.0) - - MaterialComponents/Slider (= 4.1.0) - - MaterialComponents/SpritedAnimationView (= 4.1.0) - - MaterialComponents/Switch (= 4.1.0) - - MaterialComponents/Typography (= 4.1.0) - - MaterialComponents/AppBar (4.1.0): + - MaterialComponents (4.0.0): + - MaterialComponents/AppBar (= 4.0.0) + - MaterialComponents/ButtonBar (= 4.0.0) + - MaterialComponents/Buttons (= 4.0.0) + - MaterialComponents/FlexibleHeader (= 4.0.0) + - MaterialComponents/FontDiskLoader (= 4.0.0) + - MaterialComponents/HeaderStackView (= 4.0.0) + - MaterialComponents/Ink (= 4.0.0) + - MaterialComponents/NavigationBar (= 4.0.0) + - MaterialComponents/PageControl (= 4.0.0) + - MaterialComponents/private (= 4.0.0) + - MaterialComponents/RobotoFontLoader (= 4.0.0) + - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.0.0) + - MaterialComponents/ShadowElevations (= 4.0.0) + - MaterialComponents/ShadowLayer (= 4.0.0) + - MaterialComponents/Slider (= 4.0.0) + - MaterialComponents/SpritedAnimationView (= 4.0.0) + - MaterialComponents/Switch (= 4.0.0) + - MaterialComponents/Typography (= 4.0.0) + - MaterialComponents/AppBar (4.0.0): - MaterialComponents/FlexibleHeader - MaterialComponents/HeaderStackView - MaterialComponents/NavigationBar @@ -26,49 +26,49 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/ButtonBar (4.1.0): + - MaterialComponents/ButtonBar (4.0.0): - MaterialComponents/Buttons - - MaterialComponents/Buttons (4.1.0): + - MaterialComponents/Buttons (4.0.0): - MaterialComponents/Ink - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/FlexibleHeader (4.1.0) - - MaterialComponents/FontDiskLoader (4.1.0) - - MaterialComponents/HeaderStackView (4.1.0) - - MaterialComponents/Ink (4.1.0) - - MaterialComponents/NavigationBar (4.1.0): + - MaterialComponents/FlexibleHeader (4.0.0) + - MaterialComponents/FontDiskLoader (4.0.0) + - MaterialComponents/HeaderStackView (4.0.0) + - MaterialComponents/Ink (4.0.0) + - MaterialComponents/NavigationBar (4.0.0): - MaterialComponents/ButtonBar - MaterialComponents/Typography - - MaterialComponents/PageControl (4.1.0) - - MaterialComponents/private (4.1.0): - - MaterialComponents/private/Color (= 4.1.0) - - MaterialComponents/private/Icons (= 4.1.0) - - MaterialComponents/private/ThumbTrack (= 4.1.0) - - MaterialComponents/private/Color (4.1.0) - - MaterialComponents/private/Icons (4.1.0): - - MaterialComponents/private/Icons/Base (= 4.1.0) - - MaterialComponents/private/Icons/ic_arrow_back (= 4.1.0) - - MaterialComponents/private/Icons/Base (4.1.0) - - MaterialComponents/private/Icons/ic_arrow_back (4.1.0): + - MaterialComponents/PageControl (4.0.0) + - MaterialComponents/private (4.0.0): + - MaterialComponents/private/Color (= 4.0.0) + - MaterialComponents/private/Icons (= 4.0.0) + - MaterialComponents/private/ThumbTrack (= 4.0.0) + - MaterialComponents/private/Color (4.0.0) + - MaterialComponents/private/Icons (4.0.0): + - MaterialComponents/private/Icons/Base (= 4.0.0) + - MaterialComponents/private/Icons/ic_arrow_back (= 4.0.0) + - MaterialComponents/private/Icons/Base (4.0.0) + - MaterialComponents/private/Icons/ic_arrow_back (4.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/ThumbTrack (4.1.0): + - MaterialComponents/private/ThumbTrack (4.0.0): - MaterialComponents/Ink - MaterialComponents/private/Color - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - - MaterialComponents/RobotoFontLoader (4.1.0): + - MaterialComponents/RobotoFontLoader (4.0.0): - MaterialComponents/FontDiskLoader - MaterialComponents/Typography - - MaterialComponents/ScrollViewDelegateMultiplexer (4.1.0) - - MaterialComponents/ShadowElevations (4.1.0) - - MaterialComponents/ShadowLayer (4.1.0) - - MaterialComponents/Slider (4.1.0): + - MaterialComponents/ScrollViewDelegateMultiplexer (4.0.0) + - MaterialComponents/ShadowElevations (4.0.0) + - MaterialComponents/ShadowLayer (4.0.0) + - MaterialComponents/Slider (4.0.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/SpritedAnimationView (4.1.0) - - MaterialComponents/Switch (4.1.0): + - MaterialComponents/SpritedAnimationView (4.0.0) + - MaterialComponents/Switch (4.0.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/Typography (4.1.0) + - MaterialComponents/Typography (4.0.0) DEPENDENCIES: - MaterialComponents (from `../../`) @@ -78,6 +78,6 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - MaterialComponents: 909ae0687c67a89e70cfece537a752fac5a5efa2 + MaterialComponents: b9f68b528c394b14b774591be1de18e25648c38a COCOAPODS: 0.39.0 diff --git a/demos/Shrine/Podfile.lock b/demos/Shrine/Podfile.lock index d4bd0d48667..db4ecdc5e0b 100644 --- a/demos/Shrine/Podfile.lock +++ b/demos/Shrine/Podfile.lock @@ -1,24 +1,24 @@ PODS: - - MaterialComponents (4.1.0): - - MaterialComponents/AppBar (= 4.1.0) - - MaterialComponents/ButtonBar (= 4.1.0) - - MaterialComponents/Buttons (= 4.1.0) - - MaterialComponents/FlexibleHeader (= 4.1.0) - - MaterialComponents/FontDiskLoader (= 4.1.0) - - MaterialComponents/HeaderStackView (= 4.1.0) - - MaterialComponents/Ink (= 4.1.0) - - MaterialComponents/NavigationBar (= 4.1.0) - - MaterialComponents/PageControl (= 4.1.0) - - MaterialComponents/private (= 4.1.0) - - MaterialComponents/RobotoFontLoader (= 4.1.0) - - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.1.0) - - MaterialComponents/ShadowElevations (= 4.1.0) - - MaterialComponents/ShadowLayer (= 4.1.0) - - MaterialComponents/Slider (= 4.1.0) - - MaterialComponents/SpritedAnimationView (= 4.1.0) - - MaterialComponents/Switch (= 4.1.0) - - MaterialComponents/Typography (= 4.1.0) - - MaterialComponents/AppBar (4.1.0): + - MaterialComponents (4.0.0): + - MaterialComponents/AppBar (= 4.0.0) + - MaterialComponents/ButtonBar (= 4.0.0) + - MaterialComponents/Buttons (= 4.0.0) + - MaterialComponents/FlexibleHeader (= 4.0.0) + - MaterialComponents/FontDiskLoader (= 4.0.0) + - MaterialComponents/HeaderStackView (= 4.0.0) + - MaterialComponents/Ink (= 4.0.0) + - MaterialComponents/NavigationBar (= 4.0.0) + - MaterialComponents/PageControl (= 4.0.0) + - MaterialComponents/private (= 4.0.0) + - MaterialComponents/RobotoFontLoader (= 4.0.0) + - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.0.0) + - MaterialComponents/ShadowElevations (= 4.0.0) + - MaterialComponents/ShadowLayer (= 4.0.0) + - MaterialComponents/Slider (= 4.0.0) + - MaterialComponents/SpritedAnimationView (= 4.0.0) + - MaterialComponents/Switch (= 4.0.0) + - MaterialComponents/Typography (= 4.0.0) + - MaterialComponents/AppBar (4.0.0): - MaterialComponents/FlexibleHeader - MaterialComponents/HeaderStackView - MaterialComponents/NavigationBar @@ -26,49 +26,49 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/ButtonBar (4.1.0): + - MaterialComponents/ButtonBar (4.0.0): - MaterialComponents/Buttons - - MaterialComponents/Buttons (4.1.0): + - MaterialComponents/Buttons (4.0.0): - MaterialComponents/Ink - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/FlexibleHeader (4.1.0) - - MaterialComponents/FontDiskLoader (4.1.0) - - MaterialComponents/HeaderStackView (4.1.0) - - MaterialComponents/Ink (4.1.0) - - MaterialComponents/NavigationBar (4.1.0): + - MaterialComponents/FlexibleHeader (4.0.0) + - MaterialComponents/FontDiskLoader (4.0.0) + - MaterialComponents/HeaderStackView (4.0.0) + - MaterialComponents/Ink (4.0.0) + - MaterialComponents/NavigationBar (4.0.0): - MaterialComponents/ButtonBar - MaterialComponents/Typography - - MaterialComponents/PageControl (4.1.0) - - MaterialComponents/private (4.1.0): - - MaterialComponents/private/Color (= 4.1.0) - - MaterialComponents/private/Icons (= 4.1.0) - - MaterialComponents/private/ThumbTrack (= 4.1.0) - - MaterialComponents/private/Color (4.1.0) - - MaterialComponents/private/Icons (4.1.0): - - MaterialComponents/private/Icons/Base (= 4.1.0) - - MaterialComponents/private/Icons/ic_arrow_back (= 4.1.0) - - MaterialComponents/private/Icons/Base (4.1.0) - - MaterialComponents/private/Icons/ic_arrow_back (4.1.0): + - MaterialComponents/PageControl (4.0.0) + - MaterialComponents/private (4.0.0): + - MaterialComponents/private/Color (= 4.0.0) + - MaterialComponents/private/Icons (= 4.0.0) + - MaterialComponents/private/ThumbTrack (= 4.0.0) + - MaterialComponents/private/Color (4.0.0) + - MaterialComponents/private/Icons (4.0.0): + - MaterialComponents/private/Icons/Base (= 4.0.0) + - MaterialComponents/private/Icons/ic_arrow_back (= 4.0.0) + - MaterialComponents/private/Icons/Base (4.0.0) + - MaterialComponents/private/Icons/ic_arrow_back (4.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/ThumbTrack (4.1.0): + - MaterialComponents/private/ThumbTrack (4.0.0): - MaterialComponents/Ink - MaterialComponents/private/Color - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - - MaterialComponents/RobotoFontLoader (4.1.0): + - MaterialComponents/RobotoFontLoader (4.0.0): - MaterialComponents/FontDiskLoader - MaterialComponents/Typography - - MaterialComponents/ScrollViewDelegateMultiplexer (4.1.0) - - MaterialComponents/ShadowElevations (4.1.0) - - MaterialComponents/ShadowLayer (4.1.0) - - MaterialComponents/Slider (4.1.0): + - MaterialComponents/ScrollViewDelegateMultiplexer (4.0.0) + - MaterialComponents/ShadowElevations (4.0.0) + - MaterialComponents/ShadowLayer (4.0.0) + - MaterialComponents/Slider (4.0.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/SpritedAnimationView (4.1.0) - - MaterialComponents/Switch (4.1.0): + - MaterialComponents/SpritedAnimationView (4.0.0) + - MaterialComponents/Switch (4.0.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/Typography (4.1.0) + - MaterialComponents/Typography (4.0.0) DEPENDENCIES: - MaterialComponents (from `../../`) @@ -78,6 +78,6 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - MaterialComponents: 909ae0687c67a89e70cfece537a752fac5a5efa2 + MaterialComponents: b9f68b528c394b14b774591be1de18e25648c38a COCOAPODS: 0.39.0 From e3a56fe9624ab978850665ab4f06479ebe5e13bf Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Fri, 15 Apr 2016 23:54:09 -0400 Subject: [PATCH 032/129] [ButtonBar] Check UIBarButtonItem global appearance configuration when creating the buttons. Summary: Related to https://github.com/google/material-components-ios/issues/370. Reviewers: randallli, #mdc_ios_owners, larche Reviewed By: randallli, #mdc_ios_owners, larche Projects: #material_components_ios Differential Revision: http://codereview.cc/D625 --- .../src/private/MDCAppBarButtonBarBuilder.m | 10 +- .../tests/unit/ButtonBarIssue370Tests.swift | 101 ++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 components/ButtonBar/tests/unit/ButtonBarIssue370Tests.swift diff --git a/components/ButtonBar/src/private/MDCAppBarButtonBarBuilder.m b/components/ButtonBar/src/private/MDCAppBarButtonBarBuilder.m index 26c9ee97c73..3491135cbea 100644 --- a/components/ButtonBar/src/private/MDCAppBarButtonBarBuilder.m +++ b/components/ButtonBar/src/private/MDCAppBarButtonBarBuilder.m @@ -202,7 +202,15 @@ - (void)updateButton:(UIButton *)button barMetrics:(UIBarMetrics)barMetrics { NSString *title = item.title ?: @""; if ([UIButton instancesRespondToSelector:@selector(setAttributedTitle:forState:)]) { - NSDictionary *attributes = [item titleTextAttributesForState:state]; + NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; + + // UIBarButtonItem's appearance proxy values don't appear to come "for free" like they do with + // typical UIView instances, so we're attempting to recreate the behavior here. + NSArray *appearanceProxies = @[ [item.class appearance] ]; + for (UIBarButtonItem *appearance in appearanceProxies) { + [attributes addEntriesFromDictionary:[appearance titleTextAttributesForState:state]]; + } + [attributes addEntriesFromDictionary:[item titleTextAttributesForState:state]]; if ([attributes count] > 0) { [button setAttributedTitle:[[NSAttributedString alloc] initWithString:title attributes:attributes] diff --git a/components/ButtonBar/tests/unit/ButtonBarIssue370Tests.swift b/components/ButtonBar/tests/unit/ButtonBarIssue370Tests.swift new file mode 100644 index 00000000000..fdd1114b9bf --- /dev/null +++ b/components/ButtonBar/tests/unit/ButtonBarIssue370Tests.swift @@ -0,0 +1,101 @@ +/* +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 XCTest +import MaterialComponents + +// Tests confirming that the Button Bar respects UI appearance for bar button item +// titleTextAttributes. +// +// Based on issue https://github.com/google/material-components-ios/issues/370 +class ButtonBarIssue370Tests: XCTestCase { + + var buttonBar: MDCButtonBar! + let globalAttributes = [NSForegroundColorAttributeName:UIColor.blueColor()] + let directAttributes = [NSForegroundColorAttributeName:UIColor.blueColor()] + let fontAttributes = [NSFontAttributeName:UIFont.systemFontOfSize(12)] + + override func setUp() { + buttonBar = MDCButtonBar() + } + + override func tearDown() { + UIBarButtonItem.appearance().setTitleTextAttributes(nil, forState: .Normal) + } + + func testDirectOnly() { + let item = UIBarButtonItem(title: "Text", style: .Plain, target: nil, action: nil) + item.setTitleTextAttributes(directAttributes, forState: .Normal) + buttonBar.items = [item] + + forEachButton { button in + let attributes = button.titleLabel?.attributedText?.attributesAtIndex(0, effectiveRange: nil) + XCTAssertTrue(NSDictionary(dictionary: self.directAttributes).isEqualToDictionary(attributes!)) + } + } + + func testGlobalAppearanceOnly() { + UIBarButtonItem.appearance().setTitleTextAttributes(globalAttributes, forState: .Normal) + let item = UIBarButtonItem(title: "Text", style: .Plain, target: nil, action: nil) + buttonBar.items = [item] + + forEachButton { button in + let attributes = button.titleLabel?.attributedText?.attributesAtIndex(0, effectiveRange: nil) + XCTAssertTrue(NSDictionary(dictionary: self.globalAttributes).isEqualToDictionary(attributes!)) + } + } + + func testGlobalAppearanceAndDirectOverwriting() { + UIBarButtonItem.appearance().setTitleTextAttributes(globalAttributes, forState: .Normal) + + let item = UIBarButtonItem(title: "Text", style: .Plain, target: nil, action: nil) + + // Should take priority. + item.setTitleTextAttributes(directAttributes, forState: .Normal) + + buttonBar.items = [item] + + forEachButton { button in + let attributes = button.titleLabel?.attributedText?.attributesAtIndex(0, effectiveRange: nil) + XCTAssertTrue(NSDictionary(dictionary: self.directAttributes).isEqualToDictionary(attributes!)) + } + } + + func testGlobalAppearanceAndDirectMerging() { + UIBarButtonItem.appearance().setTitleTextAttributes(fontAttributes, forState: .Normal) + let item = UIBarButtonItem(title: "Text", style: .Plain, target: nil, action: nil) + item.setTitleTextAttributes(directAttributes, forState: .Normal) + buttonBar.items = [item] + + var composite: [String: AnyObject] = fontAttributes + for (key, value) in directAttributes { + composite[key] = value + } + + self.forEachButton { button in + let attributes = button.titleLabel?.attributedText?.attributesAtIndex(0, effectiveRange: nil) + XCTAssertTrue(NSDictionary(dictionary: composite).isEqualToDictionary(attributes!)) + } + } + + func forEachButton(work: MDCButton -> Void) { + for view in buttonBar.subviews { + if let button = view as? MDCButton { + work(button) + } + } + } +} From 9f41faf6af1457d6056d50b902a0d81bf6f67de9 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Fri, 15 Apr 2016 16:17:34 -0400 Subject: [PATCH 033/129] [Catalog] Use MDCAppBarContainerViewController to wrap examples. Summary: Removes a good amount of duplicated logic. This ensures that we now observe navigationItem properties. Closes https://github.com/google/material-components-ios/issues/366. Reviewers: junius, #mdc_ios_owners Reviewed By: junius, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D626 --- catalog/MDCCatalog.xcodeproj/project.pbxproj | 4 -- ...CCatalogTypicalExampleViewController.swift | 60 ------------------- catalog/MDCCatalog/NodeViewController.swift | 30 +++++++++- 3 files changed, 28 insertions(+), 66 deletions(-) delete mode 100644 catalog/MDCCatalog/MDCCatalogTypicalExampleViewController.swift diff --git a/catalog/MDCCatalog.xcodeproj/project.pbxproj b/catalog/MDCCatalog.xcodeproj/project.pbxproj index 13d8032470a..a8ed35518d4 100644 --- a/catalog/MDCCatalog.xcodeproj/project.pbxproj +++ b/catalog/MDCCatalog.xcodeproj/project.pbxproj @@ -32,7 +32,6 @@ DE1944931CBD9E40009E0321 /* MDCCatalogTileDataSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = DE1944821CBD9E40009E0321 /* MDCCatalogTileDataSwitch.m */; }; DE1944941CBD9E40009E0321 /* MDCCatalogTileDataTypography.m in Sources */ = {isa = PBXBuildFile; fileRef = DE1944841CBD9E40009E0321 /* MDCCatalogTileDataTypography.m */; }; DE309CF31C8DEB8400E73247 /* MDCCatalogComponentsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */; }; - DE3799581CAD9AB00036D5B0 /* MDCCatalogTypicalExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE3799571CAD9AB00036D5B0 /* MDCCatalogTypicalExampleViewController.swift */; }; DE5BD5AE1CB3F7F100D8D75D /* MDCCatalogTile.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE5BD5AD1CB3F7F100D8D75D /* MDCCatalogTile.swift */; }; DEEC11E51CA9AC3A00A15FC8 /* MDCCatalogBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEEC11E41CA9AC3A00A15FC8 /* MDCCatalogBar.swift */; }; DEF64EA41C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF64EA31C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift */; }; @@ -132,7 +131,6 @@ DE1944841CBD9E40009E0321 /* MDCCatalogTileDataTypography.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MDCCatalogTileDataTypography.m; sourceTree = ""; }; DE1944851CBD9E40009E0321 /* MDCCatalogTiles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDCCatalogTiles.h; sourceTree = ""; }; DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogComponentsController.swift; sourceTree = ""; }; - DE3799571CAD9AB00036D5B0 /* MDCCatalogTypicalExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogTypicalExampleViewController.swift; sourceTree = ""; }; DE5BD5AD1CB3F7F100D8D75D /* MDCCatalogTile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogTile.swift; sourceTree = ""; }; DEEC11E41CA9AC3A00A15FC8 /* MDCCatalogBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogBar.swift; sourceTree = ""; }; DEF64EA31C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogCollectionViewCell.swift; sourceTree = ""; }; @@ -199,7 +197,6 @@ DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */, DE5BD5AD1CB3F7F100D8D75D /* MDCCatalogTile.swift */, DE5BD5AF1CB3F8FF00D8D75D /* MDCCatalogTiles */, - DE3799571CAD9AB00036D5B0 /* MDCCatalogTypicalExampleViewController.swift */, 664524B81C6BA62A001ADBF8 /* NodeViewController.swift */, 665A34DE1C6BDAE700962055 /* Resources */, ); @@ -464,7 +461,6 @@ DE309CF31C8DEB8400E73247 /* MDCCatalogComponentsController.swift in Sources */, DE19448C1CBD9E40009E0321 /* MDCCatalogTileDataInk.m in Sources */, 664524B91C6BA62A001ADBF8 /* NodeViewController.swift in Sources */, - DE3799581CAD9AB00036D5B0 /* MDCCatalogTypicalExampleViewController.swift in Sources */, DE19448D1CBD9E40009E0321 /* MDCCatalogTileDataMisc.m in Sources */, DE19448B1CBD9E40009E0321 /* MDCCatalogTileDataHeaderStackView.m in Sources */, DE5BD5AE1CB3F7F100D8D75D /* MDCCatalogTile.swift in Sources */, diff --git a/catalog/MDCCatalog/MDCCatalogTypicalExampleViewController.swift b/catalog/MDCCatalog/MDCCatalogTypicalExampleViewController.swift deleted file mode 100644 index b79b61c60b4..00000000000 --- a/catalog/MDCCatalog/MDCCatalogTypicalExampleViewController.swift +++ /dev/null @@ -1,60 +0,0 @@ -/* -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 UIKit -import MaterialComponents - -class MDCCatalogTypicalExampleViewController: UIViewController { - let appBar = MDCAppBar() - var contentViewController = UIViewController() - - init(contentViewController: UIViewController, title: String) { - super.init(nibName: nil, bundle: nil) - assert(contentViewController.view != nil, "expecting a valid contentViewController") - self.contentViewController = contentViewController - self.addChildViewController(contentViewController) - self.title = title - - self.addChildViewController(appBar.headerViewController) - appBar.headerViewController.headerView.backgroundColor = UIColor.whiteColor() - - let headerContentView = appBar.headerViewController.headerView - let lineFrame = CGRectMake(0, headerContentView.frame.height, headerContentView.frame.width, 1) - let line = UIView(frame: lineFrame) - line.backgroundColor = UIColor(white: 0.72, alpha: 1) - line.autoresizingMask = [.FlexibleTopMargin, .FlexibleWidth] - headerContentView.addSubview(line) - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - override func viewDidLoad() { - let headerHeight = appBar.headerViewController.headerView.minimumHeight - self.view.addSubview(contentViewController.view) - contentViewController.didMoveToParentViewController(self) - contentViewController.view.frame = - CGRectMake(0, - headerHeight, - self.view.bounds.size.width, - self.view.bounds.size.height - headerHeight) - contentViewController.view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] - - appBar.addSubviewsToParent() - } - -} diff --git a/catalog/MDCCatalog/NodeViewController.swift b/catalog/MDCCatalog/NodeViewController.swift index 6311c3c0ded..3c8b003fbfd 100644 --- a/catalog/MDCCatalog/NodeViewController.swift +++ b/catalog/MDCCatalog/NodeViewController.swift @@ -257,8 +257,34 @@ class NodeViewController: CBCNodeListViewController { if contentVC.respondsToSelector("catalogShouldHideNavigation") { vc = contentVC } else { - vc = MDCCatalogTypicalExampleViewController(contentViewController: contentVC, - title: node.title) + let container = MDCAppBarContainerViewController(contentViewController: contentVC) + + // TODO(featherless): Remove once + // https://github.com/google/material-components-ios/issues/367 is resolved. + contentVC.title = node.title + + let headerView = container.appBar.headerViewController.headerView + + headerView.backgroundColor = UIColor.whiteColor() + + let textColor = UIColor(white: 0, alpha: 0.8) + UIBarButtonItem.appearance().setTitleTextAttributes( + [NSForegroundColorAttributeName:textColor], + forState: .Normal) + + let lineFrame = CGRectMake(0, headerView.bounds.height, headerView.bounds.width, 1) + let line = UIView(frame: lineFrame) + line.backgroundColor = UIColor(white: 0.72, alpha: 1) + line.autoresizingMask = [.FlexibleTopMargin, .FlexibleWidth] + headerView.addSubview(line) + + var contentFrame = container.contentViewController.view.frame + let headerSize = headerView.sizeThatFits(container.contentViewController.view.frame.size) + contentFrame.origin.y += headerSize.height + contentFrame.size.height -= headerSize.height + container.contentViewController.view.frame = contentFrame + + vc = container } } else { vc = NodeViewController(node: node) From 8f3c3f8607ff295b8594ea1edbe8dadb244eaaaa Mon Sep 17 00:00:00 2001 From: randallli Date: Mon, 18 Apr 2016 10:54:28 -0400 Subject: [PATCH 034/129] [AppBar]! NSLog warning to NSAssert for incorrect parentViewController behavior. Summary: closes https://github.com/google/material-components-ios/issues/341 Reviewers: featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D635 --- components/AppBar/src/MDCAppBar.m | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/components/AppBar/src/MDCAppBar.m b/components/AppBar/src/MDCAppBar.m index 8ece08dfa02..3aa4ff21a5c 100644 --- a/components/AppBar/src/MDCAppBar.m +++ b/components/AppBar/src/MDCAppBar.m @@ -78,13 +78,10 @@ - (void)addHeaderViewControllerToParentViewController:(nonnull UIViewController - (void)addSubviewsToParent { MDCFlexibleHeaderViewController *fhvc = self.headerViewController; -#if DEBUG - if (!fhvc.parentViewController) { - NSLog(@"headerViewController does not have a parentViewController. " - @"Use [self addChildViewController:appBar.headerViewController]. " - @"This warning only appears in DEBUG builds"); - } -#endif //DEBUG + NSAssert(fhvc.parentViewController, + @"headerViewController does not have a parentViewController. " + @"Use [self addChildViewController:appBar.headerViewController]. " + @"This warning only appears in DEBUG builds"); if (fhvc.view.superview == fhvc.parentViewController.view) { return; } From a147d7b359d1d99fca81aa97594d93263a3cedee Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Mon, 18 Apr 2016 14:11:52 -0400 Subject: [PATCH 035/129] [Catalog] Update AppBar demo design, table view should not have text Reviewers: ajsecord, featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D640 --- .../AppBar/examples/AppBarDelegateForwardingExample.m | 9 ++++++--- .../examples/AppBarDelegateForwardingExample.swift | 9 ++++++--- components/AppBar/examples/AppBarImageryExample.m | 5 ++++- components/AppBar/examples/AppBarImageryExample.swift | 2 +- components/AppBar/examples/AppBarTypicalUseExample.m | 5 ++++- components/AppBar/examples/AppBarTypicalUseExample.swift | 5 ++++- .../supplemental/HeaderStackViewTypicalUseSupplemental.m | 1 - 7 files changed, 25 insertions(+), 11 deletions(-) diff --git a/components/AppBar/examples/AppBarDelegateForwardingExample.m b/components/AppBar/examples/AppBarDelegateForwardingExample.m index 65a554143f6..69036b4b74b 100644 --- a/components/AppBar/examples/AppBarDelegateForwardingExample.m +++ b/components/AppBar/examples/AppBarDelegateForwardingExample.m @@ -35,6 +35,9 @@ - (void)viewDidLoad { self.appBar.headerViewController.headerView.trackingScrollView = self.tableView; [self.appBar addSubviewsToParent]; + + self.tableView.layoutMargins = UIEdgeInsetsZero; + self.tableView.separatorInset = UIEdgeInsetsZero; } #pragma mark - UIScrollViewDelegate @@ -76,7 +79,7 @@ - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView @implementation AppBarDelegateForwardingExample (CatalogByConvention) + (NSArray *)catalogBreadcrumbs { - return @[ @"App Bar", @"Delegate forwarding" ]; + return @[ @"App Bar", @"Delegate Forwarding" ]; } - (BOOL)catalogShouldHideNavigation { @@ -94,7 +97,7 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil _appBar.navigationBar.tintColor = [UIColor whiteColor]; [self addChildViewController:_appBar.headerViewController]; - self.title = @"Delegate forwarding"; + self.title = @"Delegate Forwarding"; UIColor *color = [UIColor colorWithRed:(CGFloat)0x03 / (CGFloat)255 green:(CGFloat)0xA9 / (CGFloat)255 @@ -136,7 +139,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; } - cell.textLabel.text = [NSString stringWithFormat:@"%ld", (long)indexPath.row]; + cell.layoutMargins = UIEdgeInsetsZero; return cell; } diff --git a/components/AppBar/examples/AppBarDelegateForwardingExample.swift b/components/AppBar/examples/AppBarDelegateForwardingExample.swift index c95a6efeb81..48b7ed1a94c 100644 --- a/components/AppBar/examples/AppBarDelegateForwardingExample.swift +++ b/components/AppBar/examples/AppBarDelegateForwardingExample.swift @@ -32,6 +32,9 @@ class AppBarDelegateForwardingExample: UITableViewController { appBar.headerViewController.headerView.trackingScrollView = self.tableView appBar.addSubviewsToParent() + + self.tableView.layoutMargins = UIEdgeInsetsZero + self.tableView.separatorInset = UIEdgeInsetsZero } // The following four methods must be forwarded to the tracking scroll view in order to implement @@ -68,7 +71,7 @@ class AppBarDelegateForwardingExample: UITableViewController { override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) - self.title = "Delegate forwarding (Swift)" + self.title = "Delegate Forwarding (Swift)" self.addChildViewController(appBar.headerViewController) @@ -89,7 +92,7 @@ class AppBarDelegateForwardingExample: UITableViewController { // MARK: Catalog by convention extension AppBarDelegateForwardingExample { class func catalogBreadcrumbs() -> [String] { - return ["App Bar", "Delegate forwarding (Swift)"] + return ["App Bar", "Delegate Forwarding (Swift)"] } func catalogShouldHideNavigation() -> Bool { return true @@ -128,7 +131,7 @@ extension AppBarDelegateForwardingExample { if cell == nil { cell = UITableViewCell(style: .Default, reuseIdentifier: "cell") } - cell!.textLabel!.text = "\(indexPath.row)" + cell!.layoutMargins = UIEdgeInsetsZero return cell! } diff --git a/components/AppBar/examples/AppBarImageryExample.m b/components/AppBar/examples/AppBarImageryExample.m index 5b30281843f..d55a8a26fd0 100644 --- a/components/AppBar/examples/AppBarImageryExample.m +++ b/components/AppBar/examples/AppBarImageryExample.m @@ -55,6 +55,9 @@ - (void)viewDidLoad { self.tableView.delegate = self.appBar.headerViewController; [self.appBar addSubviewsToParent]; + + self.tableView.layoutMargins = UIEdgeInsetsZero; + self.tableView.separatorInset = UIEdgeInsetsZero; } - (UIStatusBarStyle)preferredStatusBarStyle { @@ -119,7 +122,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; } - cell.textLabel.text = [NSString stringWithFormat:@"%ld", (long)indexPath.row]; + cell.layoutMargins = UIEdgeInsetsZero; return cell; } diff --git a/components/AppBar/examples/AppBarImageryExample.swift b/components/AppBar/examples/AppBarImageryExample.swift index 761747487ee..b96d7b7146c 100644 --- a/components/AppBar/examples/AppBarImageryExample.swift +++ b/components/AppBar/examples/AppBarImageryExample.swift @@ -106,7 +106,7 @@ extension AppBarImagerySwiftExample { if cell == nil { cell = UITableViewCell(style: .Default, reuseIdentifier: "cell") } - cell!.textLabel!.text = "\(indexPath.row)" + cell!.layoutMargins = UIEdgeInsetsZero return cell! } } diff --git a/components/AppBar/examples/AppBarTypicalUseExample.m b/components/AppBar/examples/AppBarTypicalUseExample.m index 1b5fbdc7369..58ce3ae1f9a 100644 --- a/components/AppBar/examples/AppBarTypicalUseExample.m +++ b/components/AppBar/examples/AppBarTypicalUseExample.m @@ -59,6 +59,9 @@ - (void)viewDidLoad { // Step 3: Register the App Bar views. [self.appBar addSubviewsToParent]; + + self.tableView.layoutMargins = UIEdgeInsetsZero; + self.tableView.separatorInset = UIEdgeInsetsZero; } // Optional step: If you allow the header view to hide the status bar you must implement this @@ -119,7 +122,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; } - cell.textLabel.text = [NSString stringWithFormat:@"%ld", (long)indexPath.row]; + cell.layoutMargins = UIEdgeInsetsZero; return cell; } diff --git a/components/AppBar/examples/AppBarTypicalUseExample.swift b/components/AppBar/examples/AppBarTypicalUseExample.swift index 0bfaa556b8f..7cd29e4d227 100644 --- a/components/AppBar/examples/AppBarTypicalUseExample.swift +++ b/components/AppBar/examples/AppBarTypicalUseExample.swift @@ -56,6 +56,9 @@ class AppBarTypicalUseSwiftExample: UITableViewController { // Step 3: Register the App Bar views. appBar.addSubviewsToParent() + + self.tableView.layoutMargins = UIEdgeInsetsZero + self.tableView.separatorInset = UIEdgeInsetsZero } // Optional step: If you allow the header view to hide the status bar you must implement this @@ -105,7 +108,7 @@ extension AppBarTypicalUseSwiftExample { if cell == nil { cell = UITableViewCell(style: .Default, reuseIdentifier: "cell") } - cell!.textLabel!.text = "\(indexPath.row)" + cell!.layoutMargins = UIEdgeInsetsZero return cell! } diff --git a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m index e3852c98e2d..800df2f297b 100644 --- a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m +++ b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m @@ -58,7 +58,6 @@ - (void)setupExampleViews { style:UIBarButtonItemStylePlain target:self action:@selector(didTapToggleButton:)]; - self.navBar.rightBarButtonItems = @[ self.moreButton ]; } From 753838833b5ba17503b685e8a7172a49dd19e584 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Mon, 18 Apr 2016 14:55:13 -0400 Subject: [PATCH 036/129] Set arc.feature.start.default to origin/develop. Summary: This makes `arc feature` default to starting from origin/develop. This can still be overridden by providing an explicit branch to `arc feature`. See `arc help feature` for more details. Reviewers: #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Subscribers: randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D642 --- .arcconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/.arcconfig b/.arcconfig index 07bbc247a35..e44627df053 100644 --- a/.arcconfig +++ b/.arcconfig @@ -9,6 +9,7 @@ "phabricator.uri" : "http://codereview.cc/", "repository.callsign" : "MDC", "arc.land.onto.default" : "develop", + "arc.feature.start.default": "origin/develop", "git.default-relative-commit" : "origin/develop", "unit.xcode": { "build": { From 788a302c178520ab189d6eebf7c3bdc9f89ac218 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Mon, 18 Apr 2016 15:50:35 -0400 Subject: [PATCH 037/129] [Catalog] Rename NodeViewController to MDCNodeListViewController. Reviewers: #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D651 --- catalog/MDCCatalog.xcodeproj/project.pbxproj | 8 ++++---- catalog/MDCCatalog/MDCCatalogComponentsController.swift | 2 +- ...ewController.swift => MDCNodeListViewController.swift} | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename catalog/MDCCatalog/{NodeViewController.swift => MDCNodeListViewController.swift} (98%) diff --git a/catalog/MDCCatalog.xcodeproj/project.pbxproj b/catalog/MDCCatalog.xcodeproj/project.pbxproj index a8ed35518d4..e9e17669414 100644 --- a/catalog/MDCCatalog.xcodeproj/project.pbxproj +++ b/catalog/MDCCatalog.xcodeproj/project.pbxproj @@ -11,7 +11,7 @@ 5D090E571C9AEB8D0061344A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5D090E341C9AEB8C0061344A /* Localizable.strings */; }; 6642AB811CBDBE0900F5B1D7 /* CBCRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = 6642AB801CBDBE0900F5B1D7 /* CBCRuntime.m */; }; 664524B71C6BA62A001ADBF8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664524B61C6BA62A001ADBF8 /* AppDelegate.swift */; }; - 664524B91C6BA62A001ADBF8 /* NodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664524B81C6BA62A001ADBF8 /* NodeViewController.swift */; }; + 664524B91C6BA62A001ADBF8 /* MDCNodeListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664524B81C6BA62A001ADBF8 /* MDCNodeListViewController.swift */; }; 664524BE1C6BA62A001ADBF8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664524BD1C6BA62A001ADBF8 /* Assets.xcassets */; }; 664524C11C6BA62A001ADBF8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 664524BF1C6BA62A001ADBF8 /* LaunchScreen.storyboard */; }; 666CA70D1CAEBCA9001B1884 /* CBCNodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 666CA70A1CAEBCA9001B1884 /* CBCNodeViewController.m */; }; @@ -85,7 +85,7 @@ 6642AB801CBDBE0900F5B1D7 /* CBCRuntime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CBCRuntime.m; sourceTree = ""; }; 664524B31C6BA62A001ADBF8 /* MDCCatalog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MDCCatalog.app; sourceTree = BUILT_PRODUCTS_DIR; }; 664524B61C6BA62A001ADBF8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 664524B81C6BA62A001ADBF8 /* NodeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeViewController.swift; sourceTree = ""; }; + 664524B81C6BA62A001ADBF8 /* MDCNodeListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MDCNodeListViewController.swift; sourceTree = ""; }; 664524BD1C6BA62A001ADBF8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 664524C01C6BA62A001ADBF8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 664524C21C6BA62A001ADBF8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -196,8 +196,8 @@ DEF64EA31C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift */, DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */, DE5BD5AD1CB3F7F100D8D75D /* MDCCatalogTile.swift */, + 664524B81C6BA62A001ADBF8 /* MDCNodeListViewController.swift */, DE5BD5AF1CB3F8FF00D8D75D /* MDCCatalogTiles */, - 664524B81C6BA62A001ADBF8 /* NodeViewController.swift */, 665A34DE1C6BDAE700962055 /* Resources */, ); path = MDCCatalog; @@ -460,7 +460,7 @@ DEEC11E51CA9AC3A00A15FC8 /* MDCCatalogBar.swift in Sources */, DE309CF31C8DEB8400E73247 /* MDCCatalogComponentsController.swift in Sources */, DE19448C1CBD9E40009E0321 /* MDCCatalogTileDataInk.m in Sources */, - 664524B91C6BA62A001ADBF8 /* NodeViewController.swift in Sources */, + 664524B91C6BA62A001ADBF8 /* MDCNodeListViewController.swift in Sources */, DE19448D1CBD9E40009E0321 /* MDCCatalogTileDataMisc.m in Sources */, DE19448B1CBD9E40009E0321 /* MDCCatalogTileDataHeaderStackView.m in Sources */, DE5BD5AE1CB3F7F100D8D75D /* MDCCatalogTile.swift in Sources */, diff --git a/catalog/MDCCatalog/MDCCatalogComponentsController.swift b/catalog/MDCCatalog/MDCCatalogComponentsController.swift index ae5f97e072b..e1306a3512e 100644 --- a/catalog/MDCCatalog/MDCCatalogComponentsController.swift +++ b/catalog/MDCCatalog/MDCCatalogComponentsController.swift @@ -149,7 +149,7 @@ class MDCCatalogComponentsController: UICollectionViewController { if node.isExample() { vc = node.createExampleViewController() } else { - vc = NodeViewController(node: node) + vc = MDCNodeListViewController(node: node) } self.navigationController?.pushViewController(vc, animated: true) } diff --git a/catalog/MDCCatalog/NodeViewController.swift b/catalog/MDCCatalog/MDCNodeListViewController.swift similarity index 98% rename from catalog/MDCCatalog/NodeViewController.swift rename to catalog/MDCCatalog/MDCNodeListViewController.swift index 3c8b003fbfd..6c2e1452129 100644 --- a/catalog/MDCCatalog/NodeViewController.swift +++ b/catalog/MDCCatalog/MDCNodeListViewController.swift @@ -33,7 +33,7 @@ class NodeViewTableViewDemoCell: UITableViewCell { } -class NodeViewController: CBCNodeListViewController { +class MDCNodeListViewController: CBCNodeListViewController { let appBar = MDCAppBar() let sectionNames = ["Description", "Additional Examples"] let descriptionSectionHeight = CGFloat(100) @@ -287,7 +287,7 @@ class NodeViewController: CBCNodeListViewController { vc = container } } else { - vc = NodeViewController(node: node) + vc = MDCNodeListViewController(node: node) } self.navigationController?.pushViewController(vc, animated: true) } From 1e84b7cd5173d85bffb77628341e91d08a90ced6 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Sat, 16 Apr 2016 01:05:30 -0400 Subject: [PATCH 038/129] [NavigationBar] Use UIViewNoIntrinsicMetric to indicate the the NavigationBar has no intrinsic width. Reviewers: #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Subscribers: iangordon Projects: #material_components_ios Differential Revision: http://codereview.cc/D629 --- components/NavigationBar/src/MDCNavigationBar.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/NavigationBar/src/MDCNavigationBar.m b/components/NavigationBar/src/MDCNavigationBar.m index 56a7ab852a5..03b987e6940 100644 --- a/components/NavigationBar/src/MDCNavigationBar.m +++ b/components/NavigationBar/src/MDCNavigationBar.m @@ -235,7 +235,7 @@ - (CGSize)sizeThatFits:(CGSize)size { - (CGSize)intrinsicContentSize { const BOOL isPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad; CGFloat height = (isPad ? kNavigationBarPadDefaultHeight : kNavigationBarDefaultHeight); - return CGSizeMake(self.superview.superview.bounds.size.width, height); + return CGSizeMake(UIViewNoIntrinsicMetric, height); } #pragma mark - Private From c92a9510bc8ed6d18339b4d7928cc470cc2a4f07 Mon Sep 17 00:00:00 2001 From: Ian Gordon Date: Mon, 18 Apr 2016 15:09:24 -0400 Subject: [PATCH 039/129] [Switch] Rename label from slider to switch. Reviewers: junius, #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D645 --- .../Switch/examples/supplemental/SwitchTypicalUseSupplemental.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m b/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m index f6eeca02927..4af671ce2a1 100644 --- a/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m +++ b/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m @@ -31,7 +31,7 @@ @implementation SwitchTypicalUseViewController (Supplemental) - (void)setupExampleViews { UILabel *switchLabel = [[UILabel alloc] init]; - switchLabel.text = @"Slider"; + switchLabel.text = @"Switch"; switchLabel.font = [MDCTypography captionFont]; switchLabel.alpha = [MDCTypography captionFontOpacity]; [switchLabel sizeToFit]; From a6a09c75afa36f0effa0f6342f0a3cff9bf3c736 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Mon, 18 Apr 2016 15:57:49 -0400 Subject: [PATCH 040/129] [Catalog] Remove the bar. Summary: These files are no longer being used. Reviewers: #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D654 --- catalog/MDCCatalog.xcodeproj/project.pbxproj | 8 -- catalog/MDCCatalog/MDCCatalogBar.swift | 87 ------------------- .../MDCCatalogBarViewController.swift | 70 --------------- 3 files changed, 165 deletions(-) delete mode 100644 catalog/MDCCatalog/MDCCatalogBar.swift delete mode 100644 catalog/MDCCatalog/MDCCatalogBarViewController.swift diff --git a/catalog/MDCCatalog.xcodeproj/project.pbxproj b/catalog/MDCCatalog.xcodeproj/project.pbxproj index e9e17669414..25a65289b5e 100644 --- a/catalog/MDCCatalog.xcodeproj/project.pbxproj +++ b/catalog/MDCCatalog.xcodeproj/project.pbxproj @@ -15,7 +15,6 @@ 664524BE1C6BA62A001ADBF8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664524BD1C6BA62A001ADBF8 /* Assets.xcassets */; }; 664524C11C6BA62A001ADBF8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 664524BF1C6BA62A001ADBF8 /* LaunchScreen.storyboard */; }; 666CA70D1CAEBCA9001B1884 /* CBCNodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 666CA70A1CAEBCA9001B1884 /* CBCNodeViewController.m */; }; - DE0B35471CAC16F2002C7357 /* MDCCatalogBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE0B35461CAC16F2002C7357 /* MDCCatalogBarViewController.swift */; }; DE1944861CBD9E40009E0321 /* MDCCatalogTileData.m in Sources */ = {isa = PBXBuildFile; fileRef = DE1944681CBD9E40009E0321 /* MDCCatalogTileData.m */; }; DE1944871CBD9E40009E0321 /* MDCCatalogTileDataAppBar.m in Sources */ = {isa = PBXBuildFile; fileRef = DE19446A1CBD9E40009E0321 /* MDCCatalogTileDataAppBar.m */; }; DE1944881CBD9E40009E0321 /* MDCCatalogTileDataButtonBar.m in Sources */ = {isa = PBXBuildFile; fileRef = DE19446C1CBD9E40009E0321 /* MDCCatalogTileDataButtonBar.m */; }; @@ -33,7 +32,6 @@ DE1944941CBD9E40009E0321 /* MDCCatalogTileDataTypography.m in Sources */ = {isa = PBXBuildFile; fileRef = DE1944841CBD9E40009E0321 /* MDCCatalogTileDataTypography.m */; }; DE309CF31C8DEB8400E73247 /* MDCCatalogComponentsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */; }; DE5BD5AE1CB3F7F100D8D75D /* MDCCatalogTile.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE5BD5AD1CB3F7F100D8D75D /* MDCCatalogTile.swift */; }; - DEEC11E51CA9AC3A00A15FC8 /* MDCCatalogBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEEC11E41CA9AC3A00A15FC8 /* MDCCatalogBar.swift */; }; DEF64EA41C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF64EA31C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift */; }; /* End PBXBuildFile section */ @@ -98,7 +96,6 @@ 878DC2619FAA226EA1A1849A /* libPods-MDCCatalog-MDCCatalog.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MDCCatalog-MDCCatalog.a"; sourceTree = BUILT_PRODUCTS_DIR; }; A2985B007C0718CB5E2511B3 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; BFBFBC9BA56EED442714E5F3 /* Pods-MDCCatalog.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MDCCatalog.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MDCCatalog/Pods-MDCCatalog.debug.xcconfig"; sourceTree = ""; }; - DE0B35461CAC16F2002C7357 /* MDCCatalogBarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogBarViewController.swift; sourceTree = ""; }; DE1944671CBD9E40009E0321 /* MDCCatalogTileData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDCCatalogTileData.h; sourceTree = ""; }; DE1944681CBD9E40009E0321 /* MDCCatalogTileData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MDCCatalogTileData.m; sourceTree = ""; }; DE1944691CBD9E40009E0321 /* MDCCatalogTileDataAppBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDCCatalogTileDataAppBar.h; sourceTree = ""; }; @@ -132,7 +129,6 @@ DE1944851CBD9E40009E0321 /* MDCCatalogTiles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDCCatalogTiles.h; sourceTree = ""; }; DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogComponentsController.swift; sourceTree = ""; }; DE5BD5AD1CB3F7F100D8D75D /* MDCCatalogTile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogTile.swift; sourceTree = ""; }; - DEEC11E41CA9AC3A00A15FC8 /* MDCCatalogBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogBar.swift; sourceTree = ""; }; DEF64EA31C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogCollectionViewCell.swift; sourceTree = ""; }; DEFF1C057348FFB1866CD023 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; FBDDFF6A49F084B665BA398A /* Pods-MDCCatalog-MDCCatalog.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MDCCatalog-MDCCatalog.release.xcconfig"; path = "Pods/Target Support Files/Pods-MDCCatalog-MDCCatalog/Pods-MDCCatalog-MDCCatalog.release.xcconfig"; sourceTree = ""; }; @@ -191,8 +187,6 @@ children = ( 664524B61C6BA62A001ADBF8 /* AppDelegate.swift */, 665A34D91C6BD01900962055 /* MDCCatalog-Bridging-Header.h */, - DEEC11E41CA9AC3A00A15FC8 /* MDCCatalogBar.swift */, - DE0B35461CAC16F2002C7357 /* MDCCatalogBarViewController.swift */, DEF64EA31C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift */, DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */, DE5BD5AD1CB3F7F100D8D75D /* MDCCatalogTile.swift */, @@ -457,7 +451,6 @@ DE19448F1CBD9E40009E0321 /* MDCCatalogTileDataPageControl.m in Sources */, DE1944931CBD9E40009E0321 /* MDCCatalogTileDataSwitch.m in Sources */, DE1944871CBD9E40009E0321 /* MDCCatalogTileDataAppBar.m in Sources */, - DEEC11E51CA9AC3A00A15FC8 /* MDCCatalogBar.swift in Sources */, DE309CF31C8DEB8400E73247 /* MDCCatalogComponentsController.swift in Sources */, DE19448C1CBD9E40009E0321 /* MDCCatalogTileDataInk.m in Sources */, 664524B91C6BA62A001ADBF8 /* MDCNodeListViewController.swift in Sources */, @@ -466,7 +459,6 @@ DE5BD5AE1CB3F7F100D8D75D /* MDCCatalogTile.swift in Sources */, DE1944881CBD9E40009E0321 /* MDCCatalogTileDataButtonBar.m in Sources */, DE19448A1CBD9E40009E0321 /* MDCCatalogTileDataFlexibleHeader.m in Sources */, - DE0B35471CAC16F2002C7357 /* MDCCatalogBarViewController.swift in Sources */, DE1944921CBD9E40009E0321 /* MDCCatalogTileDataSpritedAnimationView.m in Sources */, DE1944891CBD9E40009E0321 /* MDCCatalogTileDataButtons.m in Sources */, 664524B71C6BA62A001ADBF8 /* AppDelegate.swift in Sources */, diff --git a/catalog/MDCCatalog/MDCCatalogBar.swift b/catalog/MDCCatalog/MDCCatalogBar.swift deleted file mode 100644 index 8c776fd1b53..00000000000 --- a/catalog/MDCCatalog/MDCCatalogBar.swift +++ /dev/null @@ -1,87 +0,0 @@ -/* - 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 UIKit -import MaterialComponents - -protocol MDCCatalogBarDelegate { - func didPressExit() -} - -class MDCCatalogBar: UIView { - - var delegate: MDCCatalogBarDelegate? - var titleString = "Component" - let descriptionLabel = UILabel() - let exitLabel = UILabel() - - internal var title: String { - get { - return titleString - } - set { - titleString = newValue - descriptionLabel.text = titleString - } - } - - override init(frame: CGRect) { - super.init(frame: frame) - commonMDCCatalogBarInit() - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - override func layoutSubviews() { - descriptionLabel.frame = CGRect( - x: 20, - y: 0, - width: self.frame.size.width - 120, - height: self.frame.size.height - ) - exitLabel.frame = CGRect( - x: self.frame.size.width - 100, - y: 0, - width: 80, - height: self.frame.size.height - ) - } - - func commonMDCCatalogBarInit() { - self.backgroundColor = UIColor(white: 0.2, alpha: 1) - descriptionLabel.text = title - descriptionLabel.textColor = UIColor.whiteColor() - descriptionLabel.font = MDCTypography.body1Font() - addSubview(descriptionLabel) - - let blueColor = UIColor(red:0.012, green:0.663, blue:0.957, alpha:1) - exitLabel.text = "Exit Demo".uppercaseString - exitLabel.textColor = blueColor - exitLabel.font = MDCTypography.buttonFont() - exitLabel.textAlignment = .Right - addSubview(exitLabel) - - let tap = UITapGestureRecognizer(target: self, action: "exitPressed") - addGestureRecognizer(tap) - } - - func exitPressed() { - delegate?.didPressExit() - } - -} diff --git a/catalog/MDCCatalog/MDCCatalogBarViewController.swift b/catalog/MDCCatalog/MDCCatalogBarViewController.swift deleted file mode 100644 index cf18a5d989b..00000000000 --- a/catalog/MDCCatalog/MDCCatalogBarViewController.swift +++ /dev/null @@ -1,70 +0,0 @@ -/* -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 UIKit -import MaterialComponents - -class MDCCatalogBarViewController: UIViewController { - - internal let catalogBar = MDCCatalogBar(frame: CGRect()) - var contentViewController = UIViewController() - - init(contentViewController: UIViewController, title: String, delegate: MDCCatalogBarDelegate) { - super.init(nibName: nil, bundle: nil) - - self.contentViewController = contentViewController - self.addChildViewController(contentViewController) - - catalogBar.title = title - catalogBar.delegate = delegate - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - override func viewDidLoad() { - self.view.addSubview(contentViewController.view) - self.view.addSubview(catalogBar) - contentViewController.didMoveToParentViewController(self) - - let catalogBarHeight = CGFloat(52) - let catalogBarRect = CGRect( - x: 0, - y: self.view.frame.size.height - catalogBarHeight, - width: self.view.frame.size.width, - height: catalogBarHeight - ) - catalogBar.frame = catalogBarRect - catalogBar.autoresizingMask = [.FlexibleTopMargin, .FlexibleWidth] - - contentViewController.view.frame = CGRect( - x: 0, - y: 0, - width: self.view.bounds.size.width, - height: self.view.bounds.size.height - catalogBarHeight - ) - contentViewController.view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] - } - - override func childViewControllerForStatusBarHidden() -> UIViewController? { - return contentViewController - } - - override func childViewControllerForStatusBarStyle() -> UIViewController? { - return contentViewController - } -} From 03e0cdb05742eca86b0c2f2ee678d99617827cbe Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Mon, 18 Apr 2016 15:12:08 -0400 Subject: [PATCH 041/129] [HeaderStackView] Rename mdc_theme.png to header_stack_view_theme.png. Summary: This is necessary because our example resources are contained in a "global" scope (the catalog), so all resource names must be unique. Closes https://github.com/google/material-components-ios/issues/373. Reviewers: #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D648 --- ..._theme.png => header_stack_view_theme.png} | Bin .../HeaderStackViewTypicalUseSupplemental.m | 24 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) rename components/HeaderStackView/examples/resources/{mdc_theme.png => header_stack_view_theme.png} (100%) diff --git a/components/HeaderStackView/examples/resources/mdc_theme.png b/components/HeaderStackView/examples/resources/header_stack_view_theme.png similarity index 100% rename from components/HeaderStackView/examples/resources/mdc_theme.png rename to components/HeaderStackView/examples/resources/header_stack_view_theme.png diff --git a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m index 800df2f297b..197463a6766 100644 --- a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m +++ b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m @@ -21,8 +21,8 @@ #import "HeaderStackViewTypicalUseSupplemental.h" -#import "MaterialNavigationBar.h" #import "MaterialHeaderStackView.h" +#import "MaterialNavigationBar.h" @interface ExampleInstructionsViewHeaderStackViewTypicalUse : UIView @@ -81,7 +81,7 @@ - (void)didTapToggleButton:(id)sender { - (UIImage *)headerBackgroundImage { NSBundle *bundle = [NSBundle bundleForClass:[self class]]; - NSString *imagePath = [bundle pathForResource:@"mdc_theme" ofType:@"png"]; + NSString *imagePath = [bundle pathForResource:@"header_stack_view_theme" ofType:@"png"]; return [UIImage imageWithContentsOfFile:imagePath]; } @@ -150,19 +150,19 @@ - (NSAttributedString *)instructionsString { NSDictionary *instructionAttributes1 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSDictionary *instructionAttributes2 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSString *instructionText = @"SWIPE RIGHT\n\n\n\nto go back\n\n\n\n\n\n"; NSMutableAttributedString *instructionsAttributedString = [[NSMutableAttributedString alloc] From 618b49ecb9f692ecbe6709b80a993c142bc4128c Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Mon, 18 Apr 2016 15:53:19 -0400 Subject: [PATCH 042/129] [Catalog] Rename MDCCatalogTile to MDCCatalogTileView. Reviewers: #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D652 --- catalog/MDCCatalog.xcodeproj/project.pbxproj | 8 ++++---- catalog/MDCCatalog/MDCCatalogCollectionViewCell.swift | 2 +- .../{MDCCatalogTile.swift => MDCCatalogTileView.swift} | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) rename catalog/MDCCatalog/{MDCCatalogTile.swift => MDCCatalogTileView.swift} (99%) diff --git a/catalog/MDCCatalog.xcodeproj/project.pbxproj b/catalog/MDCCatalog.xcodeproj/project.pbxproj index 25a65289b5e..5823e08e3cf 100644 --- a/catalog/MDCCatalog.xcodeproj/project.pbxproj +++ b/catalog/MDCCatalog.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 664524BE1C6BA62A001ADBF8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664524BD1C6BA62A001ADBF8 /* Assets.xcassets */; }; 664524C11C6BA62A001ADBF8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 664524BF1C6BA62A001ADBF8 /* LaunchScreen.storyboard */; }; 666CA70D1CAEBCA9001B1884 /* CBCNodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 666CA70A1CAEBCA9001B1884 /* CBCNodeViewController.m */; }; + 6681FDFD1CC586660013A0C7 /* MDCCatalogTileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6681FDFC1CC586660013A0C7 /* MDCCatalogTileView.swift */; }; DE1944861CBD9E40009E0321 /* MDCCatalogTileData.m in Sources */ = {isa = PBXBuildFile; fileRef = DE1944681CBD9E40009E0321 /* MDCCatalogTileData.m */; }; DE1944871CBD9E40009E0321 /* MDCCatalogTileDataAppBar.m in Sources */ = {isa = PBXBuildFile; fileRef = DE19446A1CBD9E40009E0321 /* MDCCatalogTileDataAppBar.m */; }; DE1944881CBD9E40009E0321 /* MDCCatalogTileDataButtonBar.m in Sources */ = {isa = PBXBuildFile; fileRef = DE19446C1CBD9E40009E0321 /* MDCCatalogTileDataButtonBar.m */; }; @@ -31,7 +32,6 @@ DE1944931CBD9E40009E0321 /* MDCCatalogTileDataSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = DE1944821CBD9E40009E0321 /* MDCCatalogTileDataSwitch.m */; }; DE1944941CBD9E40009E0321 /* MDCCatalogTileDataTypography.m in Sources */ = {isa = PBXBuildFile; fileRef = DE1944841CBD9E40009E0321 /* MDCCatalogTileDataTypography.m */; }; DE309CF31C8DEB8400E73247 /* MDCCatalogComponentsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */; }; - DE5BD5AE1CB3F7F100D8D75D /* MDCCatalogTile.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE5BD5AD1CB3F7F100D8D75D /* MDCCatalogTile.swift */; }; DEF64EA41C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF64EA31C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift */; }; /* End PBXBuildFile section */ @@ -91,6 +91,7 @@ 666CA7081CAEBCA9001B1884 /* CBCCatalogExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBCCatalogExample.h; sourceTree = ""; }; 666CA7091CAEBCA9001B1884 /* CBCNodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBCNodeViewController.h; sourceTree = ""; }; 666CA70A1CAEBCA9001B1884 /* CBCNodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CBCNodeViewController.m; sourceTree = ""; }; + 6681FDFC1CC586660013A0C7 /* MDCCatalogTileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogTileView.swift; sourceTree = ""; }; 7385A3AD61219974D1545BD4 /* Pods-MDCCatalogUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MDCCatalogUnitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MDCCatalogUnitTests/Pods-MDCCatalogUnitTests.debug.xcconfig"; sourceTree = ""; }; 8206EEC8F0212B48C4AC4C01 /* libPods-MDCCatalogUnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MDCCatalogUnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 878DC2619FAA226EA1A1849A /* libPods-MDCCatalog-MDCCatalog.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MDCCatalog-MDCCatalog.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -128,7 +129,6 @@ DE1944841CBD9E40009E0321 /* MDCCatalogTileDataTypography.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MDCCatalogTileDataTypography.m; sourceTree = ""; }; DE1944851CBD9E40009E0321 /* MDCCatalogTiles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDCCatalogTiles.h; sourceTree = ""; }; DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogComponentsController.swift; sourceTree = ""; }; - DE5BD5AD1CB3F7F100D8D75D /* MDCCatalogTile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogTile.swift; sourceTree = ""; }; DEF64EA31C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogCollectionViewCell.swift; sourceTree = ""; }; DEFF1C057348FFB1866CD023 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; FBDDFF6A49F084B665BA398A /* Pods-MDCCatalog-MDCCatalog.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MDCCatalog-MDCCatalog.release.xcconfig"; path = "Pods/Target Support Files/Pods-MDCCatalog-MDCCatalog/Pods-MDCCatalog-MDCCatalog.release.xcconfig"; sourceTree = ""; }; @@ -189,7 +189,7 @@ 665A34D91C6BD01900962055 /* MDCCatalog-Bridging-Header.h */, DEF64EA31C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift */, DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */, - DE5BD5AD1CB3F7F100D8D75D /* MDCCatalogTile.swift */, + 6681FDFC1CC586660013A0C7 /* MDCCatalogTileView.swift */, 664524B81C6BA62A001ADBF8 /* MDCNodeListViewController.swift */, DE5BD5AF1CB3F8FF00D8D75D /* MDCCatalogTiles */, 665A34DE1C6BDAE700962055 /* Resources */, @@ -452,11 +452,11 @@ DE1944931CBD9E40009E0321 /* MDCCatalogTileDataSwitch.m in Sources */, DE1944871CBD9E40009E0321 /* MDCCatalogTileDataAppBar.m in Sources */, DE309CF31C8DEB8400E73247 /* MDCCatalogComponentsController.swift in Sources */, + 6681FDFD1CC586660013A0C7 /* MDCCatalogTileView.swift in Sources */, DE19448C1CBD9E40009E0321 /* MDCCatalogTileDataInk.m in Sources */, 664524B91C6BA62A001ADBF8 /* MDCNodeListViewController.swift in Sources */, DE19448D1CBD9E40009E0321 /* MDCCatalogTileDataMisc.m in Sources */, DE19448B1CBD9E40009E0321 /* MDCCatalogTileDataHeaderStackView.m in Sources */, - DE5BD5AE1CB3F7F100D8D75D /* MDCCatalogTile.swift in Sources */, DE1944881CBD9E40009E0321 /* MDCCatalogTileDataButtonBar.m in Sources */, DE19448A1CBD9E40009E0321 /* MDCCatalogTileDataFlexibleHeader.m in Sources */, DE1944921CBD9E40009E0321 /* MDCCatalogTileDataSpritedAnimationView.m in Sources */, diff --git a/catalog/MDCCatalog/MDCCatalogCollectionViewCell.swift b/catalog/MDCCatalog/MDCCatalogCollectionViewCell.swift index e7d5dfe95d8..7172f9c342e 100644 --- a/catalog/MDCCatalog/MDCCatalogCollectionViewCell.swift +++ b/catalog/MDCCatalog/MDCCatalogCollectionViewCell.swift @@ -21,7 +21,7 @@ class MDCCatalogCollectionViewCell: UICollectionViewCell { var label = UILabel() let pad = CGFloat(14) - let tile = MDCCatalogTile(frame: CGRectZero) + let tile = MDCCatalogTileView(frame: CGRectZero) override init(frame: CGRect) { super.init(frame: frame) diff --git a/catalog/MDCCatalog/MDCCatalogTile.swift b/catalog/MDCCatalog/MDCCatalogTileView.swift similarity index 99% rename from catalog/MDCCatalog/MDCCatalogTile.swift rename to catalog/MDCCatalog/MDCCatalogTileView.swift index 25e371a9844..f0a71579c2c 100644 --- a/catalog/MDCCatalog/MDCCatalogTile.swift +++ b/catalog/MDCCatalog/MDCCatalogTileView.swift @@ -17,7 +17,7 @@ limitations under the License. import UIKit import MaterialComponents -class MDCCatalogTile: UIView { +class MDCCatalogTileView: UIView { private var componentNameString = "Misc" var componentName:String { From 7eca04464315372b3c4fcce161c0a0f9ee2c4624 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Mon, 18 Apr 2016 15:56:09 -0400 Subject: [PATCH 043/129] [Catalog] Forward touch events to the home screen's header. Summary: Closes https://github.com/google/material-components-ios/issues/363. Reviewers: #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D653 --- catalog/MDCCatalog/MDCCatalogComponentsController.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/catalog/MDCCatalog/MDCCatalogComponentsController.swift b/catalog/MDCCatalog/MDCCatalogComponentsController.swift index e1306a3512e..d2ae78bce32 100644 --- a/catalog/MDCCatalog/MDCCatalogComponentsController.swift +++ b/catalog/MDCCatalog/MDCCatalogComponentsController.swift @@ -86,6 +86,8 @@ class MDCCatalogComponentsController: UICollectionViewController { self.headerViewController.headerView.addSubview(containerView) + self.headerViewController.headerView.forwardTouchEventsForView(containerView) + self.headerViewController.headerView.backgroundColor = UIColor.whiteColor() self.headerViewController.headerView.trackingScrollView = self.collectionView From 1a7d800cc9a6b29320bf57d61806a85cde387f15 Mon Sep 17 00:00:00 2001 From: randallli Date: Mon, 18 Apr 2016 17:55:51 -0400 Subject: [PATCH 044/129] [Switch] Added swift example to catalog and readme. Summary: work on https://github.com/google/material-components-ios/issues/318 Reviewers: ajsecord, #mdc_ios_owners Reviewed By: ajsecord, #mdc_ios_owners Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D655 --- components/Switch/README.md | 18 +++++++++ .../SwitchSwiftExampleViewController.swift | 39 +++++++++++++++++++ components/Switch/examples/SwitchTypicalUse.m | 11 +++--- ...iftExampleViewControllerSupplemental.swift | 17 ++++++++ .../SwitchTypicalUseSupplemental.m | 5 ++- 5 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 components/Switch/examples/SwitchSwiftExampleViewController.swift create mode 100644 components/Switch/examples/supplemental/SwitchSwiftExampleViewControllerSupplemental.swift diff --git a/components/Switch/README.md b/components/Switch/README.md index 99e5ff0fb4c..88de65a1d4e 100644 --- a/components/Switch/README.md +++ b/components/Switch/README.md @@ -75,6 +75,7 @@ import MaterialComponents ~~~ +### Setup ### Objective C @@ -95,5 +96,22 @@ import MaterialComponents ... } +~~~ +#### Swift +~~~ swift + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = UIColor.whiteColor() + + switchComponent.on = true + switchComponent.addTarget(self, action: Selector("didChangeSwitchValue:"), forControlEvents: UIControlEvents.ValueChanged) + view.addSubview(switchComponent) + switchComponent.center = CGPointMake(50, 50); + } + + func didChangeSwitchValue(senderSwitch:MDCSwitch) { + NSLog("did change value: %@", senderSwitch.on); + } + ~~~ diff --git a/components/Switch/examples/SwitchSwiftExampleViewController.swift b/components/Switch/examples/SwitchSwiftExampleViewController.swift new file mode 100644 index 00000000000..e3b8fc8f258 --- /dev/null +++ b/components/Switch/examples/SwitchSwiftExampleViewController.swift @@ -0,0 +1,39 @@ +/* + Copyright 2015-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 Foundation + +import MaterialComponents + +class SwitchSwiftExampleViewController : UIViewController { + + let switchComponent = MDCSwitch() + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = UIColor.whiteColor() + + switchComponent.on = true + switchComponent.addTarget(self, action: Selector("didChangeSwitchValue:"), forControlEvents: UIControlEvents.ValueChanged) + view.addSubview(switchComponent) + switchComponent.center = CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds)); + switchComponent.autoresizingMask = [.FlexibleBottomMargin, .FlexibleTopMargin, .FlexibleLeftMargin, .FlexibleRightMargin] + } + + func didChangeSwitchValue(senderSwitch:MDCSwitch) { + NSLog("Did change switch value to: %@.", senderSwitch.on); + } +} diff --git a/components/Switch/examples/SwitchTypicalUse.m b/components/Switch/examples/SwitchTypicalUse.m index 80ec9a491e1..edf9d07b887 100644 --- a/components/Switch/examples/SwitchTypicalUse.m +++ b/components/Switch/examples/SwitchTypicalUse.m @@ -31,7 +31,7 @@ - (void)viewDidLoad { self.switchComponent = [[MDCSwitch alloc] init]; [self.switchComponent setOn:YES]; [self.switchComponent addTarget:self - action:@selector(didChangeSliderValue:) + action:@selector(didChangeSwitchValue:) forControlEvents:UIControlEventValueChanged]; [self.view addSubview:self.switchComponent]; @@ -41,7 +41,7 @@ - (void)viewDidLoad { self.colorSwitchComponent.onTintColor = [UIColor colorWithRed:0 green:0.47f blue:0.9f alpha:1]; [self.colorSwitchComponent setOn:YES]; [self.colorSwitchComponent addTarget:self - action:@selector(didChangeSliderValue:) + action:@selector(didChangeSwitchValue:) forControlEvents:UIControlEventValueChanged]; [self.view addSubview:self.colorSwitchComponent]; @@ -49,7 +49,7 @@ - (void)viewDidLoad { self.disabledSwitchComponent = [[MDCSwitch alloc] init]; [self.disabledSwitchComponent addTarget:self - action:@selector(didChangeSliderValue:) + action:@selector(didChangeSwitchValue:) forControlEvents:UIControlEventValueChanged]; self.disabledSwitchComponent.enabled = NO; [self.view addSubview:self.disabledSwitchComponent]; @@ -61,9 +61,8 @@ - (void)viewDidLoad { [self setupExampleViews]; } -- (void)didChangeSliderValue:(id)sender { - UISwitch *slider = sender; - NSLog(@"did change %@ value: %d", NSStringFromClass([sender class]), slider.isOn); +- (void)didChangeSwitchValue:(MDCSwitch *)sender { + NSLog(@"did change %@ value: %d", NSStringFromClass([sender class]), sender.isOn); } @end diff --git a/components/Switch/examples/supplemental/SwitchSwiftExampleViewControllerSupplemental.swift b/components/Switch/examples/supplemental/SwitchSwiftExampleViewControllerSupplemental.swift new file mode 100644 index 00000000000..023df401eac --- /dev/null +++ b/components/Switch/examples/supplemental/SwitchSwiftExampleViewControllerSupplemental.swift @@ -0,0 +1,17 @@ +/* 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 Foundation +import MaterialComponents + +extension SwitchSwiftExampleViewController { + + // (CatalogByConvention) + + class func catalogBreadcrumbs() -> [String] { + return [ "Switch", "Swift example"] + } +} diff --git a/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m b/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m index 4af671ce2a1..f1c1c219039 100644 --- a/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m +++ b/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m @@ -5,15 +5,16 @@ #import -#import "SwitchTypicalUseSupplemental.h" +#import "MaterialSwitch.h" #import "MaterialTypography.h" +#import "SwitchTypicalUseSupplemental.h" #pragma mark - SwitchTypicalUseViewController @implementation SwitchTypicalUseViewController (CatalogByConvention) + (NSArray *)catalogBreadcrumbs { - return @[ @"Switch", @"Switch" ]; + return @[ @"Switch", @"Typical use" ]; } + (NSString *)catalogDescription { From 62f57e2b290155d5bfdfafb3f27839eb8e9c1eb5 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Tue, 19 Apr 2016 09:26:02 -0400 Subject: [PATCH 045/129] [Catalog and Typography] Group Typography and Font Loader examples into Typography and Fonts Reviewers: featherless, ajsecord, #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D636 --- catalog/MDCCatalog/MDCCatalogTileView.swift | 2 +- .../examples/FontDiskLoaderSimipleExample.m | 2 +- .../RobotoFontLoaderSimpleExampleViewController.m | 2 +- .../examples/RobotoVsSystemExampleViewController.m | 10 +--------- .../examples/TypographyExamplesViewController.swift | 2 +- .../examples/TypographySimpleExampleViewController.m | 2 +- .../TypographySystemFontLoaderExampleViewController.m | 2 +- 7 files changed, 7 insertions(+), 15 deletions(-) diff --git a/catalog/MDCCatalog/MDCCatalogTileView.swift b/catalog/MDCCatalog/MDCCatalogTileView.swift index f0a71579c2c..eb9705cd164 100644 --- a/catalog/MDCCatalog/MDCCatalogTileView.swift +++ b/catalog/MDCCatalog/MDCCatalogTileView.swift @@ -98,7 +98,7 @@ class MDCCatalogTileView: UIView { newImage = MDCCatalogTileDataSpritedAnimationView.drawTileImage(centeredFrame) case "Switch": newImage = MDCCatalogTileDataSwitch.drawTileImage(centeredFrame) - case "Typography": + case "Typography and Fonts": newImage = MDCCatalogTileDataTypography.drawTileImage(centeredFrame) default: newImage = MDCCatalogTileDataMisc.drawTileImage(centeredFrame) diff --git a/components/FontDiskLoader/examples/FontDiskLoaderSimipleExample.m b/components/FontDiskLoader/examples/FontDiskLoaderSimipleExample.m index 588dd6970d5..7cf6e0b696c 100644 --- a/components/FontDiskLoader/examples/FontDiskLoaderSimipleExample.m +++ b/components/FontDiskLoader/examples/FontDiskLoaderSimipleExample.m @@ -27,7 +27,7 @@ @implementation FontDiskLoaderSimpleExample // TODO: Support other categorizational methods. + (NSArray *)catalogBreadcrumbs { - return @[ @"Roboto Font Loader", @"Font Disk Loader" ]; + return @[ @"Typography and Fonts", @"Font Disk Loader" ]; } - (void)viewDidLoad { diff --git a/components/RobotoFontLoader/examples/RobotoFontLoaderSimpleExampleViewController.m b/components/RobotoFontLoader/examples/RobotoFontLoaderSimpleExampleViewController.m index b0f42abb436..50d8d649957 100644 --- a/components/RobotoFontLoader/examples/RobotoFontLoaderSimpleExampleViewController.m +++ b/components/RobotoFontLoader/examples/RobotoFontLoaderSimpleExampleViewController.m @@ -40,7 +40,7 @@ - (void)viewDidLoad { } + (NSArray *)catalogBreadcrumbs { - return @[ @"Roboto Font Loader", @"Roboto Font Loader" ]; + return @[ @"Typography and Fonts", @"Roboto Font Loader" ]; } @end diff --git a/components/RobotoFontLoader/examples/RobotoVsSystemExampleViewController.m b/components/RobotoFontLoader/examples/RobotoVsSystemExampleViewController.m index d556848d158..43610ac5e49 100644 --- a/components/RobotoFontLoader/examples/RobotoVsSystemExampleViewController.m +++ b/components/RobotoFontLoader/examples/RobotoVsSystemExampleViewController.m @@ -34,19 +34,11 @@ - (void)viewDidLoad { } + (NSArray *)catalogBreadcrumbs { - return @[ @"Roboto Font Loader", @"Roboto Font Loader" ]; + return @[ @"Typography and Fonts", @"Roboto Font Loader" ]; } + (NSString *)catalogStoryboardName { return @"RobotoVsSystem"; } -+ (NSString *)catalogDescription { - return @"The Roboto Font Loader lazy loads the Roboto font."; -} - -+ (BOOL)catalogIsPrimaryDemo { - return YES; -} - @end diff --git a/components/Typography/examples/TypographyExamplesViewController.swift b/components/Typography/examples/TypographyExamplesViewController.swift index 1dca4fdb22a..5ea973ac4cb 100644 --- a/components/Typography/examples/TypographyExamplesViewController.swift +++ b/components/Typography/examples/TypographyExamplesViewController.swift @@ -203,7 +203,7 @@ class TypographyExamplesViewController: UICollectionViewController { } class func catalogBreadcrumbs() -> [String] { - return ["Typography", "Typography"] + return ["Typography and Fonts", "Typography"] } class func catalogDescription() -> String { diff --git a/components/Typography/examples/TypographySimpleExampleViewController.m b/components/Typography/examples/TypographySimpleExampleViewController.m index 16cf75353b4..be58755cb0c 100644 --- a/components/Typography/examples/TypographySimpleExampleViewController.m +++ b/components/Typography/examples/TypographySimpleExampleViewController.m @@ -35,7 +35,7 @@ - (void)viewDidLoad { } + (NSArray *)catalogBreadcrumbs { - return @[ @"Typography", @"Read Me Demo" ]; + return @[ @"Typography and Fonts", @"Read Me Demo" ]; } @end diff --git a/components/Typography/examples/TypographySystemFontLoaderExampleViewController.m b/components/Typography/examples/TypographySystemFontLoaderExampleViewController.m index a013e2903d4..9834d10128e 100644 --- a/components/Typography/examples/TypographySystemFontLoaderExampleViewController.m +++ b/components/Typography/examples/TypographySystemFontLoaderExampleViewController.m @@ -58,7 +58,7 @@ - (IBAction)didChangeSwitchValue:(id)sender { } + (NSArray *)catalogBreadcrumbs { - return @[ @"Typography", @"Set Font Loader" ]; + return @[ @"Typography and Fonts", @"Set Font Loader" ]; } + (NSString *)catalogStoryboardName { From b8dc78d58c06c45a8616e6798839b37fd065fc05 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Tue, 19 Apr 2016 09:26:26 -0400 Subject: [PATCH 046/129] [Catalog and Shadow] Group shadow elevations with shadow demos Reviewers: featherless, ajsecord, #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D637 --- catalog/MDCCatalog/MDCCatalogTileView.swift | 2 +- .../examples/ShadowElevationsTypicalUseExample.m | 12 ++---------- .../ShadowDragSquareExampleViewController.swift | 2 +- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/catalog/MDCCatalog/MDCCatalogTileView.swift b/catalog/MDCCatalog/MDCCatalogTileView.swift index eb9705cd164..3ddb5e9a582 100644 --- a/catalog/MDCCatalog/MDCCatalogTileView.swift +++ b/catalog/MDCCatalog/MDCCatalogTileView.swift @@ -90,7 +90,7 @@ class MDCCatalogTileView: UIView { newImage = MDCCatalogTileDataMisc.drawTileImage(centeredFrame) case "Page Control": newImage = MDCCatalogTileDataPageControl.drawTileImage(centeredFrame) - case "Shadow Layer": + case "Shadow": newImage = MDCCatalogTileDataShadowLayer.drawTileImage(centeredFrame) case "Slider": newImage = MDCCatalogTileDataSlider.drawTileImage(centeredFrame) diff --git a/components/ShadowElevations/examples/ShadowElevationsTypicalUseExample.m b/components/ShadowElevations/examples/ShadowElevationsTypicalUseExample.m index d724caf228b..ca30bd318db 100644 --- a/components/ShadowElevations/examples/ShadowElevationsTypicalUseExample.m +++ b/components/ShadowElevations/examples/ShadowElevationsTypicalUseExample.m @@ -130,7 +130,7 @@ @implementation ShadowElevationsTypicalUseViewController - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; - self.title = @"Shadows (Points)"; + self.title = @"Shadow Elevations"; _shadowsView = [[ShadowElevationsPointsView alloc] initWithFrame:self.view.bounds]; _shadowsView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; @@ -140,15 +140,7 @@ - (void)viewDidLoad { #pragma mark catalog by convention + (NSArray *)catalogBreadcrumbs { - return @[ @"Shadow Elevations", @"Shadow Elevations" ]; -} - -+ (NSString *)catalogDescription { - return @"This component provides the most commonly-used Material Design elevations."; -} - -+ (BOOL)catalogIsPrimaryDemo { - return YES; + return @[ @"Shadow", @"Shadow Elevations" ]; } @end diff --git a/components/ShadowLayer/examples/ShadowDragSquareExampleViewController.swift b/components/ShadowLayer/examples/ShadowDragSquareExampleViewController.swift index bb1a3f340d5..99754de202e 100644 --- a/components/ShadowLayer/examples/ShadowDragSquareExampleViewController.swift +++ b/components/ShadowLayer/examples/ShadowDragSquareExampleViewController.swift @@ -72,7 +72,7 @@ class ShadowDragSquareExampleViewController: UIViewController { // MARK: catalog by convention class func catalogBreadcrumbs() -> Array { - return [ "Shadow Layer", "Shadow Layer"] + return [ "Shadow", "Shadow Layer"] } class func catalogStoryboardName() -> String { From 13bb72bed147c249d9db1e8a31e3f7d9a62ecc46 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Mon, 18 Apr 2016 17:33:52 -0400 Subject: [PATCH 047/129] [Buttons] Resolve deprecation warnings. Summary: Closes https://github.com/google/material-components-ios/issues/360. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Subscribers: randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D656 --- components/Buttons/src/MDCButton.m | 3 +++ components/Buttons/src/MDCFlatButton.m | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/components/Buttons/src/MDCButton.m b/components/Buttons/src/MDCButton.m index 65c58645cf1..b2c03f0d549 100644 --- a/components/Buttons/src/MDCButton.m +++ b/components/Buttons/src/MDCButton.m @@ -154,7 +154,10 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { } if ([aDecoder containsValueForKey:MDCButtonShouldRaiseOnTouchKey]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" self.shouldRaiseOnTouch = [aDecoder decodeBoolForKey:MDCButtonShouldRaiseOnTouchKey]; +#pragma clang diagnostic pop } if ([aDecoder containsValueForKey:MDCButtonUppercaseTitleKey]) { diff --git a/components/Buttons/src/MDCFlatButton.m b/components/Buttons/src/MDCFlatButton.m index a88059b250e..6c38f4dfa3e 100644 --- a/components/Buttons/src/MDCFlatButton.m +++ b/components/Buttons/src/MDCFlatButton.m @@ -46,8 +46,12 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { } - (void)commonMDCFlatButtonInit { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" self.shouldRaiseOnTouch = NO; - [self setBackgroundColor:nil forState:UIControlStateNormal]; +#pragma clang diagnostic pop + [self setBackgroundColor:nil + forState:UIControlStateNormal]; self.inkColor = [UIColor colorWithWhite:0 alpha:0.06f]; } From 16856a625d7ad85fe6537a8024674a080821e597 Mon Sep 17 00:00:00 2001 From: Adrian Secord Date: Tue, 19 Apr 2016 11:57:09 -0400 Subject: [PATCH 048/129] Created howto/build_env and moved build_configuration.md into it. Reviewers: #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D660 --- howto/README.md | 716 +---------------------------------- howto/build-env/README.md | 32 ++ howto/tutorial/README.md | 716 +++++++++++++++++++++++++++++++++++ usage/build_configuration.md | 19 - 4 files changed, 756 insertions(+), 727 deletions(-) create mode 100644 howto/build-env/README.md create mode 100644 howto/tutorial/README.md delete mode 100644 usage/build_configuration.md diff --git a/howto/README.md b/howto/README.md index 0b9a4b26443..a0e64f2caae 100644 --- a/howto/README.md +++ b/howto/README.md @@ -1,716 +1,16 @@ --- -title: "Material Components Development Guide" +title: "How to use Material Components" layout: landing section: howto --- -# Material Components Development Guide +# How to use Material Components -Material Components for iOS is a set of components that help iOS app developers build Material Design apps. These are the same components Google uses to build apps like Google Maps, Calendar, Chrome and many more. +TODO Intro text -Individually, the components bring Material Design principles to common UI elements and behaviors, but tailored for iOS. Our team has taken care to design the APIs to feel natural on iOS. +- [Development Guide](/howto/tutorial/) + -Our goal is to make implementing Material Design as easy as possible. The components are easy to assemble and be used piecemeal. - -This tutorial will take you through building an example app called Abstractor and show some of the neat features and benefits of using our components to build your app. In order to get through this tutorial, Swift and iOS development knowledge is required. - - -- - - - -## Getting Started - -### Tutorial Setup - -Material Components for iOS can be integrated like any other shared code library on iOS. The preferred method of integration is through CocoaPods. - -To help get started quickly, `git clone` this skeleton new project which the rest of the tutorial will use. - -~~~ bash -git clone https://github.com/google/material-components-ios-example/ -~~~ - -This project is similar to a new project created using Xcode's new project template except with a small number of changes: - -1. Removes the Main.storyboard and references to it in favor of programmatically creating the UI. -2. Adds a bridging header (BridgingHeader.h) and the Xcode configuration for it. -3. Adds a simple String class extension for creating sample text. -4. Adds two icons (search and add) from [Material Icons](https://github.io/google/material-icons) -5. Creates a new MainViewController.swift. - -#### CocoaPods - -The first step is to add Material Components through CocoaPods. The [Material Components quickstart](https://materialcomponents.org/) has detailed instructions, but in short, create a Podfile in the root of the example with the following contents: - -~~~ ruby -target 'Abstractor' do - pod 'MaterialComponents' -end -~~~ - -Run `pod install` in that directory and open up `Abstractor.xcworkspace`. - -#### Bridging for Swift - -Material Components is written in Objective-C and is completely usable from Swift. In order to make the classes visible to Swift, the headers need to be added to the `BridgingHeaders.h`. Open up `BridgingHeaders.h` and add the following lines. - - -~~~ objc -#import "MaterialAppBar.h" -#import "MaterialButtons.h" -#import "MaterialCollections.h" -#import "MaterialFlexibleHeader.h" -~~~ - -#### Building and running the app - -The Abstractor project should be now set up and ready to run. Building and running the project should show you a fairly boring app with a yellow background with no contents. That is our skeleton project the rest of the tutorial will use. - -**TODO: Insert image of the app.** - - -- - - - -## Material Headers - -Headers exist in nearly all apps we see to provide framing and navigation. The header and scrolling behavior is well defined in the [Material Design Guidelines](https://www.google.com/design/spec/TODO) but it is tricky to get right. - -Material Components for iOS provides both a higher level and lower level implementation that allow developers to easily customize the right behavior for the view controller they are building. Both implementations provide a responsive header that can expand and contract in response to scrolling behaviors to maximize the content area or show high level information to the user. - -The [App Bar](https://materialcomponents.org/components/appbar/) is the first way to implement a response header. It uses the familiar UINavigationItem properties of a UIViewController to derive the contents of the header view. - -The [Flexible Header](https://materialcomponents.org/components/flexible-header/) is the second way to implement the responsive header. This component is what the App Bar is built on and is perfect if the developer would like fine grained control over it's contents and behavior. For example, the flexible header can contain a fully custom view that would respond to size changes as the user scrolled. - -If you are used to UINavigationController's UINavigationBar, the fundamental different in design is that your UIViewController has the actual header bar in it's view hierarchy. This contrasts with UINavigationBar being part of UINavigationController's view hierarchy. The different view hierarchy allows gives developers more flexibility when animating between view controllers or customizing unique behaviors when the size of the header changes. - -- - - - -## Starting with App Bar - -In the Abstractor project, there is a view controller already created called MainViewController. To start, we will add a scroll view to the MainViewController since the AppBar works best with a scroll view. - -Modify the MainViewController.swift by adding a UIScrollView. Make the following changes to the MainViewController: - - - -#### Swift - -~~~ swift -class MainViewController : UIViewController { - var scrollView: UIScrollView? - - override func viewDidLoad() { - super.viewDidLoad() - - // Create and initialize a blank scroll view. - scrollView = UIScrollView(frame: view.bounds) - scrollView!.contentSize = CGSize(width: view.bounds.size.width, height: 1000) - scrollView!.backgroundColor = UIColor.whiteColor() - view.addSubview(scrollView!) - } -} -~~~ - - -This snippet is basic UIKit code to create a scroll view, setting the size of the scroll view to be at least 1000 points tall so it will scroll further off screen. - -To actually add the App Bar, we need to give the view controller a protocol to conform to, and override the initializers: - - - -#### Swift - -~~~ swift -class MainViewController : UIViewController, MDCAppBarParenting { - var scrollView: UIScrollView? - - // -- start MDCAppBarParenting - var headerStackView: MDCHeaderStackView? - var navigationBar: MDCNavigationBar? - var headerViewController: MDCFlexibleHeaderViewController? - - override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) - MDCAppBarPrepareParent(self) - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - MDCAppBarPrepareParent(self) - } - // -- end MDCAppBarParenting - - override func viewDidLoad() { - super.viewDidLoad() - - // Create and initialize a blank scroll view. - scrollView = UIScrollView(frame: view.bounds) - scrollView!.contentSize = CGSize(width: view.bounds.size.width, height: 1000) - scrollView!.backgroundColor = UIColor.whiteColor() - view.addSubview(scrollView!) - - // -- start MDCAppBarParenting - MDCAppBarAddViews(self) - // -- end MDCAppBarParenting - } -} - -~~~ - - - -At this point, the app will add a grey header bar at the top of the view, but the scroll view will live under the header bar. You can observe this by scrolling the the view and seeing the scroll indicator go below the grey header bar. - -**TODO: Add image of the grey header** - -The MDCFlexibleHeaderViewController that was now exposed as a property of our view controller doesn't know about the scroll view and therefore it cannot adjust any scroll view insets the scroll view needs to render below the bar. To rectify this, simply tell the headerView in the MDCFlexibleHeaderViewController about the scroll view in viewDidLoad(): - - - -#### Swift - -~~~ swift -class MainViewController : UIViewController, MDCAppBarParenting { - // ... - override func viewDidLoad() { - // ... - - // Connect scroll view with the header view controller. - headerViewController?.headerView.trackingScrollView = contentScrollView - headerViewController?.headerView.behavior = .EnabledWithStatusBar - scrollView.delegate = headerViewController - - } -~~~ - - - -Now the scroll view correctly aligns to the bottom of the header bar. Notice that `headerView` had a property called `behavior` which is set to EnabledWithStatusBar. This behavior controls how the header view reacts to scrolling. When `Enabled`, the header will collapse to maximize the content area. Developers can choose whether the status bar should also be hidden. - -**TODO: Add animation of the grey header collapsing.** - -The status bar is not hiding yet, and the reason is by default UIViewController does not hide the status bar. In order for `headerViewController` to assume control of the status bar, override the method `childViewControllerForStatusBarHidden` to use the headerViewController as the childViewController (see the FlexibleHeader component documentation for more details): - - - -#### Swift - -~~~ swift -class MainViewController : UIViewController, MDCAppBarParenting { - // ... - override func childViewControllerForStatusBarHidden() -> UIViewController { - return headerViewController! - } -} -~~~ - - - -**TODO: Add animation of the status bar correctly collapsing.** - -To complete the integration, let's set a proper color and some items on to the header bar. - -To set the color of the header, we can directly manipulate the headerView in viewDidLoad: - - - -#### Swift - -~~~ swift -class MainViewController : UIViewController, MDCAppBarParenting { - // ... - override func viewDidLoad() { - // ... - - // Set color using UIColor extension in UIColorAbstractor.swift - headerViewController!.headerView.backgroundColor = UIColor.materialOrange700() - headerViewController!.headerView.tintColor = UIColor.whiteColor().colorWithAlphaComponent(0.87) - } - - // Set the status bar to white. - override func preferredStatusBarStyle() -> UIStatusBarStyle { - return .LightContent - } -} -~~~ - - - -And finally put some buttons in to the header bar. - - - -#### Swift - -~~~ swift -class MainViewController : UIViewController, MDCAppBarParenting { - override func viewDidLoad() { - // ... - - // Set up UINavigationItems - navigationItem.title = "Abstractor".blackout() - navigationItem.rightBarButtonItem = UIBarButtonItem( - image: UIImage(named: "ic_search")?.imageWithRenderingMode(.AlwaysTemplate), - style: .Plain, - target: self, - action: #selector(MainViewController.search(_:))) - - // Last step in viewDidLoad - MDCAppBarAddViews(self) - } - - // Implement the callback method for the search button. - func search(target: AnyObject) { - } -} -~~~ - - - -And there you have a responsive header that reacts to the scroll view and collapses to maximize -the content. - -- - - - -## Flexible Header - -One advantage of the App Bar component is it's compatibility with UINavigationItem. If developers would like to customize the actual contents inside the header, they need to look at the powerful Flexible Header component. - -Observant developers would already have noticed that App Bar uses Flexible Header to create the behavior. Imagine instead that we would like to lock the title to the bottom of the header but keep search button attached to the top. - - - -### Create a custom header view - -The first thing to do is to create a custom view that will be placed inside the FlexibleHeader. This can be in conjunction with the App Bar or completed without. Notice in the previous steps, another property we added is the `MDCNavigationBar` that provides the logic to layout the single line button bar. - - - -#### Swift - -~~~ swift -class CustomHeaderView : UIView { - var titleLabel = UILabel() - var iconView = UIImageView() - - override init(frame: CGRect) { - super.init(frame: frame) - - let icon = UIImage(named: "ic_search")!.imageWithRenderingMode(.AlwaysTemplate) - iconView.image = icon - iconView.tintColor = UIColor.whiteColor().colorWithAlphaComponent(0.7) - titleLabel.textColor = UIColor.whiteColor().colorWithAlphaComponent(0.7) - - self.addSubview(iconView) - self.addSubview(titleLabel) - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - // not implementated - } - - override func layoutSubviews() { - super.layoutSubviews() - // Some fancying arithmetic : TODO replace with autolayout - titleLabel.frame = CGRect(x: 16, y: self.bounds.size.height - 40, width: 128, height: 24) - iconView.frame = CGRect(x: self.bounds.size.width - 24 - 16, y: 20 + 16, width: 24, height: 24) - } -} -~~~ - - - -This header view creates a similar view as the one in the App Bar, for simplicity, but the layoutSubviews -logic locks the titleLabel to the bottom of the header while the iconView stays locks to the top right. - -### Adding Flexible Header - -Integrating the Flexible Header is about the same amount of work as App Bar: - - - -#### Swift - -~~~ swift -class MainViewController : UIViewController, MDCFlexibleHeaderViewLayoutDelegate { - var headerViewController: MDCFlexibleHeaderViewController - var customHeaderView = CustomHeaderView() - - //... -} -~~~ - - - -Override the initialize to add the MDCFlexibleHeaderViewController to the view controller -as a childViewController replacing the previous `MDCAppBArPrepareParent`: - - - -#### Swift - -~~~ swift -override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { - headerViewController = MDCFlexibleHeaderViewController() - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) - self.addChildViewController(headerViewController) - } - - required init?(coder aDecoder: NSCoder) { - headerViewController = MDCFlexibleHeaderViewController() - super.init(coder: aDecoder) - self.addChildViewController(headerViewController) - } -~~~ - - - -Initialize the customHeaderView and connect the headerViewController's headerView to -the UIViewController's hierarchy. This replaces `MDCAppBarAddViews` was doing -except this is not creating any observing of the UINavigationItem. - - - -#### Swift - -~~~ swift -override func viewDidLoad() { - super.viewDidLoad() - - // ... - - // Remove setting up UINavigationItem and MDCAppBarAddViews(self). - - // Initialize the customHeaderView - var headerViewInitialFrame = headerViewController.headerView.contentView!.bounds - headerViewInitialFrame.size.height = 56 + 20 - customHeaderView.frame = headerViewInitialFrame - headerViewController.headerView.contentView?.addSubview(customHeaderView) - - // Connect headerViewController to this viewController's view hierarchy - view.addSubview(headerViewController!.headerView) - headerViewController.didMoveToParentViewController(self) - headerViewController.layoutDelegate = self - -} -~~~ - - - -One final thing that is added is to add this viewController as the layoutDelegate. This allows -us to listen for events when the header is resized. And we can implement a very simple way to -update the header view contents when the layout is changed. - - - -#### Swift - -~~~ swift -// MDCFlexibleHeaderViewLayoutDelegate - func flexibleHeaderViewController(flexibleHeaderViewController: MDCFlexibleHeaderViewController, - flexibleHeaderViewFrameDidChange headerView: MDCFlexibleHeaderView) { - customHeaderView.frame = headerView.contentView!.bounds - } -~~~ - - - - - - -- - - - -## Material Collection Views - -In previous examples, we used a scroll view rather than any content. In Material Components, there -is a sophisticated collection view addition that we've added which implements many of the Material -design styled layout and transitions. - -In order for styling to be done in a compartmentalized way, MDCCollectionViewController has an -abstraction called MDCCollectionViewModel. The model is an implementation of the -UICollectionViewDataSource. The model implements storage for a model objects that contain the -data for rendering the cells. - - -Apps can use the MDCCollectionViewController without the model API and still get the same styling, -but there is more plumbing that needs to be implemented. In some cases, working without the model -API is required if more fine grained control is required. - -The following steps will use the MDCCollectionViewModel to build up a simple collection view - -### Using the MDCCollectionViewController - -MDCCollectionViewController is a subclass of the UICollectionViewController and can be used in place -of a UIViewController base class. Using this is the easiest way to get started with Material collection views. - - - -#### Swift - -~~~ swift -class MainViewController : MDCCollectionViewController, MDCAppBarParenting { - -} -~~~ - - - -The collection view will replace the scroll view that was in the App Bar example earlier. In place of -the scroll view, a model is initialized: - - - -#### Swift - -~~~ swift -override func viewDidLoad() { - super.viewDidLoad() - - // Remove initialization of the scrollView. - - // Initialize the collection view. - let thisCollectionView = collectionView as UICollectionView! - thisCollectionView.mdc_styleController.cellStyle = .Grouped - thisCollectionView.frame = view.bounds - thisCollectionView.delegate = self - view.addSubview(collectionView!) - - // Setup the collection view as the scroll view for the App Bar header to track. - headerViewController?.headerView.trackingScrollView = thisCollectionView - - // Setup the model. - model = MDCCollectionViewModel(delegate: self) - model.setHeader(MDCCellModel.objectWithHeader("Inbox".blackout()), forSection: 0) - // Add 100 rows. - for 0..100 { - model.addItem(MDCCellModel.objectWithTitle("Hello there".blackout()), toSection: 0) - } - - // ... - MDCAppBarAddViews(self) -} -~~~ - - - -Unlike with the simple example with the App Bar above, the UICollectionViewController is the -delegate for the UICollectionView, we cannot just do a simple delegate assignment like we did -with the App Bar to forward scroll view events. - -Instead, we need to manually forward four additional UIScrollViewDelegate methods to -the MDCFlexibleHeaderView to preserve our collapsing header functionality. - - - -#### Swift - -~~~ swift - - override func childViewControllerForStatusBarHidden() -> UIViewController { - return headerViewController! - } - - // UIScrollViewDelegate - override func scrollViewDidScroll(scrollView: UIScrollView) { - headerViewController?.headerView.trackingScrollViewDidScroll() - } - - override func scrollViewDidEndDecelerating(scrollView: UIScrollView) { - headerViewController?.headerView.trackingScrollViewDidEndDecelerating() - } - - override func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) { - headerViewController?.headerView.trackingScrollViewDidEndDraggingWillDecelerate(decelerate) - } - - override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { - headerViewController?.headerView.trackingScrollViewWillEndDraggingWithVelocity(velocity, targetContentOffset: targetContentOffset) - } -~~~ - - - -If these are omitted, the header will continue to work, but there will not be any collapsing and expanding behavior. - -If you prefer to use not use the model, see the Material Collection View component documentation for -more detail. This component also handles editing and moving of rows, which is also covered in the component documentation. - -### Handling taps on a row - -The final step is to handle taps on a row. It is very similar to the normal UICollectionViewDelegate -way of doing things - - - -#### Swift - -~~~ swift -override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { - super.collectionView(collectionView, didSelectItemAtIndexPath: indexPath) - let vc = ViewControllerWithCollections(nibName: nil, bundle: nil) - self.navigationController?.pushViewController(vc, animated: true) -} -~~~ - - - -### Bonus: Custom cells - -[TODO] - - -- - - - -## Material Buttons - -Material Components has several styled buttons depending on where they are placed. For Floating Action Buttons (spec), -the Material Buttons component has a class called MDCShapedButton that allows for creating a simple -rounded button that contains an icon. - -To add this to the Abstract app, the button should be initialized at viewDidLoad and then -added to the view controller's root view so it stays floated in the corner. - - - -#### Swift - -~~~ swift -override func viewDidLoad() { - - button = MDCFloatingButton(shape: .Default) - button!.sizeToFit() - var buttonFrame = button!.frame - buttonFrame.origin.x = view.bounds.size.width - buttonFrame.size.width - 24 - buttonFrame.origin.y = view.bounds.size.height - buttonFrame.size.height - 24 - button!.setBackgroundColor(UIColor(red: 1.0, green: 0.562, blue: 0, alpha:1.0), forState: .Normal) - button!.frame = buttonFrame - button!.setImage(UIImage(named: "ic_add")?.imageWithRenderingMode(.AlwaysTemplate), forState: .Normal) - button!.tintColor = UIColor.whiteColor() - button!.alpha = 0 - button!.addTarget(self, - action: #selector(ViewControllerWithCollections.add(_:)), - forControlEvents: .TouchUpInside) -} - -override func viewDidAppear { - super.viewDidAppear() - weak var weakButton = button - UIView.animateWithDuration(0.2, animations: { - weakButton!.alpha = 1 - }) -} - -~~~ - - - -When the floating action button is tapped on, the `add:` selector is called and it will add a -row to the collection view. Collection views can animate any changes using MDCCollectionViewModel.performBatchOperations - - - -#### Swift - -~~~ swift -func add(target: AnyObject) { - weak var weakModel = model - model.performBatchUpdates({ - weakModel?.insertItem( - MDCCellModel.objectWithTitle("I got added".blackout(), - subtitle: "More text".blackout()), - atIndexPath: NSIndexPath(forRow: 0, inSection: 0)) - }, - withCollectionView: collectionView, - completion: nil) -} -~~~ - - - - -- - - - -## Bonus : Proper view controller transitions - -One odd artifact with this app is the floating action button animates with the rest of the view -controller. The intention of the design is it floats on top of all the views, but for convenience -we've added it to the view controller. - -Rather, when a view controller transition happens, the floating action button should be removed -from the view hierarchy and animated in when the view controller appears. - -Fading in to the view controller: - - - -#### Swift - -~~~ swift - override func viewDidAppear(animated: Bool) { - weak var weakButton = button - UIView.animateWithDuration(0.2, animations: { - weakButton!.alpha = 1 - }) - } -~~~ - - - -Fading out when the view controller changes, replace the `collectionView:didSelectItemAtIndexPath` -to pushViewController with an animation to FAB. - - - -#### Swift - -~~~ swift -override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { - super.collectionView(collectionView, didSelectItemAtIndexPath: indexPath) - let vc = ViewControllerWithCollections(nibName: nil, bundle: nil) - - weak var weakButton = button - weak var weakSelf = self - UIView.animateWithDuration(0.2, animations: { - weakButton!.alpha = 0 - }) { (completed) in - weakSelf?.navigationController?.pushViewController(vc, animated: true) - } -} - -~~~ - - - -- - - - -## Next steps - -This tutorial has taken you through implementing a basic Material Design style app with some of our -components. There are a lot more components that are not covered which are covered in our component -documentation. - -Also see our examples and catalog apps that are in the project to show how to use some of the more -advanced features. - - -- [Read the Component Documentation](/components/) - - -- [Stack Overflow "material-components-ios"](http://stackoverflow.com/questions/tagged/material-components-ios) - - - -- - - - -## Sample Code - -- [**Pesto** - A simple recipe app, incorporating a flexible header, floating action button, and collections. - ](https://github.com/google/material-components-ios/tree/master/demos/Pesto) - - -- [**Shrine** - A demo shopping app, incorporating a flexible header, custom typography, and collections. - ](https://github.com/google/material-components-ios/tree/master/demos/Shrine) - - +- [Build environment](/howto/build-env/) + + diff --git a/howto/build-env/README.md b/howto/build-env/README.md new file mode 100644 index 00000000000..cb4b259dcd7 --- /dev/null +++ b/howto/build-env/README.md @@ -0,0 +1,32 @@ +--- +title: "Build environment" +layout: landing +section: howto +--- + +# Build environment + +Material Components iOS builds with the standard open-source iOS toolchain: +Xcode and CocoaPods. However, there are certain settings that you can use to +maximize compatibility with our source. + +- - - + +## Xcode warning settings + +Deprecation warnings are an important part of how we communicate upcoming +changes to the library; they are enabled by default in typical Xcode and +CocoaPods projects. + +If your project doesn't already specify these warnings, include the following +flags in your build: + + -Wdeprecated + -Wdeprecated-declarations + +If you treat warnings as errors (`-Werror` or "Treat warnings as errors" in +Xcode), then you should exclude deprecation warnings from being treated as +errors to allow the normal deprecation process to work: + + -Wno-error=deprecated + -Wno-error=deprecated-declarations diff --git a/howto/tutorial/README.md b/howto/tutorial/README.md new file mode 100644 index 00000000000..0b9a4b26443 --- /dev/null +++ b/howto/tutorial/README.md @@ -0,0 +1,716 @@ +--- +title: "Material Components Development Guide" +layout: landing +section: howto +--- + +# Material Components Development Guide + +Material Components for iOS is a set of components that help iOS app developers build Material Design apps. These are the same components Google uses to build apps like Google Maps, Calendar, Chrome and many more. + +Individually, the components bring Material Design principles to common UI elements and behaviors, but tailored for iOS. Our team has taken care to design the APIs to feel natural on iOS. + +Our goal is to make implementing Material Design as easy as possible. The components are easy to assemble and be used piecemeal. + +This tutorial will take you through building an example app called Abstractor and show some of the neat features and benefits of using our components to build your app. In order to get through this tutorial, Swift and iOS development knowledge is required. + + +- - - + +## Getting Started + +### Tutorial Setup + +Material Components for iOS can be integrated like any other shared code library on iOS. The preferred method of integration is through CocoaPods. + +To help get started quickly, `git clone` this skeleton new project which the rest of the tutorial will use. + +~~~ bash +git clone https://github.com/google/material-components-ios-example/ +~~~ + +This project is similar to a new project created using Xcode's new project template except with a small number of changes: + +1. Removes the Main.storyboard and references to it in favor of programmatically creating the UI. +2. Adds a bridging header (BridgingHeader.h) and the Xcode configuration for it. +3. Adds a simple String class extension for creating sample text. +4. Adds two icons (search and add) from [Material Icons](https://github.io/google/material-icons) +5. Creates a new MainViewController.swift. + +#### CocoaPods + +The first step is to add Material Components through CocoaPods. The [Material Components quickstart](https://materialcomponents.org/) has detailed instructions, but in short, create a Podfile in the root of the example with the following contents: + +~~~ ruby +target 'Abstractor' do + pod 'MaterialComponents' +end +~~~ + +Run `pod install` in that directory and open up `Abstractor.xcworkspace`. + +#### Bridging for Swift + +Material Components is written in Objective-C and is completely usable from Swift. In order to make the classes visible to Swift, the headers need to be added to the `BridgingHeaders.h`. Open up `BridgingHeaders.h` and add the following lines. + + +~~~ objc +#import "MaterialAppBar.h" +#import "MaterialButtons.h" +#import "MaterialCollections.h" +#import "MaterialFlexibleHeader.h" +~~~ + +#### Building and running the app + +The Abstractor project should be now set up and ready to run. Building and running the project should show you a fairly boring app with a yellow background with no contents. That is our skeleton project the rest of the tutorial will use. + +**TODO: Insert image of the app.** + + +- - - + +## Material Headers + +Headers exist in nearly all apps we see to provide framing and navigation. The header and scrolling behavior is well defined in the [Material Design Guidelines](https://www.google.com/design/spec/TODO) but it is tricky to get right. + +Material Components for iOS provides both a higher level and lower level implementation that allow developers to easily customize the right behavior for the view controller they are building. Both implementations provide a responsive header that can expand and contract in response to scrolling behaviors to maximize the content area or show high level information to the user. + +The [App Bar](https://materialcomponents.org/components/appbar/) is the first way to implement a response header. It uses the familiar UINavigationItem properties of a UIViewController to derive the contents of the header view. + +The [Flexible Header](https://materialcomponents.org/components/flexible-header/) is the second way to implement the responsive header. This component is what the App Bar is built on and is perfect if the developer would like fine grained control over it's contents and behavior. For example, the flexible header can contain a fully custom view that would respond to size changes as the user scrolled. + +If you are used to UINavigationController's UINavigationBar, the fundamental different in design is that your UIViewController has the actual header bar in it's view hierarchy. This contrasts with UINavigationBar being part of UINavigationController's view hierarchy. The different view hierarchy allows gives developers more flexibility when animating between view controllers or customizing unique behaviors when the size of the header changes. + +- - - + +## Starting with App Bar + +In the Abstractor project, there is a view controller already created called MainViewController. To start, we will add a scroll view to the MainViewController since the AppBar works best with a scroll view. + +Modify the MainViewController.swift by adding a UIScrollView. Make the following changes to the MainViewController: + + + +#### Swift + +~~~ swift +class MainViewController : UIViewController { + var scrollView: UIScrollView? + + override func viewDidLoad() { + super.viewDidLoad() + + // Create and initialize a blank scroll view. + scrollView = UIScrollView(frame: view.bounds) + scrollView!.contentSize = CGSize(width: view.bounds.size.width, height: 1000) + scrollView!.backgroundColor = UIColor.whiteColor() + view.addSubview(scrollView!) + } +} +~~~ + + +This snippet is basic UIKit code to create a scroll view, setting the size of the scroll view to be at least 1000 points tall so it will scroll further off screen. + +To actually add the App Bar, we need to give the view controller a protocol to conform to, and override the initializers: + + + +#### Swift + +~~~ swift +class MainViewController : UIViewController, MDCAppBarParenting { + var scrollView: UIScrollView? + + // -- start MDCAppBarParenting + var headerStackView: MDCHeaderStackView? + var navigationBar: MDCNavigationBar? + var headerViewController: MDCFlexibleHeaderViewController? + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + MDCAppBarPrepareParent(self) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + MDCAppBarPrepareParent(self) + } + // -- end MDCAppBarParenting + + override func viewDidLoad() { + super.viewDidLoad() + + // Create and initialize a blank scroll view. + scrollView = UIScrollView(frame: view.bounds) + scrollView!.contentSize = CGSize(width: view.bounds.size.width, height: 1000) + scrollView!.backgroundColor = UIColor.whiteColor() + view.addSubview(scrollView!) + + // -- start MDCAppBarParenting + MDCAppBarAddViews(self) + // -- end MDCAppBarParenting + } +} + +~~~ + + + +At this point, the app will add a grey header bar at the top of the view, but the scroll view will live under the header bar. You can observe this by scrolling the the view and seeing the scroll indicator go below the grey header bar. + +**TODO: Add image of the grey header** + +The MDCFlexibleHeaderViewController that was now exposed as a property of our view controller doesn't know about the scroll view and therefore it cannot adjust any scroll view insets the scroll view needs to render below the bar. To rectify this, simply tell the headerView in the MDCFlexibleHeaderViewController about the scroll view in viewDidLoad(): + + + +#### Swift + +~~~ swift +class MainViewController : UIViewController, MDCAppBarParenting { + // ... + override func viewDidLoad() { + // ... + + // Connect scroll view with the header view controller. + headerViewController?.headerView.trackingScrollView = contentScrollView + headerViewController?.headerView.behavior = .EnabledWithStatusBar + scrollView.delegate = headerViewController + + } +~~~ + + + +Now the scroll view correctly aligns to the bottom of the header bar. Notice that `headerView` had a property called `behavior` which is set to EnabledWithStatusBar. This behavior controls how the header view reacts to scrolling. When `Enabled`, the header will collapse to maximize the content area. Developers can choose whether the status bar should also be hidden. + +**TODO: Add animation of the grey header collapsing.** + +The status bar is not hiding yet, and the reason is by default UIViewController does not hide the status bar. In order for `headerViewController` to assume control of the status bar, override the method `childViewControllerForStatusBarHidden` to use the headerViewController as the childViewController (see the FlexibleHeader component documentation for more details): + + + +#### Swift + +~~~ swift +class MainViewController : UIViewController, MDCAppBarParenting { + // ... + override func childViewControllerForStatusBarHidden() -> UIViewController { + return headerViewController! + } +} +~~~ + + + +**TODO: Add animation of the status bar correctly collapsing.** + +To complete the integration, let's set a proper color and some items on to the header bar. + +To set the color of the header, we can directly manipulate the headerView in viewDidLoad: + + + +#### Swift + +~~~ swift +class MainViewController : UIViewController, MDCAppBarParenting { + // ... + override func viewDidLoad() { + // ... + + // Set color using UIColor extension in UIColorAbstractor.swift + headerViewController!.headerView.backgroundColor = UIColor.materialOrange700() + headerViewController!.headerView.tintColor = UIColor.whiteColor().colorWithAlphaComponent(0.87) + } + + // Set the status bar to white. + override func preferredStatusBarStyle() -> UIStatusBarStyle { + return .LightContent + } +} +~~~ + + + +And finally put some buttons in to the header bar. + + + +#### Swift + +~~~ swift +class MainViewController : UIViewController, MDCAppBarParenting { + override func viewDidLoad() { + // ... + + // Set up UINavigationItems + navigationItem.title = "Abstractor".blackout() + navigationItem.rightBarButtonItem = UIBarButtonItem( + image: UIImage(named: "ic_search")?.imageWithRenderingMode(.AlwaysTemplate), + style: .Plain, + target: self, + action: #selector(MainViewController.search(_:))) + + // Last step in viewDidLoad + MDCAppBarAddViews(self) + } + + // Implement the callback method for the search button. + func search(target: AnyObject) { + } +} +~~~ + + + +And there you have a responsive header that reacts to the scroll view and collapses to maximize +the content. + +- - - + +## Flexible Header + +One advantage of the App Bar component is it's compatibility with UINavigationItem. If developers would like to customize the actual contents inside the header, they need to look at the powerful Flexible Header component. + +Observant developers would already have noticed that App Bar uses Flexible Header to create the behavior. Imagine instead that we would like to lock the title to the bottom of the header but keep search button attached to the top. + + + +### Create a custom header view + +The first thing to do is to create a custom view that will be placed inside the FlexibleHeader. This can be in conjunction with the App Bar or completed without. Notice in the previous steps, another property we added is the `MDCNavigationBar` that provides the logic to layout the single line button bar. + + + +#### Swift + +~~~ swift +class CustomHeaderView : UIView { + var titleLabel = UILabel() + var iconView = UIImageView() + + override init(frame: CGRect) { + super.init(frame: frame) + + let icon = UIImage(named: "ic_search")!.imageWithRenderingMode(.AlwaysTemplate) + iconView.image = icon + iconView.tintColor = UIColor.whiteColor().colorWithAlphaComponent(0.7) + titleLabel.textColor = UIColor.whiteColor().colorWithAlphaComponent(0.7) + + self.addSubview(iconView) + self.addSubview(titleLabel) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + // not implementated + } + + override func layoutSubviews() { + super.layoutSubviews() + // Some fancying arithmetic : TODO replace with autolayout + titleLabel.frame = CGRect(x: 16, y: self.bounds.size.height - 40, width: 128, height: 24) + iconView.frame = CGRect(x: self.bounds.size.width - 24 - 16, y: 20 + 16, width: 24, height: 24) + } +} +~~~ + + + +This header view creates a similar view as the one in the App Bar, for simplicity, but the layoutSubviews +logic locks the titleLabel to the bottom of the header while the iconView stays locks to the top right. + +### Adding Flexible Header + +Integrating the Flexible Header is about the same amount of work as App Bar: + + + +#### Swift + +~~~ swift +class MainViewController : UIViewController, MDCFlexibleHeaderViewLayoutDelegate { + var headerViewController: MDCFlexibleHeaderViewController + var customHeaderView = CustomHeaderView() + + //... +} +~~~ + + + +Override the initialize to add the MDCFlexibleHeaderViewController to the view controller +as a childViewController replacing the previous `MDCAppBArPrepareParent`: + + + +#### Swift + +~~~ swift +override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { + headerViewController = MDCFlexibleHeaderViewController() + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + self.addChildViewController(headerViewController) + } + + required init?(coder aDecoder: NSCoder) { + headerViewController = MDCFlexibleHeaderViewController() + super.init(coder: aDecoder) + self.addChildViewController(headerViewController) + } +~~~ + + + +Initialize the customHeaderView and connect the headerViewController's headerView to +the UIViewController's hierarchy. This replaces `MDCAppBarAddViews` was doing +except this is not creating any observing of the UINavigationItem. + + + +#### Swift + +~~~ swift +override func viewDidLoad() { + super.viewDidLoad() + + // ... + + // Remove setting up UINavigationItem and MDCAppBarAddViews(self). + + // Initialize the customHeaderView + var headerViewInitialFrame = headerViewController.headerView.contentView!.bounds + headerViewInitialFrame.size.height = 56 + 20 + customHeaderView.frame = headerViewInitialFrame + headerViewController.headerView.contentView?.addSubview(customHeaderView) + + // Connect headerViewController to this viewController's view hierarchy + view.addSubview(headerViewController!.headerView) + headerViewController.didMoveToParentViewController(self) + headerViewController.layoutDelegate = self + +} +~~~ + + + +One final thing that is added is to add this viewController as the layoutDelegate. This allows +us to listen for events when the header is resized. And we can implement a very simple way to +update the header view contents when the layout is changed. + + + +#### Swift + +~~~ swift +// MDCFlexibleHeaderViewLayoutDelegate + func flexibleHeaderViewController(flexibleHeaderViewController: MDCFlexibleHeaderViewController, + flexibleHeaderViewFrameDidChange headerView: MDCFlexibleHeaderView) { + customHeaderView.frame = headerView.contentView!.bounds + } +~~~ + + + + + + +- - - + +## Material Collection Views + +In previous examples, we used a scroll view rather than any content. In Material Components, there +is a sophisticated collection view addition that we've added which implements many of the Material +design styled layout and transitions. + +In order for styling to be done in a compartmentalized way, MDCCollectionViewController has an +abstraction called MDCCollectionViewModel. The model is an implementation of the +UICollectionViewDataSource. The model implements storage for a model objects that contain the +data for rendering the cells. + + +Apps can use the MDCCollectionViewController without the model API and still get the same styling, +but there is more plumbing that needs to be implemented. In some cases, working without the model +API is required if more fine grained control is required. + +The following steps will use the MDCCollectionViewModel to build up a simple collection view + +### Using the MDCCollectionViewController + +MDCCollectionViewController is a subclass of the UICollectionViewController and can be used in place +of a UIViewController base class. Using this is the easiest way to get started with Material collection views. + + + +#### Swift + +~~~ swift +class MainViewController : MDCCollectionViewController, MDCAppBarParenting { + +} +~~~ + + + +The collection view will replace the scroll view that was in the App Bar example earlier. In place of +the scroll view, a model is initialized: + + + +#### Swift + +~~~ swift +override func viewDidLoad() { + super.viewDidLoad() + + // Remove initialization of the scrollView. + + // Initialize the collection view. + let thisCollectionView = collectionView as UICollectionView! + thisCollectionView.mdc_styleController.cellStyle = .Grouped + thisCollectionView.frame = view.bounds + thisCollectionView.delegate = self + view.addSubview(collectionView!) + + // Setup the collection view as the scroll view for the App Bar header to track. + headerViewController?.headerView.trackingScrollView = thisCollectionView + + // Setup the model. + model = MDCCollectionViewModel(delegate: self) + model.setHeader(MDCCellModel.objectWithHeader("Inbox".blackout()), forSection: 0) + // Add 100 rows. + for 0..100 { + model.addItem(MDCCellModel.objectWithTitle("Hello there".blackout()), toSection: 0) + } + + // ... + MDCAppBarAddViews(self) +} +~~~ + + + +Unlike with the simple example with the App Bar above, the UICollectionViewController is the +delegate for the UICollectionView, we cannot just do a simple delegate assignment like we did +with the App Bar to forward scroll view events. + +Instead, we need to manually forward four additional UIScrollViewDelegate methods to +the MDCFlexibleHeaderView to preserve our collapsing header functionality. + + + +#### Swift + +~~~ swift + + override func childViewControllerForStatusBarHidden() -> UIViewController { + return headerViewController! + } + + // UIScrollViewDelegate + override func scrollViewDidScroll(scrollView: UIScrollView) { + headerViewController?.headerView.trackingScrollViewDidScroll() + } + + override func scrollViewDidEndDecelerating(scrollView: UIScrollView) { + headerViewController?.headerView.trackingScrollViewDidEndDecelerating() + } + + override func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) { + headerViewController?.headerView.trackingScrollViewDidEndDraggingWillDecelerate(decelerate) + } + + override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + headerViewController?.headerView.trackingScrollViewWillEndDraggingWithVelocity(velocity, targetContentOffset: targetContentOffset) + } +~~~ + + + +If these are omitted, the header will continue to work, but there will not be any collapsing and expanding behavior. + +If you prefer to use not use the model, see the Material Collection View component documentation for +more detail. This component also handles editing and moving of rows, which is also covered in the component documentation. + +### Handling taps on a row + +The final step is to handle taps on a row. It is very similar to the normal UICollectionViewDelegate +way of doing things + + + +#### Swift + +~~~ swift +override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { + super.collectionView(collectionView, didSelectItemAtIndexPath: indexPath) + let vc = ViewControllerWithCollections(nibName: nil, bundle: nil) + self.navigationController?.pushViewController(vc, animated: true) +} +~~~ + + + +### Bonus: Custom cells + +[TODO] + + +- - - + +## Material Buttons + +Material Components has several styled buttons depending on where they are placed. For Floating Action Buttons (spec), +the Material Buttons component has a class called MDCShapedButton that allows for creating a simple +rounded button that contains an icon. + +To add this to the Abstract app, the button should be initialized at viewDidLoad and then +added to the view controller's root view so it stays floated in the corner. + + + +#### Swift + +~~~ swift +override func viewDidLoad() { + + button = MDCFloatingButton(shape: .Default) + button!.sizeToFit() + var buttonFrame = button!.frame + buttonFrame.origin.x = view.bounds.size.width - buttonFrame.size.width - 24 + buttonFrame.origin.y = view.bounds.size.height - buttonFrame.size.height - 24 + button!.setBackgroundColor(UIColor(red: 1.0, green: 0.562, blue: 0, alpha:1.0), forState: .Normal) + button!.frame = buttonFrame + button!.setImage(UIImage(named: "ic_add")?.imageWithRenderingMode(.AlwaysTemplate), forState: .Normal) + button!.tintColor = UIColor.whiteColor() + button!.alpha = 0 + button!.addTarget(self, + action: #selector(ViewControllerWithCollections.add(_:)), + forControlEvents: .TouchUpInside) +} + +override func viewDidAppear { + super.viewDidAppear() + weak var weakButton = button + UIView.animateWithDuration(0.2, animations: { + weakButton!.alpha = 1 + }) +} + +~~~ + + + +When the floating action button is tapped on, the `add:` selector is called and it will add a +row to the collection view. Collection views can animate any changes using MDCCollectionViewModel.performBatchOperations + + + +#### Swift + +~~~ swift +func add(target: AnyObject) { + weak var weakModel = model + model.performBatchUpdates({ + weakModel?.insertItem( + MDCCellModel.objectWithTitle("I got added".blackout(), + subtitle: "More text".blackout()), + atIndexPath: NSIndexPath(forRow: 0, inSection: 0)) + }, + withCollectionView: collectionView, + completion: nil) +} +~~~ + + + + +- - - + +## Bonus : Proper view controller transitions + +One odd artifact with this app is the floating action button animates with the rest of the view +controller. The intention of the design is it floats on top of all the views, but for convenience +we've added it to the view controller. + +Rather, when a view controller transition happens, the floating action button should be removed +from the view hierarchy and animated in when the view controller appears. + +Fading in to the view controller: + + + +#### Swift + +~~~ swift + override func viewDidAppear(animated: Bool) { + weak var weakButton = button + UIView.animateWithDuration(0.2, animations: { + weakButton!.alpha = 1 + }) + } +~~~ + + + +Fading out when the view controller changes, replace the `collectionView:didSelectItemAtIndexPath` +to pushViewController with an animation to FAB. + + + +#### Swift + +~~~ swift +override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { + super.collectionView(collectionView, didSelectItemAtIndexPath: indexPath) + let vc = ViewControllerWithCollections(nibName: nil, bundle: nil) + + weak var weakButton = button + weak var weakSelf = self + UIView.animateWithDuration(0.2, animations: { + weakButton!.alpha = 0 + }) { (completed) in + weakSelf?.navigationController?.pushViewController(vc, animated: true) + } +} + +~~~ + + + +- - - + +## Next steps + +This tutorial has taken you through implementing a basic Material Design style app with some of our +components. There are a lot more components that are not covered which are covered in our component +documentation. + +Also see our examples and catalog apps that are in the project to show how to use some of the more +advanced features. + + +- [Read the Component Documentation](/components/) + + +- [Stack Overflow "material-components-ios"](http://stackoverflow.com/questions/tagged/material-components-ios) + + + +- - - + +## Sample Code + +- [**Pesto** + A simple recipe app, incorporating a flexible header, floating action button, and collections. + ](https://github.com/google/material-components-ios/tree/master/demos/Pesto) + + +- [**Shrine** + A demo shopping app, incorporating a flexible header, custom typography, and collections. + ](https://github.com/google/material-components-ios/tree/master/demos/Shrine) + + diff --git a/usage/build_configuration.md b/usage/build_configuration.md deleted file mode 100644 index c5904ba2386..00000000000 --- a/usage/build_configuration.md +++ /dev/null @@ -1,19 +0,0 @@ -# Build configuration - -These are recommendations for configuring build settings in a project using material-components-ios. - -## Deprecation warnings - -material-components-ios uses deprecation warnings to communicate when an API will eventually be -removed from the code base. - -Deprecation warnings are enabled by default in typical Xcode projects and CocoaPods Pods projects. -If you need to explicitly enable these warnings in your project: - - -Wdeprecated - -Wdeprecated-declarations - -We encourage projects treating warnings as errors to disable deprecation errors: - - -Wno-error=deprecated - -Wno-error=deprecated-declarations From 28e1904f7884de7c18ff646b722678a044497506 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Tue, 19 Apr 2016 11:59:33 -0400 Subject: [PATCH 049/129] [Catalog] Adjust layout for button example in landscape mode, move layout to supplemental Summary: Screenshot http://codereview.cc/M24 Reviewers: ajsecord, featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D661 --- .../ButtonsSimpleExampleViewController.h | 21 - .../ButtonsSimpleExampleViewController.m | 233 ----------- .../Buttons/examples/ButtonsTypicalUse.m | 107 +++++ .../ButtonsTypicalUseSupplemental.h | 22 ++ .../ButtonsTypicalUseSupplemental.m | 366 ++++++++++++++++++ 5 files changed, 495 insertions(+), 254 deletions(-) delete mode 100644 components/Buttons/examples/ButtonsSimpleExampleViewController.h delete mode 100644 components/Buttons/examples/ButtonsSimpleExampleViewController.m create mode 100644 components/Buttons/examples/ButtonsTypicalUse.m create mode 100644 components/Buttons/examples/supplemental/ButtonsTypicalUseSupplemental.h create mode 100644 components/Buttons/examples/supplemental/ButtonsTypicalUseSupplemental.m diff --git a/components/Buttons/examples/ButtonsSimpleExampleViewController.h b/components/Buttons/examples/ButtonsSimpleExampleViewController.h deleted file mode 100644 index 0b6b48e099b..00000000000 --- a/components/Buttons/examples/ButtonsSimpleExampleViewController.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - 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 ButtonsSimpleExampleViewController : UIViewController - -@end diff --git a/components/Buttons/examples/ButtonsSimpleExampleViewController.m b/components/Buttons/examples/ButtonsSimpleExampleViewController.m deleted file mode 100644 index 4fc215404de..00000000000 --- a/components/Buttons/examples/ButtonsSimpleExampleViewController.m +++ /dev/null @@ -1,233 +0,0 @@ -/* - 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 "ButtonsSimpleExampleViewController.h" - -#import "MaterialButtons.h" -#import "MaterialTypography.h" - -@implementation ButtonsSimpleExampleViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - self.view.backgroundColor = [UIColor whiteColor]; - - // Raised button and label - - MDCRaisedButton *raisedButton = [[MDCRaisedButton alloc] init]; - [raisedButton setTitle:@"Button" forState:UIControlStateNormal]; - [raisedButton sizeToFit]; - [raisedButton addTarget:self - action:@selector(didTap:) - forControlEvents:UIControlEventTouchUpInside]; - raisedButton.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:raisedButton]; - - UILabel *raisedButtonLabel = [[UILabel alloc] init]; - raisedButtonLabel.text = @"Raised"; - raisedButtonLabel.font = [MDCTypography captionFont]; - raisedButtonLabel.alpha = [MDCTypography captionFontOpacity]; - [raisedButtonLabel sizeToFit]; - raisedButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:raisedButtonLabel]; - - // Disabled raised button and label - - MDCRaisedButton *disabledRaisedButton = [[MDCRaisedButton alloc] init]; - [disabledRaisedButton setTitle:@"Button" forState:UIControlStateNormal]; - [disabledRaisedButton sizeToFit]; - [disabledRaisedButton addTarget:self - action:@selector(didTap:) - forControlEvents:UIControlEventTouchUpInside]; - [disabledRaisedButton setEnabled:NO]; - disabledRaisedButton.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:disabledRaisedButton]; - - UILabel *disabledRaisedButtonLabel = [[UILabel alloc] init]; - disabledRaisedButtonLabel.text = @"Disabled Raised"; - disabledRaisedButtonLabel.font = [MDCTypography captionFont]; - disabledRaisedButtonLabel.alpha = [MDCTypography captionFontOpacity]; - [disabledRaisedButtonLabel sizeToFit]; - disabledRaisedButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:disabledRaisedButtonLabel]; - - // Flat button and label - - MDCFlatButton *flatButton = [[MDCFlatButton alloc] init]; - [flatButton setTitle:@"Button" forState:UIControlStateNormal]; - [flatButton setCustomTitleColor:[UIColor grayColor]]; - [flatButton sizeToFit]; - [flatButton addTarget:self - action:@selector(didTap:) - forControlEvents:UIControlEventTouchUpInside]; - flatButton.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:flatButton]; - - UILabel *flatButtonLabel = [[UILabel alloc] init]; - flatButtonLabel.text = @"Flat"; - flatButtonLabel.font = [MDCTypography captionFont]; - flatButtonLabel.alpha = [MDCTypography captionFontOpacity]; - [flatButtonLabel sizeToFit]; - flatButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:flatButtonLabel]; - - // Disabled flat button and label - - MDCFlatButton *disabledFlatButton = [[MDCFlatButton alloc] init]; - [disabledFlatButton setTitle:@"Button" forState:UIControlStateNormal]; - [disabledFlatButton setCustomTitleColor:[UIColor grayColor]]; - [disabledFlatButton sizeToFit]; - [disabledFlatButton addTarget:self - action:@selector(didTap:) - forControlEvents:UIControlEventTouchUpInside]; - [disabledFlatButton setEnabled:NO]; - disabledFlatButton.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:disabledFlatButton]; - - UILabel *disabledFlatButtonLabel = [[UILabel alloc] init]; - disabledFlatButtonLabel.text = @"Disabled Flat"; - disabledFlatButtonLabel.font = [MDCTypography captionFont]; - disabledFlatButtonLabel.alpha = [MDCTypography captionFontOpacity]; - [disabledFlatButtonLabel sizeToFit]; - disabledFlatButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:disabledFlatButtonLabel]; - - // Floating action button and label - - MDCFloatingButton *floatingButton = [[MDCFloatingButton alloc] init]; - [floatingButton setTitle:@"+" forState:UIControlStateNormal]; - [floatingButton sizeToFit]; - [floatingButton addTarget:self - action:@selector(didTap:) - forControlEvents:UIControlEventTouchUpInside]; - floatingButton.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:floatingButton]; - - UILabel *floatingButtonLabel = [[UILabel alloc] init]; - floatingButtonLabel.text = @"Floating Action"; - floatingButtonLabel.font = [MDCTypography captionFont]; - floatingButtonLabel.alpha = [MDCTypography captionFontOpacity]; - [floatingButtonLabel sizeToFit]; - floatingButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:floatingButtonLabel]; - - NSDictionary *views = @{ - @"raised" : raisedButton, - @"raisedLabel" : raisedButtonLabel, - @"disabledRaised" : disabledRaisedButton, - @"disabledRaisedLabel" : disabledRaisedButtonLabel, - @"flat" : flatButton, - @"flatLabel" : flatButtonLabel, - @"disabledFlat" : disabledFlatButton, - @"disabledFlatLabel" : disabledFlatButtonLabel, - @"floating" : floatingButton, - @"floatingLabel" : floatingButtonLabel - }; - - NSDictionary *metrics = @{ @"smallVMargin" : @24.0, - @"largeVMargin" : @56.0, - @"smallHMargin" : @24.0, - @"buttonHeight" : @(raisedButton.bounds.size.height), - @"fabHeight" : @(floatingButton.bounds.size.height) }; - - // Vertical column of buttons - NSString *buttonLayoutConstraints = - @"V:[raised]-smallVMargin-" - "[disabledRaised]-largeVMargin-" - "[flat]-smallVMargin-" - "[disabledFlat]-largeVMargin-" - "[floating]"; - - // Vertical column of labels - NSString *labelLayoutConstraints = - @"V:[raisedLabel(buttonHeight)]-smallVMargin-" - "[disabledRaisedLabel(buttonHeight)]-largeVMargin-" - "[flatLabel(buttonHeight)]-smallVMargin-" - "[disabledFlatLabel(buttonHeight)]-largeVMargin-" - "[floatingLabel(fabHeight)]"; - - // Horizontal alignment between the two columns - NSString *columnConstraints = @"[raisedLabel(100)]-smallHMargin-[raised]"; - - // Center view horizontally on the left edge of one of the buttons - [self.view addConstraint: - [NSLayoutConstraint constraintWithItem:flatButton - attribute:NSLayoutAttributeLeft - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeCenterX - multiplier:1.f - constant:12.f]]; - - // Center view vertically on the flat button (it's the middlemost) - [self.view addConstraint: - [NSLayoutConstraint constraintWithItem:flatButton - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeCenterY - multiplier:1.f - constant:0.f]]; - - // Center buttons in their column - [self.view addConstraints: - [NSLayoutConstraint constraintsWithVisualFormat:buttonLayoutConstraints - options:NSLayoutFormatAlignAllCenterX - metrics:metrics - views:views]]; - // Left align labels in their column - [self.view addConstraints: - [NSLayoutConstraint constraintsWithVisualFormat:labelLayoutConstraints - options:NSLayoutFormatAlignAllLeft - metrics:metrics - views:views]]; - - // Vertically align first element in label column to first element in button column - [self.view addConstraint: - [NSLayoutConstraint constraintWithItem:raisedButton - attribute:NSLayoutAttributeCenterY - relatedBy:NSLayoutRelationEqual - toItem:raisedButtonLabel - attribute:NSLayoutAttributeCenterY - multiplier:1.f - constant:0.f]]; - - // Position label column left of button column, wide enough to accommodate label text - [self.view addConstraints: - [NSLayoutConstraint constraintsWithVisualFormat:columnConstraints - options:0 - metrics:metrics - views:views]]; -} - -- (void)didTap:(id)sender { - NSLog(@"%@ was tapped.", NSStringFromClass([sender class])); -} - -+ (NSArray *)catalogBreadcrumbs { - return @[ @"Buttons", @"Buttons" ]; -} - -+ (NSString *)catalogDescription { - return @"Buttons is a collection of Material Design buttons, including a flat button, a raised" - " button and a floating action button."; -} - -+ (BOOL)catalogIsPrimaryDemo { - return YES; -} - -@end diff --git a/components/Buttons/examples/ButtonsTypicalUse.m b/components/Buttons/examples/ButtonsTypicalUse.m new file mode 100644 index 00000000000..17bb5b025b2 --- /dev/null +++ b/components/Buttons/examples/ButtonsTypicalUse.m @@ -0,0 +1,107 @@ +/* + 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 "ButtonsTypicalUseSupplemental.h" + +#import "MaterialButtons.h" +#import "MaterialTypography.h" + +@interface ButtonsTypicalUseViewController () + +@end + +@implementation ButtonsTypicalUseViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + self.view.backgroundColor = [UIColor whiteColor]; + + // Raised button + + MDCRaisedButton *raisedButton = [[MDCRaisedButton alloc] init]; + [raisedButton setTitle:@"Button" forState:UIControlStateNormal]; + [raisedButton sizeToFit]; + [raisedButton addTarget:self + action:@selector(didTap:) + forControlEvents:UIControlEventTouchUpInside]; + raisedButton.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:raisedButton]; + + // Disabled raised button + + MDCRaisedButton *disabledRaisedButton = [[MDCRaisedButton alloc] init]; + [disabledRaisedButton setTitle:@"Button" forState:UIControlStateNormal]; + [disabledRaisedButton sizeToFit]; + [disabledRaisedButton addTarget:self + action:@selector(didTap:) + forControlEvents:UIControlEventTouchUpInside]; + [disabledRaisedButton setEnabled:NO]; + disabledRaisedButton.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:disabledRaisedButton]; + + // Flat button + + MDCFlatButton *flatButton = [[MDCFlatButton alloc] init]; + [flatButton setTitle:@"Button" forState:UIControlStateNormal]; + [flatButton setCustomTitleColor:[UIColor grayColor]]; + [flatButton sizeToFit]; + [flatButton addTarget:self + action:@selector(didTap:) + forControlEvents:UIControlEventTouchUpInside]; + flatButton.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:flatButton]; + + // Disabled flat + + MDCFlatButton *disabledFlatButton = [[MDCFlatButton alloc] init]; + [disabledFlatButton setTitle:@"Button" forState:UIControlStateNormal]; + [disabledFlatButton setCustomTitleColor:[UIColor grayColor]]; + [disabledFlatButton sizeToFit]; + [disabledFlatButton addTarget:self + action:@selector(didTap:) + forControlEvents:UIControlEventTouchUpInside]; + [disabledFlatButton setEnabled:NO]; + disabledFlatButton.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:disabledFlatButton]; + + // Floating action button + + MDCFloatingButton *floatingButton = [[MDCFloatingButton alloc] init]; + [floatingButton setTitle:@"+" forState:UIControlStateNormal]; + [floatingButton sizeToFit]; + [floatingButton addTarget:self + action:@selector(didTap:) + forControlEvents:UIControlEventTouchUpInside]; + floatingButton.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:floatingButton]; + + NSDictionary *views = @{ @"raised" : raisedButton, + @"disabledRaised" : disabledRaisedButton, + @"flat" : flatButton, + @"disabledFlat" : disabledFlatButton, + @"floating" : floatingButton }; + + self.views = [NSMutableDictionary dictionary]; + [self.views addEntriesFromDictionary:views]; + + [self setupExampleViews]; +} + +- (void)didTap:(id)sender { + NSLog(@"%@ was tapped.", NSStringFromClass([sender class])); +} + +@end diff --git a/components/Buttons/examples/supplemental/ButtonsTypicalUseSupplemental.h b/components/Buttons/examples/supplemental/ButtonsTypicalUseSupplemental.h new file mode 100644 index 00000000000..1541578fe2e --- /dev/null +++ b/components/Buttons/examples/supplemental/ButtonsTypicalUseSupplemental.h @@ -0,0 +1,22 @@ +/* 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 ButtonsTypicalUseViewController; + +@interface ButtonsTypicalUseViewController : UIViewController + +@property(nonatomic, strong) NSMutableDictionary *views; +@property(nonatomic, strong) NSMutableArray *portraitLayoutConstraints; +@property(nonatomic, strong) NSMutableArray *landscapeLayoutConstraints; + +@end + +@interface ButtonsTypicalUseViewController (Supplemental) + +- (void)setupExampleViews; + +@end diff --git a/components/Buttons/examples/supplemental/ButtonsTypicalUseSupplemental.m b/components/Buttons/examples/supplemental/ButtonsTypicalUseSupplemental.m new file mode 100644 index 00000000000..f1f2a9784eb --- /dev/null +++ b/components/Buttons/examples/supplemental/ButtonsTypicalUseSupplemental.m @@ -0,0 +1,366 @@ +/* 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 "ButtonsTypicalUseSupplemental.h" +#import "MaterialButtons.h" +#import "MaterialTypography.h" + +#pragma mark - ButtonsTypicalUseViewController + +@implementation ButtonsTypicalUseViewController (CatalogByConvention) + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Buttons", @"Buttons" ]; +} + ++ (NSString *)catalogDescription { + return @"Buttons is a collection of Material Design buttons, including a flat button, a raised" + " button and a floating action button."; +} + ++ (BOOL)catalogIsPrimaryDemo { + return YES; +} + +@end + +@implementation ButtonsTypicalUseViewController (Supplemental) + +- (void)addLabels { + UILabel *raisedButtonLabel = [[UILabel alloc] init]; + raisedButtonLabel.text = @"Raised"; + raisedButtonLabel.font = [MDCTypography captionFont]; + raisedButtonLabel.alpha = [MDCTypography captionFontOpacity]; + [raisedButtonLabel sizeToFit]; + raisedButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:raisedButtonLabel]; + + UILabel *disabledRaisedButtonLabel = [[UILabel alloc] init]; + disabledRaisedButtonLabel.text = @"Disabled Raised"; + disabledRaisedButtonLabel.font = [MDCTypography captionFont]; + disabledRaisedButtonLabel.alpha = [MDCTypography captionFontOpacity]; + [disabledRaisedButtonLabel sizeToFit]; + disabledRaisedButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:disabledRaisedButtonLabel]; + + UILabel *flatButtonLabel = [[UILabel alloc] init]; + flatButtonLabel.text = @"Flat"; + flatButtonLabel.font = [MDCTypography captionFont]; + flatButtonLabel.alpha = [MDCTypography captionFontOpacity]; + [flatButtonLabel sizeToFit]; + flatButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:flatButtonLabel]; + + UILabel *disabledFlatButtonLabel = [[UILabel alloc] init]; + disabledFlatButtonLabel.text = @"Disabled Flat"; + disabledFlatButtonLabel.font = [MDCTypography captionFont]; + disabledFlatButtonLabel.alpha = [MDCTypography captionFontOpacity]; + [disabledFlatButtonLabel sizeToFit]; + disabledFlatButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:disabledFlatButtonLabel]; + + UILabel *floatingButtonLabel = [[UILabel alloc] init]; + floatingButtonLabel.text = @"Floating Action"; + floatingButtonLabel.font = [MDCTypography captionFont]; + floatingButtonLabel.alpha = [MDCTypography captionFontOpacity]; + [floatingButtonLabel sizeToFit]; + floatingButtonLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:floatingButtonLabel]; + + NSDictionary *views = @{ @"raisedLabel" : raisedButtonLabel, + @"disabledRaisedLabel" : disabledRaisedButtonLabel, + @"flatLabel" : flatButtonLabel, + @"disabledFlatLabel" : disabledFlatButtonLabel, + @"floatingLabel" : floatingButtonLabel }; + [self.views addEntriesFromDictionary:views]; +} + +- (void)setupExampleViews { + [self addLabels]; + + UILabel *raisedButtonLabel = self.views[@"raisedLabel"]; + MDCRaisedButton *raisedButton = self.views[@"raised"]; + UILabel *disabledRaisedButtonLabel = self.views[@"disabledRaisedLabel"]; + MDCRaisedButton *disabledRaisedButton = self.views[@"disabledRaised"]; + + UILabel *flatButtonLabel = self.views[@"flatLabel"]; + MDCFlatButton *flatButton = self.views[@"flat"]; + UILabel *disabledFlatButtonLabel = self.views[@"disabledFlatLabel"]; + MDCFlatButton *disabledFlatButton = self.views[@"disabledFlat"]; + + UILabel *floatingButtonLabel = self.views[@"floatingLabel"]; + MDCFloatingButton *floatingButton = self.views[@"floating"]; + + self.portraitLayoutConstraints = [NSMutableArray array]; + + NSDictionary *metrics = @{ @"smallVMargin" : @24.0, + @"largeVMargin" : @56.0, + @"smallHMargin" : @24.0, + @"buttonHeight" : @(raisedButton.bounds.size.height), + @"fabHeight" : @(floatingButton.bounds.size.height) }; + + // Vertical column of buttons + NSString *buttonLayoutConstraints = + @"V:[raised]-smallVMargin-" + "[disabledRaised]-largeVMargin-" + "[flat]-smallVMargin-" + "[disabledFlat]-largeVMargin-" + "[floating]"; + + // Vertical column of labels + NSString *labelLayoutConstraints = + @"V:[raisedLabel(buttonHeight)]-smallVMargin-" + "[disabledRaisedLabel(buttonHeight)]-largeVMargin-" + "[flatLabel(buttonHeight)]-smallVMargin-" + "[disabledFlatLabel(buttonHeight)]-largeVMargin-" + "[floatingLabel(fabHeight)]"; + + // Horizontal alignment between the two columns + NSString *columnConstraints = @"[raisedLabel(100)]-smallHMargin-[raised]"; + + // Center view horizontally on the left edge of one of the buttons + NSLayoutConstraint *horizontalConstraint = + [NSLayoutConstraint constraintWithItem:flatButton + attribute:NSLayoutAttributeLeft + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeCenterX + multiplier:1.f + constant:12.f]; + [self.portraitLayoutConstraints addObject:horizontalConstraint]; + + // Center view vertically on the flat button (it's the middlemost) + NSLayoutConstraint *verticalConstraint = + [NSLayoutConstraint constraintWithItem:flatButton + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeCenterY + multiplier:1.f + constant:0.f]; + [self.portraitLayoutConstraints addObject:verticalConstraint]; + + // Center buttons in their column + NSArray *buttonConstraints = + [NSLayoutConstraint constraintsWithVisualFormat:buttonLayoutConstraints + options:NSLayoutFormatAlignAllCenterX + metrics:metrics + views:self.views]; + [self.portraitLayoutConstraints addObjectsFromArray:buttonConstraints]; + + // Left align labels in their column + NSArray *labelConstraints = + [NSLayoutConstraint constraintsWithVisualFormat:labelLayoutConstraints + options:NSLayoutFormatAlignAllLeft + metrics:metrics + views:self.views]; + [self.portraitLayoutConstraints addObjectsFromArray:labelConstraints]; + + // Vertically align first element in label column to first element in button column + NSLayoutConstraint *labelColConstraint = + [NSLayoutConstraint constraintWithItem:raisedButton + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:raisedButtonLabel + attribute:NSLayoutAttributeCenterY + multiplier:1.f + constant:0.f]; + [self.portraitLayoutConstraints addObject:labelColConstraint]; + + // Position label column left of button column, wide enough to accommodate label text + NSArray *labelLeftColConstraints = + [NSLayoutConstraint constraintsWithVisualFormat:columnConstraints + options:0 + metrics:metrics + views:self.views]; + [self.portraitLayoutConstraints addObjectsFromArray:labelLeftColConstraints]; + + self.landscapeLayoutConstraints = [NSMutableArray array]; + + NSLayoutConstraint *landscapeRaiseButtonConstraint = + [NSLayoutConstraint constraintWithItem:raisedButtonLabel + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeCenterY + multiplier:0.5 + constant:0]; + [self.landscapeLayoutConstraints addObject:landscapeRaiseButtonConstraint]; + + NSDictionary *views = @{ @"raisedButtonLabel" : raisedButtonLabel, + @"raisedButton" : raisedButton, + @"flatButtonLabel" : flatButtonLabel, + @"flatButton" : flatButton }; + NSString *horizontalConstraints = @"H:|-(50)-[raisedButtonLabel(100)]-[raisedButton]-(50)-" + "[flatButtonLabel(100)]-[flatButton]-(50)-|"; + NSDictionary *horizontalMetrics = @{ @"raisedButtonLabel" : @(50), + @"raisedButton" : @(raisedButton.frame.size.width), + @"flatButtonLabel" : @(200), + @"flatButton" : @(flatButton.frame.size.width) }; + + NSArray *landscapeHorizontalMetricsConstraints = + [NSLayoutConstraint constraintsWithVisualFormat:horizontalConstraints + options:NSLayoutFormatAlignAllCenterY + metrics:horizontalMetrics + views:views]; + [self.landscapeLayoutConstraints addObjectsFromArray:landscapeHorizontalMetricsConstraints]; + + NSDictionary *raisedButtonLabelViews = + @{ @"raisedButtonLabel" : raisedButtonLabel, + @"disabledRaisedButtonLabel" : disabledRaisedButtonLabel }; + NSString *raisedButtonLabelConstraints = @"V:[raisedButtonLabel]-[disabledRaisedButtonLabel]"; + + NSDictionary *metricsY = + @{ @"raisedButtonLabel" : @(raisedButtonLabel.frame.size.height), + @"disabledRaisedButtonLabel" : @(disabledRaisedButtonLabel.frame.size.height) }; + + NSArray *landscapeRaisedButtonMetricsConstraints = + [NSLayoutConstraint constraintsWithVisualFormat:raisedButtonLabelConstraints + options:NSLayoutFormatAlignAllCenterX + metrics:metricsY + views:raisedButtonLabelViews]; + [self.landscapeLayoutConstraints addObjectsFromArray:landscapeRaisedButtonMetricsConstraints]; + + NSArray *landscapeRaisedButtonConstraints = + [NSLayoutConstraint constraintsWithVisualFormat:raisedButtonLabelConstraints + options:NSLayoutFormatAlignAllLeft + metrics:metricsY + views:raisedButtonLabelViews]; + [self.landscapeLayoutConstraints addObjectsFromArray:landscapeRaisedButtonConstraints]; + + NSLayoutConstraint *landscapeDisabledRaiseButtonConstraint = + [NSLayoutConstraint constraintWithItem:disabledRaisedButton + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:disabledRaisedButtonLabel + attribute:NSLayoutAttributeCenterY + multiplier:1.f + constant:0.f]; + [self.landscapeLayoutConstraints addObject:landscapeDisabledRaiseButtonConstraint]; + + NSDictionary *raisedButtonViews = @{ @"raisedButton" : raisedButton, + @"disabledRaisedButton" : disabledRaisedButton }; + NSString *raisedButtonConstraints = @"V:[raisedButton]-[disabledRaisedButton]"; + NSDictionary *raisedButtonMetrics = + @{ @"raisedButton" : @(raisedButton.frame.size.height), + @"disabledRaisedButton" : @(disabledRaisedButton.frame.size.height) }; + NSArray *landscapeMetricsY2Constraints = + [NSLayoutConstraint constraintsWithVisualFormat:raisedButtonConstraints + options:NSLayoutFormatAlignAllCenterX + metrics:raisedButtonMetrics + views:raisedButtonViews]; + [self.landscapeLayoutConstraints addObjectsFromArray:landscapeMetricsY2Constraints]; + + NSArray *landscapeMetricsY2LeftConstraints = + [NSLayoutConstraint constraintsWithVisualFormat:raisedButtonConstraints + options:NSLayoutFormatAlignAllLeft + metrics:raisedButtonMetrics + views:raisedButtonViews]; + [self.landscapeLayoutConstraints addObjectsFromArray:landscapeMetricsY2LeftConstraints]; + + NSDictionary *flatButtonLabelViews = @{ @"flatButtonLabel" : flatButtonLabel, + @"disabledFlatButtonLabel" : disabledFlatButtonLabel }; + NSString *flatButtonLabelConstraints = @"V:[flatButtonLabel]-[disabledFlatButtonLabel]"; + NSDictionary *flatButtonLabelMetrics = + @{ @"flatButtonLabel" : @(flatButtonLabel.frame.size.height), + @"disabledFlatButtonLabel" : @(disabledFlatButtonLabel.frame.size.height) }; + NSArray *landscapeMetricsY3Constraints = + [NSLayoutConstraint constraintsWithVisualFormat:flatButtonLabelConstraints + options:NSLayoutFormatAlignAllCenterX + metrics:flatButtonLabelMetrics + views:flatButtonLabelViews]; + [self.landscapeLayoutConstraints addObjectsFromArray:landscapeMetricsY3Constraints]; + + NSArray *landscapeMetricsY3LeftConstraints = + [NSLayoutConstraint constraintsWithVisualFormat:flatButtonLabelConstraints + options:NSLayoutFormatAlignAllLeft + metrics:flatButtonLabelMetrics + views:flatButtonLabelViews]; + [self.landscapeLayoutConstraints addObjectsFromArray:landscapeMetricsY3LeftConstraints]; + + NSDictionary *flatButtonViews = @{ @"flatButton" : flatButton, + @"disabledFlatButton" : disabledFlatButton }; + NSString *flatButtonConstraints = @"V:[flatButton]-[disabledFlatButton]"; + NSDictionary *flatButtonMetrics = @{ @"flatButton" : @(flatButton.frame.size.height), + @"disabledFlatButton" : @(disabledFlatButton.frame.size.height) }; + + NSArray *landscapeMetricsY4Constraints = + [NSLayoutConstraint constraintsWithVisualFormat:flatButtonConstraints + options:NSLayoutFormatAlignAllCenterX + metrics:flatButtonMetrics + views:flatButtonViews]; + [self.landscapeLayoutConstraints addObjectsFromArray:landscapeMetricsY4Constraints]; + + NSArray *landscapeMetricsY4LeftConstraints = + [NSLayoutConstraint constraintsWithVisualFormat:flatButtonConstraints + options:NSLayoutFormatAlignAllLeft + metrics:flatButtonMetrics + views:flatButtonViews]; + [self.landscapeLayoutConstraints addObjectsFromArray:landscapeMetricsY4LeftConstraints]; + + NSLayoutConstraint *landscapeMetricsCenterConstraint = + [NSLayoutConstraint constraintWithItem:disabledFlatButton + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:disabledFlatButtonLabel + attribute:NSLayoutAttributeCenterY + multiplier:1.f + constant:0.f]; + [self.landscapeLayoutConstraints addObject:landscapeMetricsCenterConstraint]; + + NSLayoutConstraint *landscapeEqualCenterYConstraint = + [NSLayoutConstraint constraintWithItem:floatingButton + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeCenterY + multiplier:1.5 + constant:0]; + [self.landscapeLayoutConstraints addObject:landscapeEqualCenterYConstraint]; + + NSLayoutConstraint *landscapeEqualCenterXConstraint = + [NSLayoutConstraint constraintWithItem:floatingButton + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeCenterX + multiplier:1 + constant:50]; + [self.landscapeLayoutConstraints addObject:landscapeEqualCenterXConstraint]; + + NSLayoutConstraint *landscapeFloatingButtonLabelCenterConstraint = + [NSLayoutConstraint constraintWithItem:floatingButton + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:floatingButtonLabel + attribute:NSLayoutAttributeCenterY + multiplier:1.f + constant:0.f]; + [self.landscapeLayoutConstraints addObject:landscapeFloatingButtonLabelCenterConstraint]; + + NSLayoutConstraint *landscapeFloatingButtonCenterEqualConstraint = + [NSLayoutConstraint constraintWithItem:floatingButtonLabel + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeCenterX + multiplier:1 + constant:-50]; + [self.landscapeLayoutConstraints addObject:landscapeFloatingButtonCenterEqualConstraint]; +} + +- (void)viewWillLayoutSubviews { + if (self.view.frame.size.width < self.view.frame.size.height) { + [self.view removeConstraints:self.landscapeLayoutConstraints]; + [self.view addConstraints:self.portraitLayoutConstraints]; + } else { + [self.view removeConstraints:self.portraitLayoutConstraints]; + [self.view addConstraints:self.landscapeLayoutConstraints]; + } +} + +@end From 6206c48cb0dfb3c23ed228ba31fce6892c3094b8 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Tue, 19 Apr 2016 12:35:33 -0400 Subject: [PATCH 050/129] [Catalog] Add autoresize masks to header stack view demo for landscape orientation Summary: Line formatting Reviewers: #mdc_ios_owners, ajsecord, featherless Reviewed By: #mdc_ios_owners, ajsecord, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D663 --- .../examples/HeaderStackViewTypicalUse.m | 2 ++ .../HeaderStackViewTypicalUseSupplemental.m | 25 +++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/components/HeaderStackView/examples/HeaderStackViewTypicalUse.m b/components/HeaderStackView/examples/HeaderStackViewTypicalUse.m index dd65a51d9fd..5d12ed93031 100644 --- a/components/HeaderStackView/examples/HeaderStackViewTypicalUse.m +++ b/components/HeaderStackView/examples/HeaderStackViewTypicalUse.m @@ -27,9 +27,11 @@ @implementation HeaderStackViewTypicalUse - (void)viewDidLoad { [super viewDidLoad]; + [self setupExampleViews]; self.stackView = [[MDCHeaderStackView alloc] init]; + self.stackView.autoresizingMask = UIViewAutoresizingFlexibleWidth; self.stackView.topBar = self.topView; self.stackView.bottomBar = self.navBar; diff --git a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m index 197463a6766..00403cabc26 100644 --- a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m +++ b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m @@ -33,11 +33,16 @@ @implementation HeaderStackViewTypicalUse (Supplemental) - (void)setupExampleViews { self.exampleView = [[ExampleInstructionsViewHeaderStackViewTypicalUse alloc] initWithFrame:self.view.bounds]; + self.exampleView.autoresizingMask = + UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; + [self.view addSubview:self.exampleView]; self.view.backgroundColor = [UIColor whiteColor]; self.topView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 100)]; + self.topView.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; UIImageView *imageView = [[UIImageView alloc] initWithImage:[self headerBackgroundImage]]; imageView.frame = self.topView.bounds; @@ -150,19 +155,19 @@ - (NSAttributedString *)instructionsString { NSDictionary *instructionAttributes1 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSDictionary *instructionAttributes2 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSString *instructionText = @"SWIPE RIGHT\n\n\n\nto go back\n\n\n\n\n\n"; NSMutableAttributedString *instructionsAttributedString = [[NSMutableAttributedString alloc] From b9b819536bf19eca5298acc9fd471f92333cb555 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Tue, 19 Apr 2016 15:15:57 -0400 Subject: [PATCH 051/129] [FlexibleHeader] Configurator example is now a table view. Summary: Added a new configuration option for header content importance. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D664 --- .../FlexibleHeaderConfiguratorExample.m | 256 +++++----- .../FlexibleHeaderConfiguratorControlItem.h | 39 ++ .../FlexibleHeaderConfiguratorControlItem.m | 36 ++ .../FlexibleHeaderConfiguratorSupplemental.h | 44 +- .../FlexibleHeaderConfiguratorSupplemental.m | 481 ++++++++---------- 5 files changed, 421 insertions(+), 435 deletions(-) create mode 100644 components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorControlItem.h create mode 100644 components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorControlItem.m diff --git a/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m b/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m index 5a663fd2dc3..9f4d00c162d 100644 --- a/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m +++ b/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m @@ -20,171 +20,149 @@ #import "FlexibleHeaderConfiguratorSupplemental.h" -@interface FlexibleHeaderConfiguratorExample () +@implementation FlexibleHeaderConfiguratorExample -@property(nonatomic) MDCFlexibleHeaderViewController *fhvc; +// Invoked when the user has changed a control's value. +- (void)field:(FlexibleHeaderConfiguratorField)field didChangeValue:(NSNumber *)value { + MDCFlexibleHeaderView *headerView = self.fhvc.headerView; + switch (field) { + // Basic behavior + + case FlexibleHeaderConfiguratorFieldCanOverExtend: + headerView.canOverExtend = [value boolValue]; + break; + + case FlexibleHeaderConfiguratorFieldInFrontOfInfiniteContent: + headerView.inFrontOfInfiniteContent = [value boolValue]; + break; + + // Shift behavior + + case FlexibleHeaderConfiguratorFieldShiftBehaviorEnabled: { + BOOL isOn = [value boolValue]; + if (!isOn) { + headerView.shiftBehavior = MDCFlexibleHeaderShiftBehaviorDisabled; + [self didChangeValueForField:FlexibleHeaderConfiguratorFieldShiftBehaviorEnabledWithStatusBar + animated:YES]; + } else { + headerView.shiftBehavior = MDCFlexibleHeaderShiftBehaviorEnabled; + } + break; + } -@end + case FlexibleHeaderConfiguratorFieldShiftBehaviorEnabledWithStatusBar: { + BOOL isOn = [value boolValue]; + if (!isOn) { + headerView.shiftBehavior = MDCFlexibleHeaderShiftBehaviorEnabled; + } else { + headerView.shiftBehavior = MDCFlexibleHeaderShiftBehaviorEnabledWithStatusBar; + [self didChangeValueForField:FlexibleHeaderConfiguratorFieldShiftBehaviorEnabled + animated:YES]; + } + break; + } -@implementation FlexibleHeaderConfiguratorExample + case FlexibleHeaderConfiguratorFieldContentImportance: + headerView.headerContentImportance = ([value boolValue] + ? MDCFlexibleHeaderContentImportanceHigh + : MDCFlexibleHeaderContentImportanceDefault); + break; -- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; - if (self) { - [self commonFlexibleHeaderConfiguratorExampleInit]; - } - return self; -} + // Header height + + case FlexibleHeaderConfiguratorFieldMinimumHeight: + headerView.minimumHeight = [self heightDenormalized:[value floatValue]]; + break; -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - [self commonFlexibleHeaderConfiguratorExampleInit]; + case FlexibleHeaderConfiguratorFieldMaximumHeight: + headerView.maximumHeight = [self heightDenormalized:[value floatValue]]; + break; } - return self; } -- (void)commonFlexibleHeaderConfiguratorExampleInit { - _fhvc = [[MDCFlexibleHeaderViewController alloc] initWithNibName:nil bundle:nil]; - [self addChildViewController:_fhvc]; +#pragma mark - Typical Flexible Header implementations - self.title = @"Header Configuration"; +// Required for shiftBehavior == MDCFlexibleHeaderShiftBehaviorEnabledWithStatusBar. +- (UIViewController *)childViewControllerForStatusBarHidden { + return self.fhvc; } -- (void)viewDidLoad { - [super viewDidLoad]; - self.view.backgroundColor = [UIColor whiteColor]; - - self.scrollView = [[UIScrollView alloc] init]; - [self.view addSubview:self.scrollView]; - - // If a tableView was being used instead of a scrollView, you would set the trackingScrollView - // to be that tableView and either set the MDCFlexibleHeaderViewController to be the - // UITableViewDelegate or forward the UIScrollViewDelegate methods to - // MDCFlexibleHeaderViewController from the UITableViewDelegate. - self.scrollView.delegate = self.fhvc; - self.fhvc.headerView.trackingScrollView = self.scrollView; - - self.fhvc.view.frame = self.view.bounds; - - [self.view addSubview:self.fhvc.view]; - - [self.fhvc didMoveToParentViewController:self]; - - UIColor *lightBlue500 = [UIColor colorWithRed:0.012 - green:0.663 - blue:0.957 - alpha:1]; - self.fhvc.headerView.backgroundColor = lightBlue500; - [self setupExampleViews:self.fhvc]; - - // Add Back button - UIToolbar *bar = [[UIToolbar alloc] initWithFrame:CGRectZero]; - bar.translatesAutoresizingMaskIntoConstraints = NO; - bar.barTintColor = lightBlue500; - bar.translucent = NO; - bar.clipsToBounds = YES; - - [self.fhvc.headerView addSubview:bar]; - - UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@"Back" - style:UIBarButtonItemStyleDone - target:self - action:@selector(didTapButton:)]; - backButton.tintColor = [UIColor whiteColor]; - - // Add a title label - UIBarButtonItem *spacer = [[UIBarButtonItem alloc] - initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace - target:nil - action:nil]; - - UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; - titleLabel.text = @"Configurator"; - titleLabel.textColor = [UIColor whiteColor]; - titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleTitle2]; - [titleLabel sizeToFit]; - - UIBarButtonItem *titleItem = [[UIBarButtonItem alloc] initWithCustomView:titleLabel]; - - bar.items = @[ backButton, spacer, titleItem, spacer ]; - - NSDictionary *viewBindings = @{ @"bar" : bar }; - NSMutableArray<__kindof NSLayoutConstraint *> *arrayOfConstraints = [NSMutableArray array]; - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"H:|-[bar]-|" - options:0 - metrics:nil - views:viewBindings]]; - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"V:[bar]-8-|" - options:0 - metrics:nil - views:viewBindings]]; - - [self.view addConstraints:arrayOfConstraints]; -} +#pragma mark - UIScrollViewDelegate -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; +// Note that, unlike the Typical Use example, we are explicitly forwarding the UIScrollViewDelegate +// methods to the header view. This is because this example controller also needs to handle other +// UITableViewDelegate events. - // If the MDCFlexibleHeaderViewController's view is not going to replace a navigation bar, - // comment this line: - [self.navigationController setNavigationBarHidden:YES animated:animated]; +- (void)scrollViewDidScroll:(UIScrollView *)scrollView { + if (scrollView == self.fhvc.headerView.trackingScrollView) { + [self.fhvc.headerView trackingScrollViewDidScroll]; + } } -- (UIStatusBarStyle)preferredStatusBarStyle { - return UIStatusBarStyleLightContent; +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + if (scrollView == self.fhvc.headerView.trackingScrollView) { + [self.fhvc.headerView trackingScrollViewDidEndDecelerating]; + } } -// This method must be implemented for MDCFlexibleHeaderViewController's -// MDCFlexibleHeaderView to properly support MDCFlexibleHeaderShiftBehavior should you choose -// to customize it. -- (UIViewController *)childViewControllerForStatusBarHidden { - return self.fhvc; +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { + MDCFlexibleHeaderView *headerView = self.fhvc.headerView; + if (scrollView == headerView.trackingScrollView) { + [headerView trackingScrollViewDidEndDraggingWillDecelerate:decelerate]; + } } -#pragma mark - Target Action - -- (void)sliderDidSlide:(UISlider *)sender { - if (sender == self.exampleView.minHeightSlider) { - self.fhvc.headerView.minimumHeight = sender.value; - self.exampleView.maxHeightSlider.value = MAX(self.exampleView.maxHeightSlider.value, - self.fhvc.headerView.minimumHeight); - } else if (sender == self.exampleView.maxHeightSlider) { - self.fhvc.headerView.maximumHeight = sender.value; - self.exampleView.minHeightSlider.value = MIN(self.exampleView.minHeightSlider.value, - self.fhvc.headerView.maximumHeight); +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView + withVelocity:(CGPoint)velocity + targetContentOffset:(inout CGPoint *)targetContentOffset { + MDCFlexibleHeaderView *headerView = self.fhvc.headerView; + if (scrollView == headerView.trackingScrollView) { + [headerView trackingScrollViewWillEndDraggingWithVelocity:velocity + targetContentOffset:targetContentOffset]; } } -- (void)switchDidToggle:(UISwitch *)sender { - if (sender == self.exampleView.overExtendSwitch) { - self.fhvc.headerView.canOverExtend = sender.isOn; - } else if (sender == self.exampleView.shiftSwitch) { - if (!self.exampleView.shiftSwitch.isOn) { - self.exampleView.shiftStatusBarSwitch.on = NO; - self.fhvc.headerView.shiftBehavior = MDCFlexibleHeaderShiftBehaviorDisabled; - } else { - if (self.fhvc.headerView.shiftBehavior != MDCFlexibleHeaderShiftBehaviorEnabled || - self.fhvc.headerView.shiftBehavior != MDCFlexibleHeaderShiftBehaviorEnabledWithStatusBar) { - self.fhvc.headerView.shiftBehavior = MDCFlexibleHeaderShiftBehaviorEnabled; - } +#pragma mark - Field data manipulation + +static const CGFloat kHeightScalar = 300; + +- (CGFloat)normalizedHeight:(CGFloat)height { + return (height - self.minimumHeaderHeight) / (kHeightScalar - self.minimumHeaderHeight); +} + +- (CGFloat)heightDenormalized:(CGFloat)normalized { + return normalized * (kHeightScalar - self.minimumHeaderHeight) + self.minimumHeaderHeight; +} + +- (NSNumber *)valueForField:(FlexibleHeaderConfiguratorField)field { + switch (field) { + case FlexibleHeaderConfiguratorFieldCanOverExtend: + return @(self.fhvc.headerView.canOverExtend); + + case FlexibleHeaderConfiguratorFieldContentImportance: + return @((self.fhvc.headerView.headerContentImportance == MDCFlexibleHeaderContentImportanceHigh)); + + case FlexibleHeaderConfiguratorFieldShiftBehaviorEnabled: { + MDCFlexibleHeaderShiftBehavior behavior = self.fhvc.headerView.shiftBehavior; + BOOL enabled = (behavior == MDCFlexibleHeaderShiftBehaviorEnabled || behavior == MDCFlexibleHeaderShiftBehaviorEnabledWithStatusBar); + return @(enabled); } - } else if (sender == self.exampleView.shiftStatusBarSwitch) { - if (sender.isOn) { - self.exampleView.shiftSwitch.on = YES; + + case FlexibleHeaderConfiguratorFieldShiftBehaviorEnabledWithStatusBar: { + MDCFlexibleHeaderShiftBehavior behavior = self.fhvc.headerView.shiftBehavior; + BOOL enabled = (behavior == MDCFlexibleHeaderShiftBehaviorEnabledWithStatusBar); + return @(enabled); } - self.fhvc.headerView.shiftBehavior = - sender.isOn ? MDCFlexibleHeaderShiftBehaviorEnabled : MDCFlexibleHeaderShiftBehaviorDisabled; - } else if (sender == self.exampleView.infiniteContentSwitch) { - self.fhvc.headerView.inFrontOfInfiniteContent = sender.isOn; - } -} + case FlexibleHeaderConfiguratorFieldInFrontOfInfiniteContent: + return @(self.fhvc.headerView.inFrontOfInfiniteContent); -- (void)didTapButton:(id)button { - [self.navigationController popViewControllerAnimated:YES]; + case FlexibleHeaderConfiguratorFieldMinimumHeight: + return @([self normalizedHeight:self.fhvc.headerView.minimumHeight]); + + case FlexibleHeaderConfiguratorFieldMaximumHeight: + return @([self normalizedHeight:self.fhvc.headerView.maximumHeight]); + } } @end diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorControlItem.h b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorControlItem.h new file mode 100644 index 00000000000..33c2571498b --- /dev/null +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorControlItem.h @@ -0,0 +1,39 @@ +/* + 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. + */ + +/** 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 + +typedef enum : NSUInteger { + FlexibleHeaderConfiguratorControlTypeSwitch, + FlexibleHeaderConfiguratorControlTypeSlider +} FlexibleHeaderConfiguratorControlType; + +@interface FlexibleHeaderConfiguratorControlItem : NSObject + ++ (instancetype)itemWithTitle:(NSString *)title + controlType:(FlexibleHeaderConfiguratorControlType)controlType + field:(NSUInteger)field; + +@property(nonatomic, strong, readonly) NSString *title; +@property(nonatomic, readonly) FlexibleHeaderConfiguratorControlType controlType; +@property(nonatomic, readonly) NSUInteger field; + +@end diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorControlItem.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorControlItem.m new file mode 100644 index 00000000000..333393ece19 --- /dev/null +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorControlItem.m @@ -0,0 +1,36 @@ +/* + 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. + */ + +/** 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 "FlexibleHeaderConfiguratorControlItem.h" + +@implementation FlexibleHeaderConfiguratorControlItem + ++ (instancetype)itemWithTitle:(NSString *)title + controlType:(FlexibleHeaderConfiguratorControlType)controlType + field:(NSUInteger)field { + FlexibleHeaderConfiguratorControlItem *item = [[self alloc] init]; + item->_title = [title copy]; + item->_controlType = controlType; + item->_field = field; + return item; +} + +@end diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.h b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.h index 37a72abe598..5f6b7f45d7a 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.h +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.h @@ -21,39 +21,35 @@ #import -@class ExampleConfigurationsView; @class MDCFlexibleHeaderViewController; -@interface FlexibleHeaderConfiguratorExample : UIViewController +typedef enum : NSUInteger { + FlexibleHeaderConfiguratorFieldCanOverExtend, + FlexibleHeaderConfiguratorFieldInFrontOfInfiniteContent, + FlexibleHeaderConfiguratorFieldContentImportance, + FlexibleHeaderConfiguratorFieldShiftBehaviorEnabled, + FlexibleHeaderConfiguratorFieldShiftBehaviorEnabledWithStatusBar, + FlexibleHeaderConfiguratorFieldMinimumHeight, + FlexibleHeaderConfiguratorFieldMaximumHeight, +} FlexibleHeaderConfiguratorField; -@property(nonatomic) ExampleConfigurationsView *exampleView; -@property(nonatomic) UIScrollView *scrollView; +@interface FlexibleHeaderConfiguratorExample : UITableViewController -- (void)sliderDidSlide:(UISwitch *)sender; -- (void)switchDidToggle:(UISwitch *)sender; +- (NSNumber *)valueForField:(FlexibleHeaderConfiguratorField)field; +- (void)field:(FlexibleHeaderConfiguratorField)field didChangeValue:(NSNumber *)value; -@end +@property(nonatomic) MDCFlexibleHeaderViewController *fhvc; +@property(nonatomic) CGFloat minimumHeaderHeight; -@interface FlexibleHeaderConfiguratorExample (Supplemental) +// Supplemental properties -- (void)setupExampleViews:(MDCFlexibleHeaderViewController *)fhvc; +@property(nonatomic, copy) NSArray *sections; +@property(nonatomic, copy) NSArray *sectionTitles; @end -@interface ExampleConfigurationsView : UIView - -@property(nonatomic) UISlider *minHeightSlider; -@property(nonatomic) UILabel *minHeightSliderLabel; -@property(nonatomic) UISlider *maxHeightSlider; -@property(nonatomic) UILabel *maxHeightSliderLabel; - -@property(nonatomic) UISwitch *overExtendSwitch; -@property(nonatomic) UILabel *overExtendSwitchLabel; -@property(nonatomic) UISwitch *shiftSwitch; -@property(nonatomic) UILabel *shiftSwitchLabel; -@property(nonatomic) UISwitch *shiftStatusBarSwitch; -@property(nonatomic) UILabel *shiftStatusBarSwitchLabel; -@property(nonatomic) UISwitch *infiniteContentSwitch; -@property(nonatomic) UILabel *infiniteContentSwitchLabel; +@interface FlexibleHeaderConfiguratorExample (Supplemental) + +- (void)didChangeValueForField:(FlexibleHeaderConfiguratorField)field animated:(BOOL)animated; @end diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m index 906961c8c12..0d9fdaf5e01 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m @@ -21,8 +21,11 @@ #import "FlexibleHeaderConfiguratorSupplemental.h" +#import "FlexibleHeaderConfiguratorControlItem.h" #import "MaterialFlexibleHeader.h" +static const UITableViewStyle kStyle = UITableViewStyleGrouped; + @implementation FlexibleHeaderConfiguratorExample (CatalogByConvention) + (NSArray *)catalogBreadcrumbs { @@ -37,92 +40,25 @@ - (BOOL)catalogShouldHideNavigation { @implementation FlexibleHeaderConfiguratorExample (Supplemental) -- (void)setupExampleViews:(MDCFlexibleHeaderViewController *)fhvc { - self.scrollView.translatesAutoresizingMaskIntoConstraints = NO; - - NSDictionary *viewBindings = @{ @"scrollView" : self.scrollView }; - NSMutableArray<__kindof NSLayoutConstraint *> *arrayOfConstraints = [NSMutableArray array]; - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"H:|[scrollView]|" - options:0 - metrics:nil - views:viewBindings]]; - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"V:|[scrollView]|" - options:0 - metrics:nil - views:viewBindings]]; - - [self.view addConstraints:arrayOfConstraints]; - - self.exampleView = [[ExampleConfigurationsView alloc] initWithFrame:CGRectZero]; - self.exampleView.translatesAutoresizingMaskIntoConstraints = NO; - [self.scrollView addSubview:self.exampleView]; - - [self.exampleView.overExtendSwitch addTarget:self - action:@selector(switchDidToggle:) - forControlEvents:UIControlEventValueChanged]; - [self.exampleView.shiftSwitch addTarget:self - action:@selector(switchDidToggle:) - forControlEvents:UIControlEventValueChanged]; - [self.exampleView.shiftStatusBarSwitch addTarget:self - action:@selector(switchDidToggle:) - forControlEvents:UIControlEventValueChanged]; - [self.exampleView.infiniteContentSwitch addTarget:self - action:@selector(switchDidToggle:) - forControlEvents:UIControlEventValueChanged]; - [self.exampleView.minHeightSlider addTarget:self - action:@selector(sliderDidSlide:) - forControlEvents:UIControlEventValueChanged]; - [self.exampleView.maxHeightSlider addTarget:self - action:@selector(sliderDidSlide:) - forControlEvents:UIControlEventValueChanged]; - - self.exampleView.minHeightSlider.minimumValue = fhvc.headerView.minimumHeight; - self.exampleView.minHeightSlider.maximumValue = 300; - self.exampleView.maxHeightSlider.minimumValue = fhvc.headerView.maximumHeight; - self.exampleView.maxHeightSlider.maximumValue = 300; - - NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:self.exampleView - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeHeight - multiplier:1 - constant:0]; - - NSLayoutConstraint *centerX = [self.exampleView.centerXAnchor - constraintEqualToAnchor:self.view.centerXAnchor]; - NSLayoutConstraint *top = [self.exampleView.topAnchor - constraintEqualToAnchor:self.scrollView.topAnchor]; - NSLayoutConstraint *bottom = [self.exampleView.bottomAnchor - constraintEqualToAnchor:self.scrollView.bottomAnchor]; - NSLayoutConstraint *leading = [self.exampleView.leadingAnchor - constraintEqualToAnchor:self.scrollView.leadingAnchor]; - NSLayoutConstraint *trailing = [self.exampleView.trailingAnchor - constraintEqualToAnchor:self.scrollView.trailingAnchor]; - - [self.view addConstraints:@[ width, centerX, top, bottom, leading, trailing ]]; - - self.exampleView.overExtendSwitch.on = fhvc.headerView.canOverExtend; - self.exampleView.shiftSwitch.on = - fhvc.headerView.shiftBehavior != MDCFlexibleHeaderShiftBehaviorDisabled; - self.exampleView.shiftStatusBarSwitch.on = - fhvc.headerView.shiftBehavior == MDCFlexibleHeaderShiftBehaviorEnabledWithStatusBar; - self.exampleView.infiniteContentSwitch.on = fhvc.headerView.inFrontOfInfiniteContent; - - self.exampleView.minHeightSlider.value = fhvc.headerView.minimumHeight; - self.exampleView.maxHeightSlider.value = fhvc.headerView.maximumHeight; -} +- (void)commonInit { + self.fhvc = [[MDCFlexibleHeaderViewController alloc] initWithNibName:nil bundle:nil]; + [self addChildViewController:self.fhvc]; -@end + self.title = @"Configurator"; +} -@implementation ExampleConfigurationsView +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + self = [super initWithStyle:kStyle]; + if (self) { + [self commonInit]; + } + return self; +} -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; +- (instancetype)initWithStyle:(UITableViewStyle)style { + self = [super initWithStyle:kStyle]; if (self) { - [self commonExampleConfigurationsViewInit]; + [self commonInit]; } return self; } @@ -130,199 +66,200 @@ - (instancetype)initWithFrame:(CGRect)frame { - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { - [self commonExampleConfigurationsViewInit]; + [self commonInit]; } return self; } -- (void)commonExampleConfigurationsViewInit { - self.overExtendSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; - self.overExtendSwitch.translatesAutoresizingMaskIntoConstraints = NO; - [self addSubview:self.overExtendSwitch]; - - UIFont *font = [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1]; - NSDictionary *textAttributes = @{NSFontAttributeName : font}; - - self.overExtendSwitchLabel = [[UILabel alloc] initWithFrame:CGRectZero]; - self.overExtendSwitchLabel.translatesAutoresizingMaskIntoConstraints = NO; - NSAttributedString *overExtendText = - [[NSAttributedString alloc] initWithString:@"Can Over-Extend" - attributes:textAttributes]; - self.overExtendSwitchLabel.attributedText = overExtendText; - [self addSubview:self.overExtendSwitchLabel]; - - self.shiftSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; - self.shiftSwitch.translatesAutoresizingMaskIntoConstraints = NO; - [self addSubview:self.shiftSwitch]; - - self.shiftSwitchLabel = [[UILabel alloc] initWithFrame:CGRectZero]; - self.shiftSwitchLabel.translatesAutoresizingMaskIntoConstraints = NO; - NSAttributedString *shiftSwitchText = - [[NSAttributedString alloc] initWithString:@"Hides when collapsed" - attributes:textAttributes]; - self.shiftSwitchLabel.attributedText = shiftSwitchText; - [self addSubview:self.shiftSwitchLabel]; - - self.shiftStatusBarSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; - self.shiftStatusBarSwitch.translatesAutoresizingMaskIntoConstraints = NO; - [self addSubview:self.shiftStatusBarSwitch]; - - self.shiftStatusBarSwitchLabel = [[UILabel alloc] initWithFrame:CGRectZero]; - self.shiftStatusBarSwitchLabel.translatesAutoresizingMaskIntoConstraints = NO; - NSAttributedString *shiftStatusBarSwitchText = - [[NSAttributedString alloc] initWithString:@"Hides status bar when collapsed" - attributes:textAttributes]; - self.shiftStatusBarSwitchLabel.attributedText = shiftStatusBarSwitchText; - [self addSubview:self.shiftStatusBarSwitchLabel]; - - self.infiniteContentSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; - self.infiniteContentSwitch.translatesAutoresizingMaskIntoConstraints = NO; - [self addSubview:self.infiniteContentSwitch]; - - self.infiniteContentSwitchLabel = [[UILabel alloc] initWithFrame:CGRectZero]; - self.infiniteContentSwitchLabel.translatesAutoresizingMaskIntoConstraints = NO; - NSAttributedString *infiniteContentSwitchText = - [[NSAttributedString alloc] initWithString:@"In front of infinite content" - attributes:textAttributes]; - self.infiniteContentSwitchLabel.attributedText = infiniteContentSwitchText; - [self addSubview:self.infiniteContentSwitchLabel]; - - self.minHeightSlider = [[UISlider alloc] initWithFrame:CGRectZero]; - self.minHeightSlider.translatesAutoresizingMaskIntoConstraints = NO; - [self addSubview:self.minHeightSlider]; - - self.minHeightSliderLabel = [[UILabel alloc] initWithFrame:CGRectZero]; - self.minHeightSliderLabel.translatesAutoresizingMaskIntoConstraints = NO; - NSAttributedString *minHeightSliderText = - [[NSAttributedString alloc] initWithString:@"Minimum Height" - attributes:textAttributes]; - self.minHeightSliderLabel.attributedText = minHeightSliderText; - [self addSubview:self.minHeightSliderLabel]; - - self.maxHeightSlider = [[UISlider alloc] initWithFrame:CGRectZero]; - self.maxHeightSlider.translatesAutoresizingMaskIntoConstraints = NO; - [self addSubview:self.maxHeightSlider]; - - self.maxHeightSliderLabel = [[UILabel alloc] initWithFrame:CGRectZero]; - self.maxHeightSliderLabel.translatesAutoresizingMaskIntoConstraints = NO; - NSAttributedString *maxHeightSliderText = - [[NSAttributedString alloc] initWithString:@"Maximum Height" - attributes:textAttributes]; - self.maxHeightSliderLabel.attributedText = maxHeightSliderText; - [self addSubview:self.maxHeightSliderLabel]; - - NSDictionary *viewBindings = @{ @"overExtendSwitch" : self.overExtendSwitch, - @"overExtendSwitchLabel" : self.overExtendSwitchLabel, - @"shiftSwitch" : self.shiftSwitch, - @"shiftSwitchLabel" : self.shiftSwitchLabel, - @"shiftStatusBarSwitch" : self.shiftStatusBarSwitch, - @"shiftStatusBarSwitchLabel" : self.shiftStatusBarSwitchLabel, - @"infiniteContentSwitch" : self.infiniteContentSwitch, - @"infiniteContentSwitchLabel" : self.infiniteContentSwitchLabel, - @"minHeightSlider" : self.minHeightSlider, - @"minHeightSliderLabel" : self.minHeightSliderLabel, - @"maxHeightSlider" : self.maxHeightSlider, - @"maxHeightSliderLabel" : self.maxHeightSliderLabel }; - - NSMutableArray<__kindof NSLayoutConstraint *> *arrayOfConstraints = - [NSMutableArray array]; - // clang-format off - [arrayOfConstraints addObjectsFromArray: - [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-8-[overExtendSwitch]" - options:NSLayoutFormatAlignAllCenterX - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"H:|-8-[overExtendSwitchLabel]-[overExtendSwitch]-8-|" - options:NSLayoutFormatAlignAllCenterY - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"V:[overExtendSwitch]-8-[shiftSwitch]" - options:NSLayoutFormatAlignAllCenterX - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"H:|-8-[shiftSwitchLabel]-[shiftSwitch]-8-|" - options:NSLayoutFormatAlignAllCenterY - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"V:[shiftSwitch]-8-[shiftStatusBarSwitch]" - options:NSLayoutFormatAlignAllCenterX - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"H:|-8-[shiftStatusBarSwitchLabel]-[shiftStatusBarSwitch]-8-|" - options:NSLayoutFormatAlignAllCenterY - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"V:[shiftStatusBarSwitch]-8-[infiniteContentSwitch]" - options:NSLayoutFormatAlignAllCenterX - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"H:|-8-[infiniteContentSwitchLabel]-[infiniteContentSwitch]-8-|" - options:NSLayoutFormatAlignAllCenterY - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"V:[infiniteContentSwitchLabel]-16-[minHeightSliderLabel]" - options:0 - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"H:|-8-[minHeightSliderLabel]-8-|" - options:0 - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"V:[minHeightSliderLabel]-8-[minHeightSlider]" - options:0 - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"H:|-8-[minHeightSlider]-8-|" - options:0 - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"V:[minHeightSlider]-8-[maxHeightSliderLabel]" - options:0 - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"H:|-8-[maxHeightSliderLabel]-8-|" - options:0 - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"V:[maxHeightSliderLabel]-8-[maxHeightSlider]" - options:0 - metrics:nil - views:viewBindings]]; - - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"H:|-8-[maxHeightSlider]-8-|" - options:0 - metrics:nil - views:viewBindings]]; - // clang-format on - [self addConstraints:arrayOfConstraints]; +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + [self.navigationController setNavigationBarHidden:YES animated:animated]; +} + +- (UIStatusBarStyle)preferredStatusBarStyle { + return UIStatusBarStyleLightContent; +} + +- (void)viewDidLoad { + self.minimumHeaderHeight = self.fhvc.headerView.minimumHeight; + + self.fhvc.headerView.trackingScrollView = self.tableView; + + self.fhvc.view.frame = self.view.bounds; + [self.view addSubview:self.fhvc.view]; + [self.fhvc didMoveToParentViewController:self]; + + UIColor *lightBlue500 = [UIColor colorWithRed:0.012 + green:0.663 + blue:0.957 + alpha:1]; + self.fhvc.headerView.backgroundColor = lightBlue500; + + UILabel *titleLabel = [[UILabel alloc] init]; + CGRect frame = self.fhvc.headerView.bounds; + frame.origin.y += 20; + frame.size.height -= 20; + titleLabel.frame = frame; + titleLabel.text = self.title; + titleLabel.textColor = [UIColor whiteColor]; + titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleTitle2]; + titleLabel.textAlignment = NSTextAlignmentCenter; + titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin; + + [self.fhvc.headerView addSubview:titleLabel]; + + id (^switchItem)(NSString *, FlexibleHeaderConfiguratorField) = + ^(NSString *title, FlexibleHeaderConfiguratorField field) { + FlexibleHeaderConfiguratorControlType type = FlexibleHeaderConfiguratorControlTypeSwitch; + return [FlexibleHeaderConfiguratorControlItem itemWithTitle:title + controlType:type + field:field]; + }; + id (^sliderItem)(NSString *, FlexibleHeaderConfiguratorField) = + ^(NSString *title, FlexibleHeaderConfiguratorField field) { + FlexibleHeaderConfiguratorControlType type = FlexibleHeaderConfiguratorControlTypeSlider; + return [FlexibleHeaderConfiguratorControlItem itemWithTitle:title + controlType:type + field:field]; + }; + id (^filler)(void) = ^{ + return [NSNull null]; + }; + NSMutableArray *sections = [NSMutableArray array]; + NSMutableArray *sectionTitles = [NSMutableArray array]; + void (^createSection)(NSString *, NSArray *) = ^(NSString *title, NSArray *items) { + [sectionTitles addObject:title ?: @""]; + [sections addObject:items ?: @[]]; + }; + + createSection(@"Swipe right to go back", nil); + + createSection(@"Basic behavior", + @[ + switchItem(@"Can over-extend", + FlexibleHeaderConfiguratorFieldCanOverExtend), + switchItem(@"In front of infinite content", + FlexibleHeaderConfiguratorFieldInFrontOfInfiniteContent), + ]); + + createSection(@"Shift behavior", + @[ switchItem(@"Enabled", + FlexibleHeaderConfiguratorFieldShiftBehaviorEnabled), + switchItem(@"Enabled with status bar", + FlexibleHeaderConfiguratorFieldShiftBehaviorEnabledWithStatusBar), + switchItem(@"Header content is important", + FlexibleHeaderConfiguratorFieldContentImportance) ]); + + createSection(@"Header height", + @[ sliderItem(@"Minimum", + FlexibleHeaderConfiguratorFieldMinimumHeight), + sliderItem(@"Maximum", + FlexibleHeaderConfiguratorFieldMaximumHeight) ]); + + NSMutableArray *fillerItems = [NSMutableArray array]; + for (NSUInteger ix = 0; ix < 100; ++ix) { + [fillerItems addObject:filler()]; + } + createSection(nil, fillerItems); + + self.sections = sections; + self.sectionTitles = sectionTitles; + + self.view.backgroundColor = [UIColor whiteColor]; +} + +- (UIControl *)controlForControlType:(FlexibleHeaderConfiguratorControlType)controlType { + switch (controlType) { + case FlexibleHeaderConfiguratorControlTypeSwitch: + return [[UISwitch alloc] init]; + + case FlexibleHeaderConfiguratorControlTypeSlider: + return [[UISlider alloc] init]; + } +} + +- (NSNumber *)valueForControl:(UIControl *)control { + if ([control isKindOfClass:[UISwitch class]]) { + return @([(UISwitch *)control isOn]); + } else if ([control isKindOfClass:[UISlider class]]) { + return @([(UISlider *)control value]); + } + return nil; +} + +- (void)setValue:(NSNumber *)value onControl:(UIControl *)control animated:(BOOL)animated { + if ([control isKindOfClass:[UISwitch class]]) { + [(UISwitch *)control setOn:[value boolValue] animated:animated]; + } else if ([control isKindOfClass:[UISlider class]]) { + [(UISlider *)control setValue:[value floatValue] animated:animated]; + } +} + +- (void)didChangeValueForField:(FlexibleHeaderConfiguratorField)field animated:(BOOL)animated { + UIControl *control = [self.tableView viewWithTag:field]; + NSNumber *value = [self valueForField:field]; + [self setValue:value onControl:control animated:animated]; +} + +#pragma mark - UITableViewDataSource + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return [self.sections count]; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return [self.sections[section] count]; +} + +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + return self.sectionTitles[section]; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle + reuseIdentifier:@"cell"]; + } + id item = self.sections[indexPath.section][indexPath.row]; + if ([item isKindOfClass:[FlexibleHeaderConfiguratorControlItem class]]) { + FlexibleHeaderConfiguratorControlItem *fieldItem = (FlexibleHeaderConfiguratorControlItem *)item; + + cell.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1]; + + cell.textLabel.text = fieldItem.title; + + UIControl *control = [self controlForControlType:fieldItem.controlType]; + control.tag = fieldItem.field; + [control addTarget:self + action:@selector(didChangeControl:) + forControlEvents:UIControlEventValueChanged]; + [self setValue:[self valueForField:fieldItem.field] onControl:control animated:NO]; + cell.accessoryView = control; + + } else { + cell.textLabel.text = nil; + cell.accessoryView = nil; + } + + return cell; +} + +- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath { + return NO; } + +#pragma mark - User actions + +- (void)didChangeControl:(UIControl *)sender { + NSNumber *value = [self valueForControl:sender]; + [self field:(FlexibleHeaderConfiguratorField)sender.tag didChangeValue:value]; +} + +- (void)didTapBackButton:(id)button { + [self.navigationController popViewControllerAnimated:YES]; +} + @end From 8f1b54a64d1ad0addea34b3c372ac62d37e52664 Mon Sep 17 00:00:00 2001 From: Chris Cox Date: Tue, 19 Apr 2016 15:44:51 -0400 Subject: [PATCH 052/129] [ScrollViewDelegateMultiplexer] Removes component. Now moved to https://github.com/google/GOSScrollViewDelegateMultiplexer. Reviewers: #mdc_ios_owners, junius Reviewed By: #mdc_ios_owners, junius Projects: #material_components_ios Differential Revision: http://codereview.cc/D666 --- MaterialComponents.podspec | 6 - .../ScrollViewDelegateMultiplexer/.jazzy.yaml | 5 - .../ScrollViewDelegateMultiplexer/README.md | 30 --- .../examples/ObservingPageControl.h | 20 -- .../examples/ObservingPageControl.m | 27 -- .../examples/SVDMTypicalUseExample.m | 153 ----------- .../SVDMultiplexer.playground/Contents.swift | 115 --------- .../contents.xcplayground | 4 - .../timeline.xctimeline | 55 ---- .../src/MDCScrollViewDelegateMultiplexer.h | 111 -------- .../src/MDCScrollViewDelegateMultiplexer.m | 243 ------------------ .../MaterialScrollViewDelegateMultiplexer.h | 17 -- ...crollViewDelegateMultiplexerExampleTests.m | 136 ---------- 13 files changed, 922 deletions(-) delete mode 100644 components/ScrollViewDelegateMultiplexer/.jazzy.yaml delete mode 100644 components/ScrollViewDelegateMultiplexer/README.md delete mode 100644 components/ScrollViewDelegateMultiplexer/examples/ObservingPageControl.h delete mode 100644 components/ScrollViewDelegateMultiplexer/examples/ObservingPageControl.m delete mode 100644 components/ScrollViewDelegateMultiplexer/examples/SVDMTypicalUseExample.m delete mode 100644 components/ScrollViewDelegateMultiplexer/examples/SVDMultiplexer.playground/Contents.swift delete mode 100644 components/ScrollViewDelegateMultiplexer/examples/SVDMultiplexer.playground/contents.xcplayground delete mode 100644 components/ScrollViewDelegateMultiplexer/examples/SVDMultiplexer.playground/timeline.xctimeline delete mode 100644 components/ScrollViewDelegateMultiplexer/src/MDCScrollViewDelegateMultiplexer.h delete mode 100644 components/ScrollViewDelegateMultiplexer/src/MDCScrollViewDelegateMultiplexer.m delete mode 100644 components/ScrollViewDelegateMultiplexer/src/MaterialScrollViewDelegateMultiplexer.h delete mode 100644 components/ScrollViewDelegateMultiplexer/tests/unit/ScrollViewDelegateMultiplexerExampleTests.m diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec index a0f9c2bf447..ce511b6e2ac 100644 --- a/MaterialComponents.podspec +++ b/MaterialComponents.podspec @@ -133,12 +133,6 @@ Pod::Spec.new do |s| ss.dependency "MaterialComponents/Typography" end - s.subspec "ScrollViewDelegateMultiplexer" do |ss| - ss.public_header_files = "components/#{ss.base_name}/src/*.h" - ss.source_files = "components/#{ss.base_name}/src/*.{h,m}" - ss.header_mappings_dir = "components/#{ss.base_name}/src/*" - end - s.subspec "ShadowElevations" do |ss| ss.public_header_files = "components/#{ss.base_name}/src/*.h" ss.source_files = "components/#{ss.base_name}/src/*.{h,m}" diff --git a/components/ScrollViewDelegateMultiplexer/.jazzy.yaml b/components/ScrollViewDelegateMultiplexer/.jazzy.yaml deleted file mode 100644 index 758194ab8a2..00000000000 --- a/components/ScrollViewDelegateMultiplexer/.jazzy.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. -module: ScrollViewDelegateMultiplexer -umbrella_header: src/MaterialScrollViewDelegateMultiplexer.h -objc: true -sdk: iphonesimulator diff --git a/components/ScrollViewDelegateMultiplexer/README.md b/components/ScrollViewDelegateMultiplexer/README.md deleted file mode 100644 index 75198afa11f..00000000000 --- a/components/ScrollViewDelegateMultiplexer/README.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "ScrollViewDelegateMultiplexer" -layout: detail -section: components -excerpt: "ScrollViewDelegateMultiplexer acts as a proxy object for UIScrollViewDelegate events and forwards all received events to an ordered list of registered observers." ---- -# ScrollViewDelegateMultiplexer - -This class acts as a proxy object for UIScrollViewDelegate events and forwards all received -events to an ordered list of registered observers. - -> Notice: This component is now available at -> https://github.com/google/GOSScrollViewDelegateMultiplexer and will be removed in a subsequent MDC -> release. - -## Requirements - -- Xcode 7.0 or higher. -- iOS SDK version 7.0 or higher. - -## Usage - -```objectivec -#import "MaterialScrollViewDelegateMultiplexer.h" - -_multiplexer = [[MDCScrollViewDelegateMultiplexer alloc] init]; -myScrollView.delegate = _multiplexer; -[_multiplexer addObservingDelegate:myControl]; -[_multiplexer addObservingDelegate:anotherControl]; -``` diff --git a/components/ScrollViewDelegateMultiplexer/examples/ObservingPageControl.h b/components/ScrollViewDelegateMultiplexer/examples/ObservingPageControl.h deleted file mode 100644 index 8e26ca7ce91..00000000000 --- a/components/ScrollViewDelegateMultiplexer/examples/ObservingPageControl.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - Copyright 2015-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 ObservingPageControl : UIPageControl -@end diff --git a/components/ScrollViewDelegateMultiplexer/examples/ObservingPageControl.m b/components/ScrollViewDelegateMultiplexer/examples/ObservingPageControl.m deleted file mode 100644 index 042ce5e94d3..00000000000 --- a/components/ScrollViewDelegateMultiplexer/examples/ObservingPageControl.m +++ /dev/null @@ -1,27 +0,0 @@ -/* - Copyright 2015-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 "ObservingPageControl.h" - -@implementation ObservingPageControl - -#pragma mark - UIScrollViewDelegate - -- (void)scrollViewDidScroll:(UIScrollView *)scrollView { - self.currentPage = scrollView.contentOffset.x / scrollView.bounds.size.width + 0.5; -} - -@end diff --git a/components/ScrollViewDelegateMultiplexer/examples/SVDMTypicalUseExample.m b/components/ScrollViewDelegateMultiplexer/examples/SVDMTypicalUseExample.m deleted file mode 100644 index 0b9ad1fc884..00000000000 --- a/components/ScrollViewDelegateMultiplexer/examples/SVDMTypicalUseExample.m +++ /dev/null @@ -1,153 +0,0 @@ -/* - Copyright 2015-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 "MaterialScrollViewDelegateMultiplexer.h" -#import "ObservingPageControl.h" - -@interface SVDMTypicalUseViewController : UIViewController -@end - -#define RGBCOLOR(r, g, b) [UIColor colorWithRed:(r) / 255.0f green:(g) / 255.0f blue:(b) / 255.0f alpha:1] -#define HEXCOLOR(hex) RGBCOLOR((((hex) >> 16) & 0xFF), (((hex) >> 8) & 0xFF), ((hex)&0xFF)) - -@implementation SVDMTypicalUseViewController { - UIScrollView *_scrollView; - UIPageControl *_pageControl; - NSArray *_pages; - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - MDCScrollViewDelegateMultiplexer *_multiplexer; -#pragma clang diagnostic pop -} - -+ (NSArray *)catalogBreadcrumbs { - return @[ @"Scroll View Delegate Multiplexer", @"Scroll View Delegate Multiplexer" ]; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - - CGFloat boundsWidth = CGRectGetWidth(self.view.bounds); - CGFloat boundsHeight = CGRectGetHeight(self.view.bounds); - - NSArray *pageColors = @[ HEXCOLOR(0x81D4FA), HEXCOLOR(0x80CBC4), HEXCOLOR(0xFFCC80) ]; - - // Scroll view configuration - - _scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; - _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - _scrollView.pagingEnabled = YES; - _scrollView.contentSize = CGSizeMake(boundsWidth * pageColors.count, boundsHeight); - _scrollView.minimumZoomScale = 0.5; - _scrollView.maximumZoomScale = 6.0; - _scrollView.showsHorizontalScrollIndicator = NO; - - NSMutableArray *pages = [NSMutableArray array]; - - // Add pages to scrollView. - for (NSInteger i = 0; i < pageColors.count; i++) { - CGRect pageFrame = CGRectOffset(self.view.bounds, i * boundsWidth, 0); - UILabel *page = [[UILabel alloc] initWithFrame:pageFrame]; - page.text = [NSString stringWithFormat:@"Page %zd", i + 1]; - page.font = [UIFont systemFontOfSize:50]; - page.textColor = [UIColor colorWithWhite:0 alpha:0.8]; - page.textAlignment = NSTextAlignmentCenter; - page.backgroundColor = pageColors[i]; - page.autoresizingMask = - UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; - [_scrollView addSubview:page]; - [pages addObject:page]; - } - _pages = [pages copy]; - - // Page control configuration - - ObservingPageControl *pageControl = [[ObservingPageControl alloc] init]; - pageControl.numberOfPages = pageColors.count; - - pageControl.pageIndicatorTintColor = [UIColor colorWithWhite:0 alpha:0.2]; - pageControl.currentPageIndicatorTintColor = [UIColor colorWithWhite:0 alpha:0.8]; - - CGSize pageControlSize = [pageControl sizeThatFits:self.view.bounds.size]; - // We want the page control to span the bottom of the screen. - pageControlSize.width = self.view.bounds.size.width; - pageControl.frame = CGRectMake(0, - self.view.bounds.size.height - pageControlSize.height, - self.view.bounds.size.width, - pageControlSize.height); - [pageControl addTarget:self - action:@selector(didChangePage:) - forControlEvents:UIControlEventValueChanged]; - pageControl.defersCurrentPageDisplay = YES; - _pageControl = pageControl; - _pageControl.autoresizingMask = - UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth; - - // Add subviews - - [self.view addSubview:_scrollView]; - [self.view addSubview:pageControl]; - -// Create scrollView delegate multiplexer and register observers - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - _multiplexer = [[MDCScrollViewDelegateMultiplexer alloc] init]; -#pragma clang diagnostic pop - _scrollView.delegate = _multiplexer; - [_multiplexer addObservingDelegate:self]; - [_multiplexer addObservingDelegate:pageControl]; -} - -#pragma mark - Frame changes - -- (void)viewWillLayoutSubviews { - [super viewWillLayoutSubviews]; - NSInteger pageBeforeFrameChange = _pageControl.currentPage; - NSInteger pageCount = _pages.count; - CGFloat boundsWidth = CGRectGetWidth(self.view.bounds); - CGFloat boundsHeight = CGRectGetHeight(self.view.bounds); - for (NSInteger i = 0; i < pageCount; i++) { - UILabel *page = [_pages objectAtIndex:i]; - page.frame = CGRectOffset(self.view.bounds, i * boundsWidth, 0); - } - _scrollView.contentSize = CGSizeMake(boundsWidth * pageCount, boundsHeight); - CGPoint offset = _scrollView.contentOffset; - offset.x = pageBeforeFrameChange * boundsWidth; - // This non-anmiated change of offset ensures we keep the same page - [_scrollView setContentOffset:offset animated:NO]; -} - -#pragma mark - UIScrollViewDelegate - -- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - NSLog(@"%@", NSStringFromSelector(_cmd)); - - [_pageControl updateCurrentPageDisplay]; -} - -#pragma mark - User events - -- (void)didChangePage:(UIPageControl *)sender { - CGPoint offset = _scrollView.contentOffset; - offset.x = sender.currentPage * _scrollView.bounds.size.width; - [_scrollView setContentOffset:offset animated:YES]; -} - -@end diff --git a/components/ScrollViewDelegateMultiplexer/examples/SVDMultiplexer.playground/Contents.swift b/components/ScrollViewDelegateMultiplexer/examples/SVDMultiplexer.playground/Contents.swift deleted file mode 100644 index 195bb547fa9..00000000000 --- a/components/ScrollViewDelegateMultiplexer/examples/SVDMultiplexer.playground/Contents.swift +++ /dev/null @@ -1,115 +0,0 @@ -/*: -# Scroll view delegate multiplexer playground - -This playground explores the use of a UIScrollViewDelegate multiplexer in order to react to UIScrollView events. - -Requirements: **Xcode 7.1 or higher**. - -We've created a framework called MaterialScrollViewDelegateMultiplexer in which we've placed the relevant code. - -If the Playground can't find the framework then you need to initiate a build of the ScrollViewDelegateMultiplexerExample project. Also make sure that you're viewing the playground via Examples.xcworkspace. -*/ -import MaterialScrollViewDelegateMultiplexer - -//: We'll use the XCPlayground framework to show a live view in the Assistant editor. Simply click the Assistant editor icon to view the live view. -import XCPlayground - -/*: -## Create a multiplexer - -The multiplexer object is meant to be assigned as the delegate of a UIScrollView. The delegate property won't hold onto a reference of the multiplexer, so we need to store an instance of the multiplexer elsewhere. - -For the purposes of this Playground we'll store the multiplexer in the global scope. In practice you'd store this multiplexer as a property on your UIViewController. -*/ -let multiplexer = MDCScrollViewDelegateMultiplexer() - -/*: -Let's create a hypothetical object that is interested in reacting to UIScrollViewDelegate events. When it receives a `scrollViewDidScroll:` event it will execute a provided block. -*/ -class ScrollViewListener : NSObject { - init(didScrollCallback: UIScrollView -> Void) { self.didScrollCallback = didScrollCallback } - let didScrollCallback: UIScrollView -> Void -} - -extension ScrollViewListener : UIScrollViewDelegate { - func scrollViewDidScroll(scrollView: UIScrollView) { - self.didScrollCallback(scrollView) - } -} - -/*: -## Register listeners - -We'll create two listeners objects and add both of them to our multiplexer. When the Playground executes you'll be able to verify that each block was invoked while the scroll view scrolls. -*/ -let listener1 = ScrollViewListener { scrollView in - scrollView.contentOffset.y -//: Without the following return statement, Swift will treat the line above as an implicit return statement and we won't be able to see the Playground graph. - return -} - -let listener2 = ScrollViewListener { scrollView in - scrollView.contentOffset.y - return -} - -//: We can now register each listener instance to the multiplexer. Our multiplexer is now ready to be assigned as the delegate of a UIScrollView. -multiplexer.addObservingDelegate(listener1) -multiplexer.addObservingDelegate(listener2) - -/*: -## The view controller - -We'll use the Playground live view feature to show a subclass of UIViewController we've created for this playground. -*/ -class PlaygroundViewController : UIViewController { - var scrollView: UIScrollView? -} - -//: Assigning our view controller as the live view places will cause our view controller's view to show up in the Assistant editor. -XCPlaygroundPage.currentPage.liveView = PlaygroundViewController() - -extension PlaygroundViewController { - - override func viewDidLoad() { - super.viewDidLoad() - self.view.backgroundColor = UIColor.whiteColor() - - let width = self.view.bounds.size.width - - scrollView = UIScrollView(frame: self.view.bounds) - -//: We can now assign the multiplexer as the scroll view's delegate. - scrollView!.delegate = multiplexer - -//: The rest of this logic is straightforward UIKit view creation and manipulation. - scrollView!.contentSize = CGSize(width: width, height: self.view.bounds.height * 100) - self.view.addSubview(scrollView!) - - for _ in 0..<15 { - let square = UIView(frame: CGRect( - x: drand48() * Double(width - 30), - y: drand48() * 1000, - width: 30, - height: 30) - ) - square.backgroundColor = UIColor.redColor().colorWithAlphaComponent(0.5) - - scrollView!.addSubview(square) - } - } - - override func viewDidAppear(animated: Bool) { - super.viewDidAppear(animated) - - scrollView?.setContentOffset(CGPoint(x: 0, y: 400), animated: true) - } -} - -//: # Playground logic - -extension ScrollViewListener { - func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) { - XCPlaygroundPage.currentPage.finishExecution() - } -} diff --git a/components/ScrollViewDelegateMultiplexer/examples/SVDMultiplexer.playground/contents.xcplayground b/components/ScrollViewDelegateMultiplexer/examples/SVDMultiplexer.playground/contents.xcplayground deleted file mode 100644 index 73b0de178c2..00000000000 --- a/components/ScrollViewDelegateMultiplexer/examples/SVDMultiplexer.playground/contents.xcplayground +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/components/ScrollViewDelegateMultiplexer/examples/SVDMultiplexer.playground/timeline.xctimeline b/components/ScrollViewDelegateMultiplexer/examples/SVDMultiplexer.playground/timeline.xctimeline deleted file mode 100644 index 9ec1a58f8fb..00000000000 --- a/components/ScrollViewDelegateMultiplexer/examples/SVDMultiplexer.playground/timeline.xctimeline +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/components/ScrollViewDelegateMultiplexer/src/MDCScrollViewDelegateMultiplexer.h b/components/ScrollViewDelegateMultiplexer/src/MDCScrollViewDelegateMultiplexer.h deleted file mode 100644 index 111ce5d802c..00000000000 --- a/components/ScrollViewDelegateMultiplexer/src/MDCScrollViewDelegateMultiplexer.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - Copyright 2015-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 - -@protocol MDCScrollViewDelegateCombining; - -/** - MDCScrollViewDelegateMultiplexer acts as a proxy object for UIScrollViewDelegate events and - forwards all received events to an ordered list of registered observers. - - When a UIScrollViewDelegate method invocation is received by the multiplexer, the multiplexer - forwards the invocation to each observer in order of registration. - - If the scroll view method signature has a return value, after all delegate method invocations, - the return value will be provided by the first observing delegate that responded. If no - observers implement the method, a default return value will be used. - - However, if a combiner is set and the receiving class conforms to the - MDCScrollViewDelegateCombining protocol, then the receiving class can designate which result - value to return via the use of the optional protocol methods. - - Example implementation: - - _multiplexer = [[MDCScrollViewDelegateMultiplexer alloc] init]; - myScrollView.delegate = _multiplexer; - [_multiplexer addObservingDelegate:myControl]; - [_multiplexer addObservingDelegate:anotherControl]; - */ -// clang-format off -__deprecated_msg("This component is now available at https://github.com/google/GOSScrollViewDelegateMultiplexer.") -@interface MDCScrollViewDelegateMultiplexer : NSObject - -/** - Adds an observing delegate to the end of the array of delegates. - - @param delegate The observing delegate to be added. - */ -- (void)addObservingDelegate : (nonnull id)delegate; - -/** - Removes an observing delegate from the array of delegates. - - @param delegate The observing delegate to be removed. - */ -- (void)removeObservingDelegate:(nonnull id)delegate; - -/** - An optional delegate through which the MDCScrollViewDelegateMultiplexer may provide a - single UIScrollViewDelegate protocol return value from its array of observing delegates. - */ -@property(nonatomic, weak, null_resettable) id combiner; - -@end - -/** - The MDCScrollViewDelegateCombining protocol defines an internal mechanism through which - MDCScrollViewDelegateMultiplexer provides an array of responding observing delegates - and their respective return values. - - Since it is possible that multiple delegates may respond to UIScrollViewDelegate methods that - provide return values, this protocol allows the receiver to select the specific value to return - from an array of those responding result values. - */ -__deprecated_msg("This component is now available at https://github.com/google/GOSScrollViewDelegateMultiplexer.") -@protocol MDCScrollViewDelegateCombining @optional - -/** - Allows the receiver to return the preferred UIView result from observer delegates that have - responded to the UIScrollViewDelegate -viewForZoomingInScrollView method. - - @param multiplexer The scrollView delegate multiplexer. - @param results A pointer array of UIView instances returned by responding observer delegates. - NSPointerArray here to allow nil results from -viewForZoomingInScrollView. - @param respondingObservers An array of observing delegates that responded. - - @return The preferred UIView result for this method. - */ -- (nullable UIView *)scrollViewDelegateMultiplexer:(nonnull MDCScrollViewDelegateMultiplexer *)multiplexer - viewForZoomingWithResults:(nonnull NSPointerArray *)results - fromRespondingObservers:(nonnull NSArray *)respondingObservers; - -/** - Allows the receiver to return the preferred BOOL result from observer delegates that have - responded to the UIScrollViewDelegate -scrollViewShouldScrollToTop method. - - @param multiplexer The scrollView delegate multiplexer. - @param results An array of NSNumber instances returned by responding observer delegates. - @param respondingObservers An array of observing delegates that responded. - - @return The preferred BOOL result for this method. - */ -- (BOOL)scrollViewDelegateMultiplexer:(nonnull MDCScrollViewDelegateMultiplexer *)multiplexer - shouldScrollToTopWithResults:(nonnull NSArray *)results - fromRespondingObservers:(nonnull NSArray *)respondingObservers; - -@end - // clang-format on diff --git a/components/ScrollViewDelegateMultiplexer/src/MDCScrollViewDelegateMultiplexer.m b/components/ScrollViewDelegateMultiplexer/src/MDCScrollViewDelegateMultiplexer.m deleted file mode 100644 index 4bf751ef5b4..00000000000 --- a/components/ScrollViewDelegateMultiplexer/src/MDCScrollViewDelegateMultiplexer.m +++ /dev/null @@ -1,243 +0,0 @@ -/* - Copyright 2015-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 "MDCScrollViewDelegateMultiplexer.h" - -#import - -@implementation MDCScrollViewDelegateMultiplexer { - NSPointerArray *_observingDelegates; -} - -- (instancetype)init { - self = [super init]; - if (self) { - NSPointerFunctionsOptions options = - (NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPointerPersonality); - _observingDelegates = [NSPointerArray pointerArrayWithOptions:options]; - } - return self; -} - -- (void)addObservingDelegate:(id)delegate { - [_observingDelegates addPointer:(__bridge void *)(delegate)]; -} - -- (void)removeObservingDelegate:(id)delegate { - for (NSUInteger i = 0; i < _observingDelegates.count; i++) { - if ([_observingDelegates pointerAtIndex:i] == (__bridge void *)(delegate)) { - [_observingDelegates removePointerAtIndex:i]; - } - } -} - -- (void)setCombiner:(id)combiner { - _combiner = combiner; -} - -#pragma mark - NSObject - -- (BOOL)shouldForwardSelector:(SEL)selector { - // Check optional methods. - struct objc_method_description description = - protocol_getMethodDescription(@protocol(UIScrollViewDelegate), selector, NO, YES); - return (description.name != NULL && description.types != NULL); -} - -- (BOOL)respondsToSelector:(SEL)aSelector { - if ([super respondsToSelector:aSelector]) { - return YES; - } else if ([self shouldForwardSelector:aSelector]) { - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:aSelector]) { - return YES; - } - } - } - return NO; -} - -#pragma mark - UIScrollViewDelegate Forwarding - -- (void)scrollViewDidScroll:(UIScrollView *)scrollView { - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - [delegate scrollViewDidScroll:scrollView]; - } - } -} - -- (void)scrollViewDidZoom:(UIScrollView *)scrollView { - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - [delegate scrollViewDidZoom:scrollView]; - } - } -} - -- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - [delegate scrollViewWillBeginDragging:scrollView]; - } - } -} - -- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView - withVelocity:(CGPoint)velocity - targetContentOffset:(inout CGPoint *)targetContentOffset { - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - [delegate scrollViewWillEndDragging:scrollView - withVelocity:velocity - targetContentOffset:targetContentOffset]; - } - } -} - -- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - [delegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; - } - } -} - -- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView { - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - [delegate scrollViewWillBeginDecelerating:scrollView]; - } - } -} - -- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - [delegate scrollViewDidEndDecelerating:scrollView]; - } - } -} - -- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - [delegate scrollViewDidEndScrollingAnimation:scrollView]; - } - } -} - -- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { - // NSPointerArray here to allow nil results from -viewForZoomingInScrollView. - NSPointerArray *results = - [NSPointerArray pointerArrayWithOptions:(NSPointerFunctionsStrongMemory | - NSPointerFunctionsObjectPointerPersonality)]; - NSMutableArray *respondingObservers = [NSMutableArray array]; - - // Execute this method for every responding observer. - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - UIView *result = [delegate viewForZoomingInScrollView:scrollView]; - [results addPointer:(__bridge void *)(result)]; - [respondingObservers addObject:delegate]; - } - } - - if ([_combiner respondsToSelector:@selector(scrollViewDelegateMultiplexer: - viewForZoomingWithResults: - fromRespondingObservers:)]) { - return [_combiner scrollViewDelegateMultiplexer:(id)self - viewForZoomingWithResults:results - fromRespondingObservers:respondingObservers]; - } else if (results.count > 0) { -#if DEBUG - NSHashTable *hash = [NSHashTable weakObjectsHashTable]; - for (UIView *view in results) { - [hash addObject:view ? view : [NSNull null]]; - } - NSAssert(hash.count == 1, - @"-viewForZoomingInScrollView returns different results from multiple observers." - " Use the combiner protocol MDCScrollViewDelegateCombining to select the appropriate" - " observer return value for this method."); -#endif - - return [results pointerAtIndex:0]; - } - - return nil; -} - -- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView - withView:(UIView *)view { - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - [delegate scrollViewWillBeginZooming:scrollView withView:view]; - } - } -} - -- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView - withView:(UIView *)view - atScale:(CGFloat)scale { - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - [delegate scrollViewDidEndZooming:scrollView withView:view atScale:scale]; - } - } -} - -- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView { - NSMutableArray *results = [NSMutableArray array]; - NSMutableArray *respondingObservers = [NSMutableArray array]; - - // Execute this method for every responding observer. - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - [results addObject:@([delegate scrollViewShouldScrollToTop:scrollView])]; - [respondingObservers addObject:delegate]; - } - } - - if ([_combiner respondsToSelector:@selector(scrollViewDelegateMultiplexer: - shouldScrollToTopWithResults: - fromRespondingObservers:)]) { - return [_combiner scrollViewDelegateMultiplexer:(id)self - shouldScrollToTopWithResults:results - fromRespondingObservers:respondingObservers]; - } else if (results.count > 0) { -#if DEBUG - NSSet *set = [NSSet setWithArray:results]; - NSAssert(set.count == 1, - @"-scrollViewShouldScrollToTop returns different results from multiple observers." - " Use the combiner protocol MDCScrollViewDelegateCombining to select the appropriate" - " observer return value for this method."); -#endif - - return [results[0] boolValue]; - } - - return YES; -} - -- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView { - for (id delegate in _observingDelegates) { - if ([delegate respondsToSelector:_cmd]) { - [delegate scrollViewDidScrollToTop:scrollView]; - } - } -} - -@end diff --git a/components/ScrollViewDelegateMultiplexer/src/MaterialScrollViewDelegateMultiplexer.h b/components/ScrollViewDelegateMultiplexer/src/MaterialScrollViewDelegateMultiplexer.h deleted file mode 100644 index e3c9af1b0e5..00000000000 --- a/components/ScrollViewDelegateMultiplexer/src/MaterialScrollViewDelegateMultiplexer.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - Copyright 2015-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 "MDCScrollViewDelegateMultiplexer.h" diff --git a/components/ScrollViewDelegateMultiplexer/tests/unit/ScrollViewDelegateMultiplexerExampleTests.m b/components/ScrollViewDelegateMultiplexer/tests/unit/ScrollViewDelegateMultiplexerExampleTests.m deleted file mode 100644 index 334b4c1d223..00000000000 --- a/components/ScrollViewDelegateMultiplexer/tests/unit/ScrollViewDelegateMultiplexerExampleTests.m +++ /dev/null @@ -1,136 +0,0 @@ -/* - Copyright 2015-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 "MaterialScrollViewDelegateMultiplexer.h" - -static NSString *const kScrollViewDidScroll = @"scrollViewDidScroll"; - -#pragma mark - Simple observer object - -/** Simple object that conforms to UIScrollViewDelegate protocol. */ -@interface ScrollObservingObject : UIView -- (instancetype)initWithExpectation:(XCTestExpectation *)expectation; -@end - -@implementation ScrollObservingObject { - XCTestExpectation *_observerExpectation; -} - -- (instancetype)initWithExpectation:(XCTestExpectation *)expectation { - self = [self initWithFrame:CGRectZero]; - if (self) { - _observerExpectation = expectation; - } - return self; -} - -- (void)scrollViewDidScroll:(UIScrollView *)scrollView { - [_observerExpectation fulfill]; -} - -@end - -@interface ScrollViewDelegateMultiplexerExampleTests : XCTestCase -@end - -@implementation ScrollViewDelegateMultiplexerExampleTests { - UIScrollView *_scrollView; - ScrollObservingObject *_observingObject; - MDCScrollViewDelegateMultiplexer *_multiplexer; - XCTestExpectation *_expectation; - XCTestExpectation *_observerExpectation; -} - -- (void)setUp { - [super setUp]; - _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 100, 10)]; - _scrollView.contentSize = CGSizeMake(200, 10); -} - -- (void)tearDown { - [super tearDown]; -} - -#pragma mark - Tests - -- (void)testWithoutMultiplexer { - _expectation = [self expectationWithDescription:kScrollViewDidScroll]; - - _scrollView.delegate = self; - - // Instigate event. - [_scrollView setContentOffset:CGPointMake(50, 0) animated:YES]; - - [self waitForExpectationsWithTimeout:0 - handler:^(NSError *error) { - XCTAssertEqual(error, nil); - }]; -} - -- (void)testMuliplexerSingleDelegate { - _expectation = [self expectationWithDescription:kScrollViewDidScroll]; - - // Create scrollView delegate multiplexer. - _multiplexer = [[MDCScrollViewDelegateMultiplexer alloc] init]; - [_multiplexer addObservingDelegate:self]; - _scrollView.delegate = _multiplexer; - - // Instigate event. - [_scrollView setContentOffset:CGPointMake(50, 0) animated:YES]; - - [self waitForExpectationsWithTimeout:0 - handler:^(NSError *error) { - XCTAssertEqual(error, nil); - }]; -} - -- (void)testMuliplexerMultipleDelegate { - _expectation = [self expectationWithDescription:kScrollViewDidScroll]; - _observerExpectation = [self expectationWithDescription:kScrollViewDidScroll]; - - // Create simple object - _observingObject = [[ScrollObservingObject alloc] initWithExpectation:_observerExpectation]; - - // Create scrollView delegate multiplexer. - _multiplexer = [[MDCScrollViewDelegateMultiplexer alloc] init]; - [_multiplexer addObservingDelegate:self]; - [_multiplexer addObservingDelegate:_observingObject]; - _scrollView.delegate = _multiplexer; - - // Instigate event. - [_scrollView setContentOffset:CGPointMake(50, 0) animated:YES]; - - [self waitForExpectationsWithTimeout:0 - handler:^(NSError *error) { - XCTAssertEqual(error, nil); - - [self waitForExpectationsWithTimeout:0 - handler:^(NSError *error2) { - XCTAssertEqual(error2, nil); - }]; - - }]; -} - -#pragma mark - UIScrollViewDelegate - -- (void)scrollViewDidScroll:(UIScrollView *)scrollView { - [_expectation fulfill]; -} - -@end From 009ea9bb22d3543943843cd14feeac524d88c04f Mon Sep 17 00:00:00 2001 From: Adrian Secord Date: Tue, 19 Apr 2016 16:17:48 -0400 Subject: [PATCH 053/129] [Website] Moved versions into How To > Build Environment. Reviewers: featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D662 --- .../contributor_guides/supported_versions.md | 16 ---------------- howto/README.md | 6 ++++-- howto/build-env/README.md | 12 ++++++++++++ 3 files changed, 16 insertions(+), 18 deletions(-) delete mode 100644 contributing/contributor_guides/supported_versions.md diff --git a/contributing/contributor_guides/supported_versions.md b/contributing/contributor_guides/supported_versions.md deleted file mode 100644 index 2b5aa5b362a..00000000000 --- a/contributing/contributor_guides/supported_versions.md +++ /dev/null @@ -1,16 +0,0 @@ -# Supported versions - -The purpose of this document is to clearly communicate which versions of specific technologes the -core team uses to build Material components. - -## Xcode - -The core team uses **Xcode 7.2.1** and supports compilation of both Swift and Objective-C code to -the corresponding language versions. - -New Swift language features will be introduced in examples only once the team moves to a new version -of Xcode. - -## iOS - -All components are expected to support **iOS 7.0 and above**. diff --git a/howto/README.md b/howto/README.md index a0e64f2caae..5b45e024df8 100644 --- a/howto/README.md +++ b/howto/README.md @@ -6,11 +6,13 @@ section: howto # How to use Material Components -TODO Intro text +Material Components iOS should be immediately useable out of the box with +Apple's standard development tool chain. -- [Development Guide](/howto/tutorial/) +- [Tutorial](/howto/tutorial/) - [Build environment](/howto/build-env/) + diff --git a/howto/build-env/README.md b/howto/build-env/README.md index cb4b259dcd7..1919f21377f 100644 --- a/howto/build-env/README.md +++ b/howto/build-env/README.md @@ -30,3 +30,15 @@ errors to allow the normal deprecation process to work: -Wno-error=deprecated -Wno-error=deprecated-declarations + +## Supported versions + +### Xcode + +The core team uses **Xcode 7.2.1**, which corresponds to Swift 2.1. Once Swift +has stablized we will track particular Swift versions independently of Xcode +versions. + +### iOS + +All components are expected to support **iOS 7.0 and above**. From adba94e106b4e576160f15380f97f720800ec373 Mon Sep 17 00:00:00 2001 From: Ian Gordon Date: Tue, 19 Apr 2016 16:15:30 -0400 Subject: [PATCH 054/129] [Shadow] Add swift examples to the documentation Reviewers: randallli, ajsecord, #mdc_ios_owners Reviewed By: ajsecord, #mdc_ios_owners Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D667 --- components/ShadowLayer/README.md | 63 +++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/components/ShadowLayer/README.md b/components/ShadowLayer/README.md index 3ef052ffa9d..7de7c1ab317 100644 --- a/components/ShadowLayer/README.md +++ b/components/ShadowLayer/README.md @@ -92,9 +92,11 @@ Before using Shadow Layer, you'll need to import it: ~~~ #### Swift + ~~~ swift import MaterialComponents ~~~ + @@ -116,12 +118,25 @@ Example of a custom button based on UIButton with Material Design shadows: @end ~~~ + +### Swift +~~~ swift +class ShadowButton: UIButton { + + override class func layerClass() -> AnyClass { + return MDCShadowLayer.self + } + +} +~~~ + Add the custom button to view: + ### Objective C ~~~ objc ShadowButton *button = [ShadowButton buttonWithType:UIButtonTypeSystem]; @@ -131,13 +146,47 @@ button.frame = CGRectMake(100, 100, 200, 50); [self.view addSubview:button]; ~~~ + +### Swift +~~~ swift +let button: ShadowButton = ShadowButton.init(type: UIButtonType.System) +button.frame = CGRect(x: 100, y: 100, width: 200, height: 50) +button.setTitle("Button", forState: UIControlState.Normal) +(button.layer as! MDCShadowLayer).setElevation(6.0) +self.addSubview(button) + +~~~ + Creating a custom UIView with a shadow: -#### Swift + +### Objective C +~~~ objc +@interface ShadowedView : UIView +@end + +@implementation ShadowedView + ++ (Class)layerClass { + return [MDCShadowLayer class]; +} + +- (MDCShadowLayer)shadowLayer { + return (MDCShadowLayer *)self.layer; +} + +- (void)setElevation:(CGFloat)points { + [(MDCShadowLayer *)self.layer setElevation:points]; +} + +@end +~~~ + +### Swift ~~~ swift class ShadowedView: UIView { @@ -154,8 +203,8 @@ class ShadowedView: UIView { } } - ~~~ + @@ -163,6 +212,7 @@ To improve performance, consider rasterizing MDCShadowLayer when the view using animating or changing size. + ### Objective C ~~~ objc @@ -170,6 +220,15 @@ self.layer.shouldRasterize = YES; self.layer.rasterizationScale = [UIScreen mainScreen].scale; ~~~ + +### Swift +~~~ swift + +self.layer.shouldRasterize = true; +self.layer.rasterizationScale = UIScreen.mainScreen().scale + +~~~ + Disable rasterization before animating MDCShadowLayer. From 49af52411077d5587ccc4bcb4019ac5ece4a55fe Mon Sep 17 00:00:00 2001 From: Ian Gordon Date: Tue, 19 Apr 2016 16:40:20 -0400 Subject: [PATCH 055/129] [ShadowElevations] Update documention with Objective C examples Reviewers: ajsecord, #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Subscribers: featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D670 --- components/ShadowElevations/README.md | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/components/ShadowElevations/README.md b/components/ShadowElevations/README.md index df275cb7782..431f2dd3883 100644 --- a/components/ShadowElevations/README.md +++ b/components/ShadowElevations/README.md @@ -56,7 +56,6 @@ Before using Shadow Elevations, you'll need to import it: #### Objective-C - ~~~ objc #import "MaterialShadowElevations.h" ~~~ @@ -65,9 +64,32 @@ Before using Shadow Elevations, you'll need to import it: ~~~ swift import MaterialComponents ~~~ + +#### Objective-C +~~~ objc +@interface ShadowedView: UIView + +@end + +@implementation ShadowedView + ++ (Class)layerClass { + return [MDCShadowLayer class]; +} + +- (MDCShadowLayer *)shadowLayer { + return (MDCShadowLayer *)self.layer; +} + +- (void)setDefaultElevation { + self.shadowLayer.elevation = MDCShadowElevationCardResting; +} + +@end +~~~ #### Swift ~~~ swift @@ -81,7 +103,7 @@ class ShadowedView: UIView { return self.layer as! MDCShadowLayer } - func setElevation(points: CGFloat) { + func setDefaultElevation() { self.shadowLayer.elevation = MDCShadowElevationCardResting } From 0dc8870271222083a2a4dbbf55dd249a5b5cd4f5 Mon Sep 17 00:00:00 2001 From: randallli Date: Tue, 19 Apr 2016 17:45:16 -0400 Subject: [PATCH 056/129] [AppBar] Typical Use Example moving logic from init into viewDidLoad Reviewers: ajsecord, featherless, #mdc_ios_owners Reviewed By: ajsecord, featherless, #mdc_ios_owners Subscribers: featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D606 --- .../AppBar/examples/AppBarTypicalUseExample.m | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/components/AppBar/examples/AppBarTypicalUseExample.m b/components/AppBar/examples/AppBarTypicalUseExample.m index 58ce3ae1f9a..7536378bda8 100644 --- a/components/AppBar/examples/AppBarTypicalUseExample.m +++ b/components/AppBar/examples/AppBarTypicalUseExample.m @@ -32,16 +32,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil if (self) { // Step 2: Initialize the App Bar and add the headerViewController as a child. _appBar = [[MDCAppBar alloc] init]; - _appBar.navigationBar.tintColor = [UIColor whiteColor]; [self addChildViewController:_appBar.headerViewController]; - self.title = @"App Bar"; - - UIColor *color = [UIColor colorWithRed:(CGFloat)0x03 / (CGFloat)255 - green:(CGFloat)0xA9 / (CGFloat)255 - blue:(CGFloat)0xF4 / (CGFloat)255 - alpha:1]; - _appBar.headerViewController.headerView.backgroundColor = color; } return self; } @@ -60,6 +52,14 @@ - (void)viewDidLoad { // Step 3: Register the App Bar views. [self.appBar addSubviewsToParent]; + // Optional: Change the App Bar's background color and tint color. + UIColor *color = [UIColor colorWithRed:(CGFloat)0x03 / (CGFloat)255 + green:(CGFloat)0xA9 / (CGFloat)255 + blue:(CGFloat)0xF4 / (CGFloat)255 + alpha:1]; + _appBar.headerViewController.headerView.backgroundColor = color; + _appBar.navigationBar.tintColor = [UIColor whiteColor]; + self.tableView.layoutMargins = UIEdgeInsetsZero; self.tableView.separatorInset = UIEdgeInsetsZero; } From ebd1fbd14c6d2b0c052e28a9bc3cf59a6e01e75e Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Tue, 19 Apr 2016 18:00:00 -0400 Subject: [PATCH 057/129] [Catalog] Make example titles consistent, use Component Name Reviewers: iangordon, featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D674 --- .../examples/AppBarTypicalUseExample.swift | 2 +- .../examples/ButtonBarTypicalUseExample.m | 2 +- .../examples/ButtonBarTypicalUseExample.swift | 4 ++-- .../FlexibleHeaderTypicalUseSupplemental.m | 22 +++++++++---------- ...vigationBarTypicalUseExampleSupplemental.m | 22 +++++++++---------- ...tionBarTypicalUseExampleSupplemental.swift | 2 +- .../SwitchTypicalUseSupplemental.m | 2 +- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/components/AppBar/examples/AppBarTypicalUseExample.swift b/components/AppBar/examples/AppBarTypicalUseExample.swift index 7cd29e4d227..c3e94989a75 100644 --- a/components/AppBar/examples/AppBarTypicalUseExample.swift +++ b/components/AppBar/examples/AppBarTypicalUseExample.swift @@ -85,7 +85,7 @@ class AppBarTypicalUseSwiftExample: UITableViewController { // MARK: Catalog by convention extension AppBarTypicalUseSwiftExample { class func catalogBreadcrumbs() -> [String] { - return ["App Bar", "Basic (Swift)"] + return ["App Bar", "App Bar (Swift)"] } func catalogShouldHideNavigation() -> Bool { return true diff --git a/components/ButtonBar/examples/ButtonBarTypicalUseExample.m b/components/ButtonBar/examples/ButtonBarTypicalUseExample.m index 7e18486334e..e2b82fc5cc1 100644 --- a/components/ButtonBar/examples/ButtonBarTypicalUseExample.m +++ b/components/ButtonBar/examples/ButtonBarTypicalUseExample.m @@ -110,7 +110,7 @@ @implementation ButtonBarTypicalUseExample (GeneralApplicationLogic) - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { - self.title = @"Typical use"; + self.title = @"Button Bar"; } return self; } diff --git a/components/ButtonBar/examples/ButtonBarTypicalUseExample.swift b/components/ButtonBar/examples/ButtonBarTypicalUseExample.swift index 73245d2a9f9..d033b9359b9 100644 --- a/components/ButtonBar/examples/ButtonBarTypicalUseExample.swift +++ b/components/ButtonBar/examples/ButtonBarTypicalUseExample.swift @@ -73,7 +73,7 @@ class ButtonBarTypicalUseSwiftExample: UIViewController { override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) - self.title = "Typical use" + self.title = "Button Bar" } required init?(coder aDecoder: NSCoder) { @@ -84,7 +84,7 @@ class ButtonBarTypicalUseSwiftExample: UIViewController { // MARK: Catalog by convention extension ButtonBarTypicalUseSwiftExample { class func catalogBreadcrumbs() -> [String] { - return ["Button Bar", "Demo (Swift)"] + return ["Button Bar", "Button Bar (Swift)"] } } diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m index c0c6298b19c..e8f3850650d 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m @@ -32,7 +32,7 @@ @interface ExampleInstructionsViewFlexibleHeaderTypicalUse : UIView @implementation FlexibleHeaderTypicalUseViewController (CatalogByConvention) + (NSArray *)catalogBreadcrumbs { - return @[ @"Flexible Header", @"Typical use" ]; + return @[ @"Flexible Header", @"Flexible Header" ]; } - (BOOL)catalogShouldHideNavigation { @@ -139,18 +139,18 @@ - (NSAttributedString *)instructionsString { NSDictionary *instructionAttributes1 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSDictionary *instructionAttributes2 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSString *instructionText1 = @"PULL DOWN\n\nMDCFlexibleHeaderViewController\nallows the\ blue area to stretch\nwhen scrolled down.\n\n\n\n\n\n"; diff --git a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.m b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.m index e28dd472f7c..c7a40ce83e3 100644 --- a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.m +++ b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.m @@ -60,7 +60,7 @@ - (void)setupExampleViews { @implementation NavigationBarTypicalUseExample (CatalogByConvention) + (NSArray *)catalogBreadcrumbs { - return @[ @"Navigation Bar", @"Typical use" ]; + return @[ @"Navigation Bar", @"Navigation Bar" ]; } - (BOOL)catalogShouldHideNavigation { @@ -117,19 +117,19 @@ - (NSAttributedString *)instructionsString { NSDictionary *instructionAttributes1 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSDictionary *instructionAttributes2 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSString *instructionText = @"SWIPE RIGHT\n\n\n\nto go back\n\n\n\n\n\n"; NSMutableAttributedString *instructionsAttributedString = [[NSMutableAttributedString alloc] diff --git a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.swift b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.swift index d1f30842d10..3c0e760d2d8 100644 --- a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.swift +++ b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.swift @@ -27,7 +27,7 @@ extension NavigationBarTypicalUseSwiftExample { // (CatalogByConvention) class func catalogBreadcrumbs() -> [String] { - return [ "Navigation Bar", "Typical use (Swift)" ] + return [ "Navigation Bar", "Navigation Bar (Swift)" ] } class func catalogDescription() -> String { diff --git a/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m b/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m index f1c1c219039..1a66b8b5ad7 100644 --- a/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m +++ b/components/Switch/examples/supplemental/SwitchTypicalUseSupplemental.m @@ -14,7 +14,7 @@ @implementation SwitchTypicalUseViewController (CatalogByConvention) + (NSArray *)catalogBreadcrumbs { - return @[ @"Switch", @"Typical use" ]; + return @[ @"Switch", @"Switch" ]; } + (NSString *)catalogDescription { From effc515801088d01d3e6b91164aba229c02af829 Mon Sep 17 00:00:00 2001 From: Ian Gordon Date: Tue, 19 Apr 2016 18:40:03 -0400 Subject: [PATCH 058/129] [ScrollViewMultiplexer] Regen Podlock files Reviewers: featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D677 --- catalog/Podfile.lock | 4 +--- demos/Pesto/Podfile.lock | 4 +--- demos/Shrine/Podfile.lock | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/catalog/Podfile.lock b/catalog/Podfile.lock index 9f4155ef1b1..2b92aab9408 100644 --- a/catalog/Podfile.lock +++ b/catalog/Podfile.lock @@ -11,7 +11,6 @@ PODS: - MaterialComponents/PageControl (= 4.0.1) - MaterialComponents/private (= 4.0.1) - MaterialComponents/RobotoFontLoader (= 4.0.1) - - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.0.1) - MaterialComponents/ShadowElevations (= 4.0.1) - MaterialComponents/ShadowLayer (= 4.0.1) - MaterialComponents/Slider (= 4.0.1) @@ -60,7 +59,6 @@ PODS: - MaterialComponents/RobotoFontLoader (4.0.1): - MaterialComponents/FontDiskLoader - MaterialComponents/Typography - - MaterialComponents/ScrollViewDelegateMultiplexer (4.0.1) - MaterialComponents/ShadowElevations (4.0.1) - MaterialComponents/ShadowLayer (4.0.1) - MaterialComponents/Slider (4.0.1): @@ -88,7 +86,7 @@ EXTERNAL SOURCES: :path: ../ SPEC CHECKSUMS: - MaterialComponents: 0f57dfb792eee175d9c287e6b5778cbeb60fcf0b + MaterialComponents: 3993ec396d4d7f94073890f0a35c87013762f333 MaterialComponentsCatalog: 4769fcabbbdb4b40373eb428f329dbc4c2b53dac MaterialComponentsUnitTests: 8fa9520a8dae5ee0b7962ce449619b5be13445e6 diff --git a/demos/Pesto/Podfile.lock b/demos/Pesto/Podfile.lock index 242a9b2c87e..a301c4c75ea 100644 --- a/demos/Pesto/Podfile.lock +++ b/demos/Pesto/Podfile.lock @@ -11,7 +11,6 @@ PODS: - MaterialComponents/PageControl (= 4.0.1) - MaterialComponents/private (= 4.0.1) - MaterialComponents/RobotoFontLoader (= 4.0.1) - - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.0.1) - MaterialComponents/ShadowElevations (= 4.0.1) - MaterialComponents/ShadowLayer (= 4.0.1) - MaterialComponents/Slider (= 4.0.1) @@ -60,7 +59,6 @@ PODS: - MaterialComponents/RobotoFontLoader (4.0.1): - MaterialComponents/FontDiskLoader - MaterialComponents/Typography - - MaterialComponents/ScrollViewDelegateMultiplexer (4.0.1) - MaterialComponents/ShadowElevations (4.0.1) - MaterialComponents/ShadowLayer (4.0.1) - MaterialComponents/Slider (4.0.1): @@ -78,6 +76,6 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - MaterialComponents: 0f57dfb792eee175d9c287e6b5778cbeb60fcf0b + MaterialComponents: 3993ec396d4d7f94073890f0a35c87013762f333 COCOAPODS: 0.39.0 diff --git a/demos/Shrine/Podfile.lock b/demos/Shrine/Podfile.lock index 242a9b2c87e..a301c4c75ea 100644 --- a/demos/Shrine/Podfile.lock +++ b/demos/Shrine/Podfile.lock @@ -11,7 +11,6 @@ PODS: - MaterialComponents/PageControl (= 4.0.1) - MaterialComponents/private (= 4.0.1) - MaterialComponents/RobotoFontLoader (= 4.0.1) - - MaterialComponents/ScrollViewDelegateMultiplexer (= 4.0.1) - MaterialComponents/ShadowElevations (= 4.0.1) - MaterialComponents/ShadowLayer (= 4.0.1) - MaterialComponents/Slider (= 4.0.1) @@ -60,7 +59,6 @@ PODS: - MaterialComponents/RobotoFontLoader (4.0.1): - MaterialComponents/FontDiskLoader - MaterialComponents/Typography - - MaterialComponents/ScrollViewDelegateMultiplexer (4.0.1) - MaterialComponents/ShadowElevations (4.0.1) - MaterialComponents/ShadowLayer (4.0.1) - MaterialComponents/Slider (4.0.1): @@ -78,6 +76,6 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - MaterialComponents: 0f57dfb792eee175d9c287e6b5778cbeb60fcf0b + MaterialComponents: 3993ec396d4d7f94073890f0a35c87013762f333 COCOAPODS: 0.39.0 From 34a1e6cc39b9e9b17d178360f2f456bec1037391 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Wed, 20 Apr 2016 09:27:03 -0400 Subject: [PATCH 059/129] [Catalog] Use 3 tile row in landscape view, correct tile size when view appears Summary: Screenshot http://codereview.cc/M25 Tested visuals on iPhone 4s, iPhone 5, iPhone 5s, iPhone 6, iPhone 6+, iPhone 6s, iPhone 6s+. Still need to design for iPad. Reviewers: ajsecord, #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D665 --- .../MDCCatalog/MDCCatalogComponentsController.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/catalog/MDCCatalog/MDCCatalogComponentsController.swift b/catalog/MDCCatalog/MDCCatalogComponentsController.swift index d2ae78bce32..2731c9f60eb 100644 --- a/catalog/MDCCatalog/MDCCatalogComponentsController.swift +++ b/catalog/MDCCatalog/MDCCatalogComponentsController.swift @@ -102,13 +102,13 @@ class MDCCatalogComponentsController: UICollectionViewController { override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) - + self.collectionView?.collectionViewLayout.invalidateLayout() self.navigationController?.setNavigationBarHidden(true, animated: animated) } override func willAnimateRotationToInterfaceOrientation( toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) { - collectionView?.collectionViewLayout.invalidateLayout() + self.collectionView?.collectionViewLayout.invalidateLayout() } override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { @@ -140,7 +140,10 @@ class MDCCatalogComponentsController: UICollectionViewController { layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { let pad = CGFloat(1) - let cellWidth = (self.view.frame.size.width - 3 * pad) / 2 + var cellWidth = (self.view.frame.size.width - 3 * pad) / 2 + if (self.view.frame.size.width > self.view.frame.size.height) { + cellWidth = (self.view.frame.size.width - 4 * pad) / 3 + } return CGSize(width: cellWidth, height: cellWidth * 0.825) } @@ -155,6 +158,7 @@ class MDCCatalogComponentsController: UICollectionViewController { } self.navigationController?.pushViewController(vc, animated: true) } + } // UIScrollViewDelegate From 34b656298fd35fe1440a5e0bfcf9e10934199d91 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Tue, 19 Apr 2016 16:37:02 -0400 Subject: [PATCH 060/129] [FlexibleHeader] Add status bar visibility switch to configurator example. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D669 --- .../FlexibleHeaderConfiguratorExample.m | 24 +++++++++++++++++-- .../FlexibleHeaderConfiguratorSupplemental.h | 3 +++ .../FlexibleHeaderConfiguratorSupplemental.m | 2 ++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m b/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m index 9f4d00c162d..0b95b0cf685 100644 --- a/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m +++ b/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m @@ -20,6 +20,10 @@ #import "FlexibleHeaderConfiguratorSupplemental.h" +@interface FlexibleHeaderConfiguratorExample () +@property(nonatomic) BOOL overrideStatusBarHidden; +@end + @implementation FlexibleHeaderConfiguratorExample // Invoked when the user has changed a control's value. @@ -36,6 +40,15 @@ - (void)field:(FlexibleHeaderConfiguratorField)field didChangeValue:(NSNumber *) headerView.inFrontOfInfiniteContent = [value boolValue]; break; + case FlexibleHeaderConfiguratorFieldHideStatusBar: { + self.overrideStatusBarHidden = [value boolValue]; + [UIView animateWithDuration:0.4 + animations:^{ + [self setNeedsStatusBarAppearanceUpdate]; + }]; + break; + } + // Shift behavior case FlexibleHeaderConfiguratorFieldShiftBehaviorEnabled: { @@ -83,8 +96,12 @@ - (void)field:(FlexibleHeaderConfiguratorField)field didChangeValue:(NSNumber *) #pragma mark - Typical Flexible Header implementations // Required for shiftBehavior == MDCFlexibleHeaderShiftBehaviorEnabledWithStatusBar. -- (UIViewController *)childViewControllerForStatusBarHidden { - return self.fhvc; +- (BOOL)prefersStatusBarHidden { + return _overrideStatusBarHidden || self.fhvc.prefersStatusBarHidden; +} + +- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation { + return UIStatusBarAnimationSlide; } #pragma mark - UIScrollViewDelegate @@ -142,6 +159,9 @@ - (NSNumber *)valueForField:(FlexibleHeaderConfiguratorField)field { case FlexibleHeaderConfiguratorFieldContentImportance: return @((self.fhvc.headerView.headerContentImportance == MDCFlexibleHeaderContentImportanceHigh)); + case FlexibleHeaderConfiguratorFieldHideStatusBar: + return @(self.overrideStatusBarHidden); + case FlexibleHeaderConfiguratorFieldShiftBehaviorEnabled: { MDCFlexibleHeaderShiftBehavior behavior = self.fhvc.headerView.shiftBehavior; BOOL enabled = (behavior == MDCFlexibleHeaderShiftBehaviorEnabled || behavior == MDCFlexibleHeaderShiftBehaviorEnabledWithStatusBar); diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.h b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.h index 5f6b7f45d7a..7cd1b8df819 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.h +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.h @@ -26,9 +26,12 @@ typedef enum : NSUInteger { FlexibleHeaderConfiguratorFieldCanOverExtend, FlexibleHeaderConfiguratorFieldInFrontOfInfiniteContent, + FlexibleHeaderConfiguratorFieldHideStatusBar, + FlexibleHeaderConfiguratorFieldContentImportance, FlexibleHeaderConfiguratorFieldShiftBehaviorEnabled, FlexibleHeaderConfiguratorFieldShiftBehaviorEnabledWithStatusBar, + FlexibleHeaderConfiguratorFieldMinimumHeight, FlexibleHeaderConfiguratorFieldMaximumHeight, } FlexibleHeaderConfiguratorField; diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m index 0d9fdaf5e01..ccaa9deca18 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m @@ -141,6 +141,8 @@ - (void)viewDidLoad { FlexibleHeaderConfiguratorFieldCanOverExtend), switchItem(@"In front of infinite content", FlexibleHeaderConfiguratorFieldInFrontOfInfiniteContent), + switchItem(@"Hide status bar", + FlexibleHeaderConfiguratorFieldHideStatusBar), ]); createSection(@"Shift behavior", From 0438d1ba3e1c8e3d8280fe5e8b8ede2c7fcb538f Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 20 Apr 2016 12:11:48 -0400 Subject: [PATCH 061/129] [Catalog] Always call init when creating examples. Summary: This avoids getting into odd infinite recursions on iOS 8.4 when initializing UITableViewController instances. Reviewers: #mdc_ios_owners, junius Reviewed By: #mdc_ios_owners, junius Projects: #material_components_ios Differential Revision: http://codereview.cc/D683 --- catalog/CatalogByConvention/src/private/CBCRuntime.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalog/CatalogByConvention/src/private/CBCRuntime.m b/catalog/CatalogByConvention/src/private/CBCRuntime.m index dad10b11045..9e6e56bcdef 100644 --- a/catalog/CatalogByConvention/src/private/CBCRuntime.m +++ b/catalog/CatalogByConvention/src/private/CBCRuntime.m @@ -104,7 +104,7 @@ BOOL CBCCatalogIsPrimaryDemoFromClass(Class aClass) { NSCAssert(vc, @"expecting a initialViewController in the storyboard %@", storyboardName); return vc; } - return [[aClass alloc] initWithNibName:nil bundle:nil]; + return [[aClass alloc] init]; } NSString *CBCDescriptionFromClass(Class aClass) { From 6951e147d0a140fce0c9df61a5a074765ee0c0e8 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 20 Apr 2016 12:10:46 -0400 Subject: [PATCH 062/129] [FlexibleHeader] Remove usage of iOS 9 API in Configurator example. Reviewers: #mdc_ios_owners, iangordon Reviewed By: #mdc_ios_owners, iangordon Projects: #material_components_ios Differential Revision: http://codereview.cc/D682 --- .../supplemental/FlexibleHeaderConfiguratorSupplemental.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m index ccaa9deca18..3bacc2b5172 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m @@ -103,7 +103,7 @@ - (void)viewDidLoad { titleLabel.frame = frame; titleLabel.text = self.title; titleLabel.textColor = [UIColor whiteColor]; - titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleTitle2]; + titleLabel.font = [UIFont systemFontOfSize:22]; titleLabel.textAlignment = NSTextAlignmentCenter; titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin; From c1777eb1f1e9e095257f20cd30a54d7ebc13fff7 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 20 Apr 2016 11:53:58 -0400 Subject: [PATCH 063/129] [FlexibleHeader] Implement the correct designated initializer chain in the Configurator example. Summary: This resolves an iOS 8.4 infinite recursion crash on initialization due to a designated initializer mess in UIKit. Reviewers: junius, #mdc_ios_owners Reviewed By: junius, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D681 --- .../FlexibleHeaderConfiguratorSupplemental.m | 28 +++---------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m index 3bacc2b5172..7c3362706c2 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m @@ -40,33 +40,13 @@ - (BOOL)catalogShouldHideNavigation { @implementation FlexibleHeaderConfiguratorExample (Supplemental) -- (void)commonInit { - self.fhvc = [[MDCFlexibleHeaderViewController alloc] initWithNibName:nil bundle:nil]; - [self addChildViewController:self.fhvc]; - - self.title = @"Configurator"; -} - -- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { +- (instancetype)init { self = [super initWithStyle:kStyle]; if (self) { - [self commonInit]; - } - return self; -} + self.fhvc = [[MDCFlexibleHeaderViewController alloc] initWithNibName:nil bundle:nil]; + [self addChildViewController:self.fhvc]; -- (instancetype)initWithStyle:(UITableViewStyle)style { - self = [super initWithStyle:kStyle]; - if (self) { - [self commonInit]; - } - return self; -} - -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - [self commonInit]; + self.title = @"Configurator"; } return self; } From 35206a410e0fd5bc365d69fbbc0d955006558cdd Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 20 Apr 2016 12:26:37 -0400 Subject: [PATCH 064/129] [FlexibleHeader] Funnel init through initWithStyle:. Reviewers: #mdc_ios_owners, larche Reviewed By: larche Projects: #material_components_ios Differential Revision: http://codereview.cc/D684 --- .../supplemental/FlexibleHeaderConfiguratorSupplemental.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m index 7c3362706c2..ebbdaf54ee7 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderConfiguratorSupplemental.m @@ -41,6 +41,10 @@ - (BOOL)catalogShouldHideNavigation { @implementation FlexibleHeaderConfiguratorExample (Supplemental) - (instancetype)init { + return [self initWithStyle:kStyle]; +} + +- (instancetype)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:kStyle]; if (self) { self.fhvc = [[MDCFlexibleHeaderViewController alloc] initWithNibName:nil bundle:nil]; From 916866a5259d3972ff359f0d57bce67bdb982ed2 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 20 Apr 2016 12:42:13 -0400 Subject: [PATCH 065/129] [ButtonBar] Update tests to reflect that titleTextAttributes appearance only works on iOS 9. Summary: Part of https://github.com/google/material-components-ios/issues/370. Reviewers: #mdc_ios_owners, junius, iangordon Reviewed By: #mdc_ios_owners, junius, iangordon Subscribers: iangordon Projects: #material_components_ios Differential Revision: http://codereview.cc/D685 --- .../tests/unit/ButtonBarIssue370Tests.swift | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/components/ButtonBar/tests/unit/ButtonBarIssue370Tests.swift b/components/ButtonBar/tests/unit/ButtonBarIssue370Tests.swift index fdd1114b9bf..3ed7689888b 100644 --- a/components/ButtonBar/tests/unit/ButtonBarIssue370Tests.swift +++ b/components/ButtonBar/tests/unit/ButtonBarIssue370Tests.swift @@ -18,7 +18,7 @@ import XCTest import MaterialComponents // Tests confirming that the Button Bar respects UI appearance for bar button item -// titleTextAttributes. +// titleTextAttributes on iOS 9 and above. // // Based on issue https://github.com/google/material-components-ios/issues/370 class ButtonBarIssue370Tests: XCTestCase { @@ -49,6 +49,12 @@ class ButtonBarIssue370Tests: XCTestCase { func testGlobalAppearanceOnly() { UIBarButtonItem.appearance().setTitleTextAttributes(globalAttributes, forState: .Normal) + + if UIBarButtonItem.appearance().titleTextAttributesForState(.Normal) == nil { + // This feature is not supported on this OS + return + } + let item = UIBarButtonItem(title: "Text", style: .Plain, target: nil, action: nil) buttonBar.items = [item] @@ -76,6 +82,12 @@ class ButtonBarIssue370Tests: XCTestCase { func testGlobalAppearanceAndDirectMerging() { UIBarButtonItem.appearance().setTitleTextAttributes(fontAttributes, forState: .Normal) + + if UIBarButtonItem.appearance().titleTextAttributesForState(.Normal) == nil { + // This feature is not supported on this OS + return + } + let item = UIBarButtonItem(title: "Text", style: .Plain, target: nil, action: nil) item.setTitleTextAttributes(directAttributes, forState: .Normal) buttonBar.items = [item] From 271572706e98e93edde01a30a14daf129f639306 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 20 Apr 2016 13:31:40 -0400 Subject: [PATCH 066/129] [FlexibleHeader] Resolve iOS 8.4 unit test failure of issue176 tests. Summary: iOS 8.4 does not automatically adjust the contentOffset when injecting into the contentInset. Other OS versions do change the contentOffset and our tests were written to that assumption. We now explicitly modify the contentOffset if it doesn't appear that the contentOffset changed and our contentOffset was originally zero. This recreates the expected behavior of non-iOS 8.4 versions. Reviewers: #mdc_ios_owners, ajsecord, junius Reviewed By: #mdc_ios_owners, ajsecord, junius Subscribers: junius Projects: #material_components_ios Differential Revision: http://codereview.cc/D688 --- .../FlexibleHeader/src/MDCFlexibleHeaderView.m | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m index 009e8a92fe1..68a268e8ed8 100644 --- a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m +++ b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m @@ -336,11 +336,28 @@ - (MDCFlexibleHeaderScrollViewInfo *)fhv_addInsetsToScrollView:(UIScrollView *)s } if (!info.hasInjectedTopContentInset) { + _contentInsetsAreChanging = YES; UIEdgeInsets insets = scrollView.contentInset; insets.top += _maximumHeight; info.injectedTopContentInset = _maximumHeight; info.hasInjectedTopContentInset = YES; + CGPoint oldContentOffset = scrollView.contentOffset; scrollView.contentInset = insets; + + // The private API -[UIScrollView(UIScrollViewInternal) _adjustContentOffsetIfNecessary] + // will automatically adjust the contentOffset when adjusting contentInset. Exactly *when* this + // happens is not documented. Some OS versions appear to have different behaviors. + // Notably: iOS 8.4 does not appear to change the contentOffset when first initializing the + // scroll view. + // + // The logic below is intentionally conservative because we want to lean on + // _adjustContentOffsetIfNecessary doing the right thing most of the time. + + if (oldContentOffset.y == 0 && CGPointEqualToPoint(oldContentOffset, scrollView.contentOffset)) { + _contentOffset.y -= _maximumHeight; + _trackingScrollView.contentOffset = _contentOffset; + } + _contentInsetsAreChanging = NO; } // The scroll indicator insets are updated by fhv_accumulatorDidChange and change dynamically with From ea405734e97bf96bff2d97f46b28322a0aa2b753 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 20 Apr 2016 13:37:58 -0400 Subject: [PATCH 067/129] [Catalog] Example view controllers must implement init. Summary: The catalog now calls init on view controllers to initialize them. This does not chain to initWithNibName. Reviewers: #mdc_ios_owners, junius Reviewed By: #mdc_ios_owners, junius Projects: #material_components_ios Differential Revision: http://codereview.cc/D689 --- .../AppBar/examples/AppBarDelegateForwardingExample.m | 4 ++-- components/AppBar/examples/AppBarImageryExample.m | 4 ++-- components/AppBar/examples/AppBarTypicalUseExample.m | 4 ++-- .../ButtonBar/examples/ButtonBarTypicalUseExample.m | 4 ++-- .../examples/FlexibleHeaderTypicalUseExample.m | 8 ++++++++ 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/components/AppBar/examples/AppBarDelegateForwardingExample.m b/components/AppBar/examples/AppBarDelegateForwardingExample.m index 69036b4b74b..7c23e50e441 100644 --- a/components/AppBar/examples/AppBarDelegateForwardingExample.m +++ b/components/AppBar/examples/AppBarDelegateForwardingExample.m @@ -90,8 +90,8 @@ - (BOOL)catalogShouldHideNavigation { @implementation AppBarDelegateForwardingExample (TypicalUse) -- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; +- (id)init { + self = [super init]; if (self) { _appBar = [[MDCAppBar alloc] init]; _appBar.navigationBar.tintColor = [UIColor whiteColor]; diff --git a/components/AppBar/examples/AppBarImageryExample.m b/components/AppBar/examples/AppBarImageryExample.m index d55a8a26fd0..7a0b4488b80 100644 --- a/components/AppBar/examples/AppBarImageryExample.m +++ b/components/AppBar/examples/AppBarImageryExample.m @@ -76,8 +76,8 @@ - (UIImage *)headerBackgroundImage { @implementation AppBarImageryExample (TypicalUse) -- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; +- (id)init { + self = [super init]; if (self) { _appBar = [[MDCAppBar alloc] init]; diff --git a/components/AppBar/examples/AppBarTypicalUseExample.m b/components/AppBar/examples/AppBarTypicalUseExample.m index 7536378bda8..d282ed61efc 100644 --- a/components/AppBar/examples/AppBarTypicalUseExample.m +++ b/components/AppBar/examples/AppBarTypicalUseExample.m @@ -27,8 +27,8 @@ @interface AppBarTypicalUseExample : UITableViewController @implementation AppBarTypicalUseExample -- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; +- (id)init { + self = [super init]; if (self) { // Step 2: Initialize the App Bar and add the headerViewController as a child. _appBar = [[MDCAppBar alloc] init]; diff --git a/components/ButtonBar/examples/ButtonBarTypicalUseExample.m b/components/ButtonBar/examples/ButtonBarTypicalUseExample.m index e2b82fc5cc1..da43345c0a1 100644 --- a/components/ButtonBar/examples/ButtonBarTypicalUseExample.m +++ b/components/ButtonBar/examples/ButtonBarTypicalUseExample.m @@ -107,8 +107,8 @@ + (NSString *)catalogDescription { @implementation ButtonBarTypicalUseExample (GeneralApplicationLogic) -- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; +- (id)init { + self = [super init]; if (self) { self.title = @"Button Bar"; } diff --git a/components/FlexibleHeader/examples/FlexibleHeaderTypicalUseExample.m b/components/FlexibleHeader/examples/FlexibleHeaderTypicalUseExample.m index 99671613b11..7b1c20042f4 100644 --- a/components/FlexibleHeader/examples/FlexibleHeaderTypicalUseExample.m +++ b/components/FlexibleHeader/examples/FlexibleHeaderTypicalUseExample.m @@ -28,6 +28,14 @@ @interface FlexibleHeaderTypicalUseViewController () @implementation FlexibleHeaderTypicalUseViewController +- (instancetype)init { + self = [super init]; + if (self) { + [self commonMDCFlexibleHeaderViewControllerInit]; + } + return self; +} + - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { From 8e733e163a4b308186345e03d90056e4040c693b Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 20 Apr 2016 13:47:40 -0400 Subject: [PATCH 068/129] [Catalog] Fixing Swift example view controller initializers. Reviewers: #mdc_ios_owners, junius Reviewed By: #mdc_ios_owners, junius Projects: #material_components_ios Differential Revision: http://codereview.cc/D690 --- .../AppBarDelegateForwardingExample.swift | 22 +++++++++++++++++++ .../examples/AppBarImageryExample.swift | 4 ++-- .../examples/AppBarTypicalUseExample.swift | 4 ++-- .../examples/ButtonBarTypicalUseExample.swift | 4 ++-- .../ButtonsStoryboardAndProgrammatic.swift | 4 ++-- .../TypographyExamplesViewController.swift | 2 +- 6 files changed, 31 insertions(+), 9 deletions(-) diff --git a/components/AppBar/examples/AppBarDelegateForwardingExample.swift b/components/AppBar/examples/AppBarDelegateForwardingExample.swift index 48b7ed1a94c..09ec54fb768 100644 --- a/components/AppBar/examples/AppBarDelegateForwardingExample.swift +++ b/components/AppBar/examples/AppBarDelegateForwardingExample.swift @@ -23,6 +23,28 @@ class AppBarDelegateForwardingExample: UITableViewController { let appBar = MDCAppBar() + convenience init() { + self.init(style: .Plain) + } + + override init(style: UITableViewStyle) { + super.init(style: style) + + self.appBar.navigationBar.tintColor = UIColor.whiteColor() + + self.addChildViewController(appBar.headerViewController) + + self.title = "Delegate Forwarding" + + let color = UIColor( + red: CGFloat(0x03) / CGFloat(255), + green: CGFloat(0xA9) / CGFloat(255), + blue: CGFloat(0xF4) / CGFloat(255), + alpha: 1) + appBar.headerViewController.headerView.backgroundColor = color + appBar.navigationBar.tintColor = UIColor.whiteColor() + } + override func viewDidLoad() { super.viewDidLoad() diff --git a/components/AppBar/examples/AppBarImageryExample.swift b/components/AppBar/examples/AppBarImageryExample.swift index b96d7b7146c..85098f812c9 100644 --- a/components/AppBar/examples/AppBarImageryExample.swift +++ b/components/AppBar/examples/AppBarImageryExample.swift @@ -61,8 +61,8 @@ class AppBarImagerySwiftExample: UITableViewController { // MARK: Typical configuration - override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + init() { + super.init(nibName: nil, bundle: nil) self.title = "Imagery (Swift)" diff --git a/components/AppBar/examples/AppBarTypicalUseExample.swift b/components/AppBar/examples/AppBarTypicalUseExample.swift index c3e94989a75..099fec05da4 100644 --- a/components/AppBar/examples/AppBarTypicalUseExample.swift +++ b/components/AppBar/examples/AppBarTypicalUseExample.swift @@ -22,8 +22,8 @@ class AppBarTypicalUseSwiftExample: UITableViewController { // Step 1: Create and initialize an App Bar. let appBar = MDCAppBar() - override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + init() { + super.init(nibName: nil, bundle: nil) self.title = "App Bar (Swift)" diff --git a/components/ButtonBar/examples/ButtonBarTypicalUseExample.swift b/components/ButtonBar/examples/ButtonBarTypicalUseExample.swift index d033b9359b9..d9a050d537a 100644 --- a/components/ButtonBar/examples/ButtonBarTypicalUseExample.swift +++ b/components/ButtonBar/examples/ButtonBarTypicalUseExample.swift @@ -70,8 +70,8 @@ class ButtonBarTypicalUseSwiftExample: UIViewController { } // MARK: Typical application code (not Material-specific) - override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + init() { + super.init(nibName: nil, bundle: nil) self.title = "Button Bar" } diff --git a/components/Buttons/examples/ButtonsStoryboardAndProgrammatic.swift b/components/Buttons/examples/ButtonsStoryboardAndProgrammatic.swift index b92814bdafb..d0fbe341cf3 100644 --- a/components/Buttons/examples/ButtonsStoryboardAndProgrammatic.swift +++ b/components/Buttons/examples/ButtonsStoryboardAndProgrammatic.swift @@ -35,8 +35,8 @@ class ButtonsStoryboardAndProgrammaticController: UIViewController { @IBOutlet weak var storyboardFlat: MDCFlatButton! @IBOutlet weak var storyboardFloating: MDCFloatingButton! - override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + init() { + super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { diff --git a/components/Typography/examples/TypographyExamplesViewController.swift b/components/Typography/examples/TypographyExamplesViewController.swift index 5ea973ac4cb..1c7c683f83d 100644 --- a/components/Typography/examples/TypographyExamplesViewController.swift +++ b/components/Typography/examples/TypographyExamplesViewController.swift @@ -143,7 +143,7 @@ class TypographyExamplesViewController: UICollectionViewController { "Display 4 Font" ] - override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { + init() { let flowLayout = UICollectionViewFlowLayout() flowLayout.sectionInset = UIEdgeInsetsMake(10, 0, 10, 0) super.init(collectionViewLayout: flowLayout) From 9310ae9de1f5e7a05cfe195f89852eeb9ea7ae33 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Tue, 19 Apr 2016 11:14:15 -0400 Subject: [PATCH 069/129] [FlexibleHeader] Hide header contents in the configurator when the header is shifting. Summary: Closes https://github.com/google/material-components-ios/issues/323. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D659 --- .../FlexibleHeaderConfiguratorExample.m | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m b/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m index 0b95b0cf685..3a21cc2b68a 100644 --- a/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m +++ b/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m @@ -20,7 +20,7 @@ #import "FlexibleHeaderConfiguratorSupplemental.h" -@interface FlexibleHeaderConfiguratorExample () +@interface FlexibleHeaderConfiguratorExample () @property(nonatomic) BOOL overrideStatusBarHidden; @end @@ -139,6 +139,25 @@ - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView } } +#pragma mark - MDCFlexibleHeaderViewLayoutDelegate + +- (void)flexibleHeaderViewController:(nonnull MDCFlexibleHeaderViewController *)flexibleHeaderViewController + flexibleHeaderViewFrameDidChange:(nonnull MDCFlexibleHeaderView *)flexibleHeaderView { + CGFloat headerContentAlpha; + switch (flexibleHeaderView.scrollPhase) { + case MDCFlexibleHeaderScrollPhaseCollapsing: + case MDCFlexibleHeaderScrollPhaseOverExtending: + headerContentAlpha = 1; + break; + case MDCFlexibleHeaderScrollPhaseShifting: + headerContentAlpha = 1 - flexibleHeaderView.scrollPhasePercentage; + break; + } + for (UIView *subview in self.fhvc.headerView.subviews) { + subview.alpha = headerContentAlpha; + } +} + #pragma mark - Field data manipulation static const CGFloat kHeightScalar = 300; From 8ab280fdc926fe389396a248f2d28dc24fa75c97 Mon Sep 17 00:00:00 2001 From: randallli Date: Wed, 20 Apr 2016 12:58:59 -0400 Subject: [PATCH 070/129] [Slider] Fix unit tests by increasing epsilon Reviewers: #mdc_ios_owners, featherless, iangordon Reviewed By: #mdc_ios_owners, featherless, iangordon Projects: #material_components_ios Differential Revision: http://codereview.cc/D686 --- components/Slider/tests/unit/SliderTests.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/Slider/tests/unit/SliderTests.m b/components/Slider/tests/unit/SliderTests.m index ddb3de5f153..4872e4fe6d9 100644 --- a/components/Slider/tests/unit/SliderTests.m +++ b/components/Slider/tests/unit/SliderTests.m @@ -19,7 +19,7 @@ #import "MaterialSlider.h" static const NSUInteger kNumberOfRepeats = 20; -static const CGFloat kEpsilonAccuracy = 0.0001f; +static const CGFloat kEpsilonAccuracy = 0.001f; // Blue 500 from http://www.google.com/design/spec/style/color.html#color-color-palette . static const uint32_t MDCBlueColor = 0x2196F3; From 835a6d358253253cbd3bea960fd8eebb09ddfaf3 Mon Sep 17 00:00:00 2001 From: randallli Date: Thu, 21 Apr 2016 01:08:43 -0400 Subject: [PATCH 071/129] [Slider] Added swift example to slider readme Summary: ongoing work for: https://github.com/google/material-components-ios/issues/318 Reviewers: #mdc_ios_owners, ajsecord, featherless Reviewed By: #mdc_ios_owners, ajsecord, featherless Subscribers: ajsecord, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D658 --- components/Slider/README.md | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/components/Slider/README.md b/components/Slider/README.md index 2adebf1190d..3018add499a 100644 --- a/components/Slider/README.md +++ b/components/Slider/README.md @@ -72,29 +72,42 @@ import MaterialComponents ~~~ +### Standard usage + +MDCSlider can be be used like a standard `UIControl`. + +#### Objective C -### Objective C ~~~ objc - (void)viewDidLoad { -... - - MDCSlider *slider = [[MDCSlider alloc] initWithFrame:CGRectMake(0, 0, 100, 27)]; + MDCSlider *slider = [[MDCSlider alloc] initWithFrame:CGRectMake(50, 50, 100, 27)]; [slider addTarget:self action:@selector(didChangeSliderValue:) forControlEvents:UIControlEventValueChanged]; [self.view addSubview:slider]; - slider.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds) - 2 * slider.frame.size.height); +} -... +- (void)didChangeSliderValue:(MDCSlider *)slider { + NSLog(@"did change %@ value: %f", NSStringFromClass([slider class]), slider.value); } +~~~ -- (void)didChangeSliderValue:(id)sender { - MDCSlider *slider = sender; - NSLog(@"did change %@ value: %f", NSStringFromClass([sender class]), slider.value); +#### Swift + +~~~ swift +override func viewDidLoad() { + let slider = MDCSlider(frame: CGRectMake(50, 50, 100, 27)) + slider.addTarget(self, + action: Selector("didChangeSliderValue:"), + forControlEvents: .ValueChanged) + view.addSubview(slider) } +func didChangeSliderValue(senderSlider:MDCSlider) { + NSLog("Did change slider value to: %@", senderSlider.value) +} ~~~ From bf2c12bd7c9b46eb23858673e6c63d3dfa20edf4 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Wed, 20 Apr 2016 23:08:58 -0400 Subject: [PATCH 072/129] [FlexibleHeader] Add horizontal paging example. Summary: This demonstrates the use of a Flexible Header with a horizontal paging scroll view composed of many vertical scroll views. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D697 --- .../FlexibleHeaderHorizontalPagingExample.m | 170 ++++++++++++++++++ ...exibleHeaderHorizontalPagingSupplemental.h | 30 ++++ ...exibleHeaderHorizontalPagingSupplemental.m | 36 ++++ 3 files changed, 236 insertions(+) create mode 100644 components/FlexibleHeader/examples/FlexibleHeaderHorizontalPagingExample.m create mode 100644 components/FlexibleHeader/examples/supplemental/FlexibleHeaderHorizontalPagingSupplemental.h create mode 100644 components/FlexibleHeader/examples/supplemental/FlexibleHeaderHorizontalPagingSupplemental.m diff --git a/components/FlexibleHeader/examples/FlexibleHeaderHorizontalPagingExample.m b/components/FlexibleHeader/examples/FlexibleHeaderHorizontalPagingExample.m new file mode 100644 index 00000000000..e53c37683bd --- /dev/null +++ b/components/FlexibleHeader/examples/FlexibleHeaderHorizontalPagingExample.m @@ -0,0 +1,170 @@ +/* + 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 "FlexibleHeaderHorizontalPagingSupplemental.h" + +#import "MaterialFlexibleHeader.h" + +static UIColor *HexColor(uint32_t hex) { + return [UIColor colorWithRed:(CGFloat)((uint8_t)(hex >> 16)) / (CGFloat)255 + green:(CGFloat)((uint8_t)(hex >> 8)) / (CGFloat)255 + blue:(CGFloat)((uint8_t)hex) / (CGFloat)255 + alpha:1]; +} + +static const NSUInteger kNumberOfPages = 10; + +@interface FlexibleHeaderHorizontalPagingViewController () +@end + +@implementation FlexibleHeaderHorizontalPagingViewController { + UIScrollView *_pagingScrollView; + NSArray *_pageScrollViews; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + _pagingScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; + _pagingScrollView.pagingEnabled = YES; + _pagingScrollView.delegate = self; + _pagingScrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + _pagingScrollView.scrollsToTop = NO; + + NSArray *pageColors = @[ HexColor(0x55C4F5), HexColor(0x8BC34A), HexColor(0xFFC107) ]; + + NSMutableArray *pageScrollViews = [NSMutableArray array]; + for (NSUInteger ix = 0; ix < kNumberOfPages; ++ix) { + UIScrollView *scrollView = [[UIScrollView alloc] init]; + scrollView.delegate = self.fhvc; + scrollView.backgroundColor = pageColors[ix % [pageColors count]]; + [_pagingScrollView addSubview:scrollView]; + + [pageScrollViews addObject:scrollView]; + } + _pageScrollViews = pageScrollViews; + + [self.view addSubview:_pagingScrollView]; + + self.fhvc.headerView.trackingScrollView = [_pageScrollViews firstObject]; + + [self typicalFlexibleHeaderViewDidLoad]; +} + +- (void)recalculatePageBounds { + CGRect frame = self.view.bounds; + for (NSUInteger ix = 0; ix < [_pageScrollViews count]; ++ix) { + UIScrollView *scrollView = _pageScrollViews[ix]; + + scrollView.frame = frame; + scrollView.contentSize = CGSizeMake(frame.size.width, frame.size.height * 10); + + frame.origin.x += frame.size.width; + } + + _pagingScrollView.contentSize = CGSizeMake(self.view.bounds.size.width * [_pageScrollViews count], + self.view.bounds.size.height); +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + [self recalculatePageBounds]; + + [self.navigationController setNavigationBarHidden:YES animated:animated]; +} + +- (void)viewWillTransitionToSize:(CGSize)size + withTransitionCoordinator:(id)coordinator { + [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; + + [self recalculatePageBounds]; +} + +#pragma mark - UIScrollViewDelegate + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + if (scrollView == _pagingScrollView) { + NSInteger pageIndex = (NSInteger)(scrollView.contentOffset.x / self.view.bounds.size.width); + for (NSInteger ix = MAX(0, pageIndex - 1); + ix <= MIN((NSInteger)_pageScrollViews.count - 1, pageIndex + 1); ++ix) { + if (ix != pageIndex) { + [self.fhvc.headerView trackingScrollWillChangeToScrollView:_pageScrollViews[ix]]; + } + } + } +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + if (scrollView == _pagingScrollView) { + NSUInteger pageIndex = (NSUInteger)(scrollView.contentOffset.x / self.view.bounds.size.width); + self.fhvc.headerView.trackingScrollView = _pageScrollViews[pageIndex]; + [self.fhvc.headerView.trackingScrollView flashScrollIndicators]; + } +} + +#pragma mark - From the Typical Use example + +- (instancetype)init { + self = [super init]; + if (self) { + [self commonMDCFlexibleHeaderViewControllerInit]; + } + return self; +} + +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + [self commonMDCFlexibleHeaderViewControllerInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonMDCFlexibleHeaderViewControllerInit]; + } + return self; +} + +- (void)commonMDCFlexibleHeaderViewControllerInit { + self.fhvc = [[MDCFlexibleHeaderViewController alloc] initWithNibName:nil bundle:nil]; + [self addChildViewController:_fhvc]; +} + +- (void)typicalFlexibleHeaderViewDidLoad { + self.fhvc.view.frame = self.view.bounds; + [self.view addSubview:self.fhvc.view]; + [self.fhvc didMoveToParentViewController:self]; + + // Light blue 500 + self.fhvc.headerView.backgroundColor = [UIColor colorWithRed:0.012 + green:0.663 + blue:0.957 + alpha:1]; +} + +- (UIStatusBarStyle)preferredStatusBarStyle { + return UIStatusBarStyleLightContent; +} + +- (UIViewController *)childViewControllerForStatusBarHidden { + return self.fhvc; +} + +@end diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderHorizontalPagingSupplemental.h b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderHorizontalPagingSupplemental.h new file mode 100644 index 00000000000..c8a418fb630 --- /dev/null +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderHorizontalPagingSupplemental.h @@ -0,0 +1,30 @@ +/* + 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. + */ + +/** 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 MDCFlexibleHeaderViewController; + +@interface FlexibleHeaderHorizontalPagingViewController : UIViewController + +@property(nonatomic, strong) MDCFlexibleHeaderViewController *fhvc; + +@end diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderHorizontalPagingSupplemental.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderHorizontalPagingSupplemental.m new file mode 100644 index 00000000000..2ffcd2485cc --- /dev/null +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderHorizontalPagingSupplemental.m @@ -0,0 +1,36 @@ +/* + 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. + */ + +/** 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 "FlexibleHeaderHorizontalPagingSupplemental.h" + +#import "MaterialFlexibleHeader.h" + +@implementation FlexibleHeaderHorizontalPagingViewController (CatalogByConvention) + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Flexible Header", @"Horizontal Paging" ]; +} + +- (BOOL)catalogShouldHideNavigation { + return YES; +} + +@end From a15bfbe35bdd6e61e17739ff4199b6a3940398a0 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 10:09:55 -0400 Subject: [PATCH 073/129] Ran arc lint --everything --apply-patches. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D700 --- .../MDCCatalogTileDataNavigationBar.m | 4 ++-- catalog/MDCCatalog/MDCCatalogTiles.h | 1 - .../FlexibleHeaderTypicalUseSupplemental.m | 20 +++++++++---------- .../HeaderStackViewTypicalUseSupplemental.m | 20 +++++++++---------- ...vigationBarTypicalUseExampleSupplemental.m | 20 +++++++++---------- .../SliderTypicalUseSupplemental.m | 2 +- demos/Pesto/Pesto/PestoAppDelegate.m | 1 - 7 files changed, 33 insertions(+), 35 deletions(-) diff --git a/catalog/MDCCatalog/MDCCatalogTileDataNavigationBar.m b/catalog/MDCCatalog/MDCCatalogTileDataNavigationBar.m index d9674145fbd..f4e90f07cba 100644 --- a/catalog/MDCCatalog/MDCCatalogTileDataNavigationBar.m +++ b/catalog/MDCCatalog/MDCCatalogTileDataNavigationBar.m @@ -192,8 +192,8 @@ + (void)draw:(CGRect)frame { NSDictionary* labelFontAttributes = @{ NSFontAttributeName : [UIFont fontWithName:@"Roboto-Medium" size:11], - NSForegroundColorAttributeName : textForeground, - NSParagraphStyleAttributeName : labelStyle + NSForegroundColorAttributeName : textForeground, + NSParagraphStyleAttributeName : labelStyle }; CGFloat labelTextHeight = diff --git a/catalog/MDCCatalog/MDCCatalogTiles.h b/catalog/MDCCatalog/MDCCatalogTiles.h index 1c0d8022b89..3ee7696ecd5 100644 --- a/catalog/MDCCatalog/MDCCatalogTiles.h +++ b/catalog/MDCCatalog/MDCCatalogTiles.h @@ -25,7 +25,6 @@ #import "MDCCatalogTileDataNavigationBar.h" #import "MDCCatalogTileDataPageControl.h" #import "MDCCatalogTileDataShadowLayer.h" -#import "MDCCatalogTileDataInk.h" #import "MDCCatalogTileDataSlider.h" #import "MDCCatalogTileDataSpritedAnimationView.h" #import "MDCCatalogTileDataSwitch.h" diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m index e8f3850650d..dc99a50559f 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m @@ -139,18 +139,18 @@ - (NSAttributedString *)instructionsString { NSDictionary *instructionAttributes1 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSDictionary *instructionAttributes2 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSString *instructionText1 = @"PULL DOWN\n\nMDCFlexibleHeaderViewController\nallows the\ blue area to stretch\nwhen scrolled down.\n\n\n\n\n\n"; diff --git a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m index 00403cabc26..3dccc9ac18d 100644 --- a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m +++ b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m @@ -155,19 +155,19 @@ - (NSAttributedString *)instructionsString { NSDictionary *instructionAttributes1 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSDictionary *instructionAttributes2 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSString *instructionText = @"SWIPE RIGHT\n\n\n\nto go back\n\n\n\n\n\n"; NSMutableAttributedString *instructionsAttributedString = [[NSMutableAttributedString alloc] diff --git a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.m b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.m index c7a40ce83e3..743cc2f8df1 100644 --- a/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.m +++ b/components/NavigationBar/examples/supplemental/NavigationBarTypicalUseExampleSupplemental.m @@ -117,19 +117,19 @@ - (NSAttributedString *)instructionsString { NSDictionary *instructionAttributes1 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSDictionary *instructionAttributes2 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSString *instructionText = @"SWIPE RIGHT\n\n\n\nto go back\n\n\n\n\n\n"; NSMutableAttributedString *instructionsAttributedString = [[NSMutableAttributedString alloc] diff --git a/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m b/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m index 0013f37b23b..cc288c61d9c 100644 --- a/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m +++ b/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m @@ -5,8 +5,8 @@ #import -#import "SliderTypicalUseSupplemental.h" #import "MaterialTypography.h" +#import "SliderTypicalUseSupplemental.h" #pragma mark - SliderTypicalUseViewController diff --git a/demos/Pesto/Pesto/PestoAppDelegate.m b/demos/Pesto/Pesto/PestoAppDelegate.m index c477ed12faa..c12ff5a5716 100644 --- a/demos/Pesto/Pesto/PestoAppDelegate.m +++ b/demos/Pesto/Pesto/PestoAppDelegate.m @@ -25,7 +25,6 @@ @implementation PestoAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - PestoFlexibleHeaderContainerViewController *flexHeadContainerVC = [[PestoFlexibleHeaderContainerViewController alloc] init]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; From bd6ef35fd82c4ee16d2a4d077e224499a3abe9ed Mon Sep 17 00:00:00 2001 From: Adrian Secord Date: Thu, 21 Apr 2016 11:08:40 -0400 Subject: [PATCH 074/129] Fixes MDCSlider example build with required import. Fixes #412. Reviewers: #mdc_ios_owners, randallli Reviewed By: #mdc_ios_owners, randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D701 --- .../Slider/examples/supplemental/SliderTypicalUseSupplemental.m | 1 + 1 file changed, 1 insertion(+) diff --git a/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m b/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m index cc288c61d9c..d1248a2aa3c 100644 --- a/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m +++ b/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m @@ -5,6 +5,7 @@ #import +#import "MDCSlider.h" #import "MaterialTypography.h" #import "SliderTypicalUseSupplemental.h" From e0d8050ef395928ba3eee40045059b9c46c9f21a Mon Sep 17 00:00:00 2001 From: randallli Date: Thu, 21 Apr 2016 12:07:11 -0400 Subject: [PATCH 075/129] [PageControl] Publicized conformance to UIScrollViewDelegate. Reviewers: cjcox, featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Subscribers: featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D703 --- components/PageControl/src/MDCPageControl.h | 18 ++++++++++++++++-- components/PageControl/src/MDCPageControl.m | 3 --- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/components/PageControl/src/MDCPageControl.h b/components/PageControl/src/MDCPageControl.h index 3ba4d608126..ee6c5b2cd08 100644 --- a/components/PageControl/src/MDCPageControl.h +++ b/components/PageControl/src/MDCPageControl.h @@ -23,8 +23,22 @@ material design specifications for animation and layout. The UIControlEventValueChanged control event is sent when the user changes the current page. - */ -@interface MDCPageControl : UIControl + + ### UIScrollViewDelegate + + In order for the Page Control to respond correctly to scroll events set the scrollView.delegate to + your pageControl: + + scrollView.delegate = pageControl; + + or forward the UIScrollViewDelegate methods: + + @c scrollViewDidScroll: + @c scrollViewDidEndDecelerating: + @c scrollViewDidEndScrollingAnimation: + + */ +@interface MDCPageControl : UIControl #pragma mark Managing the page diff --git a/components/PageControl/src/MDCPageControl.m b/components/PageControl/src/MDCPageControl.m index 472bffd79d7..3da9d16e76d 100644 --- a/components/PageControl/src/MDCPageControl.m +++ b/components/PageControl/src/MDCPageControl.m @@ -53,9 +53,6 @@ static inline CGFloat normalizeValue(CGFloat value, CGFloat minRange, CGFloat ma return (diff > 0) ? ((value - minRange) / diff) : 0; } -@interface MDCPageControl () -@end - @implementation MDCPageControl { UIView *_containerView; NSMutableArray *_indicators; From 39ce5715a64383696287510825a3ef8caf295527 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 10:06:47 -0400 Subject: [PATCH 076/129] [Catalog] Subclass UIViewController rather than UITableViewController. Summary: It appears that interaction tests using XCUITesting don't support UITableViewController particularly well. Notably, we can't get access to anything in a flexible header that's a subview of a UITableViewController's tableView. This change will allow us to write interaction tests. Necessary for https://github.com/google/material-components-ios/issues/414. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D699 --- .../src/CBCNodeViewController.h | 4 +++- .../src/CBCNodeViewController.m | 18 +++++++++++++++++- .../MDCCatalog/MDCNodeListViewController.swift | 2 +- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/catalog/CatalogByConvention/src/CBCNodeViewController.h b/catalog/CatalogByConvention/src/CBCNodeViewController.h index 372cd15e1fe..729f7996e1b 100644 --- a/catalog/CatalogByConvention/src/CBCNodeViewController.h +++ b/catalog/CatalogByConvention/src/CBCNodeViewController.h @@ -22,13 +22,15 @@ An instance of CBCNodeListViewController is able to represent a non-example CBCNode instance as a UITableView. */ -@interface CBCNodeListViewController : UITableViewController +@interface CBCNodeListViewController : UIViewController /** Initializes a CBCNodeViewController instance with a non-example node. */ - (nonnull instancetype)initWithNode:(nonnull CBCNode *)node; - (nonnull instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE; +@property(nonatomic, strong, nonnull) UITableView *tableView; + /** The node that this view controller must represent. */ @property(nonatomic, strong, nonnull, readonly) CBCNode *node; diff --git a/catalog/CatalogByConvention/src/CBCNodeViewController.m b/catalog/CatalogByConvention/src/CBCNodeViewController.m index 26880f16d47..e3d9db9910c 100644 --- a/catalog/CatalogByConvention/src/CBCNodeViewController.m +++ b/catalog/CatalogByConvention/src/CBCNodeViewController.m @@ -89,7 +89,7 @@ - (instancetype)initWithNode:(CBCNode *)node { NSAssert(!_node.isExample, @"%@ cannot represent example nodes.", NSStringFromClass([self class])); - self = [super initWithStyle:UITableViewStyleGrouped]; + self = [super initWithNibName:nil bundle:nil]; if (self) { _node = node; @@ -103,6 +103,22 @@ - (instancetype)initWithStyle:(UITableViewStyle)style { return nil; } +- (void)viewDidLoad { + [super viewDidLoad]; + + self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; + self.tableView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + self.tableView.delegate = self; + self.tableView.dataSource = self; + [self.view addSubview:self.tableView]; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + + [self.tableView flashScrollIndicators]; +} + #pragma mark - UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { diff --git a/catalog/MDCCatalog/MDCNodeListViewController.swift b/catalog/MDCCatalog/MDCNodeListViewController.swift index 6c2e1452129..4684ef141db 100644 --- a/catalog/MDCCatalog/MDCNodeListViewController.swift +++ b/catalog/MDCCatalog/MDCNodeListViewController.swift @@ -74,7 +74,6 @@ class MDCNodeListViewController: CBCNodeListViewController { line.backgroundColor = UIColor(white: 0.72, alpha: 1) line.autoresizingMask = [.FlexibleTopMargin, .FlexibleWidth] headerContentView.addSubview(line) - self.tableView.backgroundColor = UIColor.whiteColor() } override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { @@ -87,6 +86,7 @@ class MDCNodeListViewController: CBCNodeListViewController { override func viewDidLoad() { super.viewDidLoad() + self.tableView.backgroundColor = UIColor.whiteColor() self.tableView.separatorColor = UIColor.clearColor() appBar.headerViewController.headerView.trackingScrollView = self.tableView From 917f68f953132ba345fdf70fe79d80c3d68b23b6 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 12:09:55 -0400 Subject: [PATCH 077/129] [Slider] Import the umbrella header in the typical use example. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D704 --- .../Slider/examples/supplemental/SliderTypicalUseSupplemental.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m b/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m index d1248a2aa3c..49fd4420372 100644 --- a/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m +++ b/components/Slider/examples/supplemental/SliderTypicalUseSupplemental.m @@ -5,7 +5,7 @@ #import -#import "MDCSlider.h" +#import "MaterialSlider.h" #import "MaterialTypography.h" #import "SliderTypicalUseSupplemental.h" From ac8642f4a0b9cd90946ff6fa65440a39b934ebbf Mon Sep 17 00:00:00 2001 From: randallli Date: Thu, 21 Apr 2016 12:13:20 -0400 Subject: [PATCH 078/129] [PageControl] Added test for updating the currentPage when the contentOffset changes Reviewers: ajsecord, #mdc_ios_owners Reviewed By: ajsecord, #mdc_ios_owners Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D702 --- .../tests/unit/PageControlExampleTests.m | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/components/PageControl/tests/unit/PageControlExampleTests.m b/components/PageControl/tests/unit/PageControlExampleTests.m index 7e981d86605..b29edf500b7 100644 --- a/components/PageControl/tests/unit/PageControlExampleTests.m +++ b/components/PageControl/tests/unit/PageControlExampleTests.m @@ -98,4 +98,21 @@ - (void)testSizeThatFits { XCTAssertFalse(CGRectEqualToRect(CGRectIntegral(pageControl.frame), nativePageControl.frame)); } +- (void)testCurrentPageGetsUpdatedWhenOffsetIsChanged { + // Given + CGRect frame = CGRectMake(0, 0, 100, 100); + MDCPageControl *pageControl = [[MDCPageControl alloc] init]; + pageControl.numberOfPages = 3; + UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:frame]; + scrollView.delegate = pageControl; + NSUInteger page = 2; + + // When + [scrollView setContentOffset:CGPointMake(frame.size.width * page, 0) + animated:YES]; + + // Then + XCTAssertEqual(pageControl.currentPage, page); +} + @end From e1f584adad2950a9c46ba7dba2da01461b6ea899 Mon Sep 17 00:00:00 2001 From: randallli Date: Thu, 21 Apr 2016 12:34:40 -0400 Subject: [PATCH 079/129] [PageControl] Fix crash when scrollView offset is set out of bounds of the numberOfPages Summary: If you set the content offset to larger than range of the page count the Page Control crashes because it tries to index out of bounds of an array. Reviewers: cjcox, #mdc_ios_owners, featherless Reviewed By: cjcox, #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D671 --- components/PageControl/src/MDCPageControl.h | 2 +- components/PageControl/src/MDCPageControl.m | 3 ++- .../tests/unit/PageControlExampleTests.m | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/components/PageControl/src/MDCPageControl.h b/components/PageControl/src/MDCPageControl.h index ee6c5b2cd08..4459bbdf849 100644 --- a/components/PageControl/src/MDCPageControl.h +++ b/components/PageControl/src/MDCPageControl.h @@ -37,7 +37,7 @@ @c scrollViewDidEndDecelerating: @c scrollViewDidEndScrollingAnimation: - */ + */ @interface MDCPageControl : UIControl #pragma mark Managing the page diff --git a/components/PageControl/src/MDCPageControl.m b/components/PageControl/src/MDCPageControl.m index 3da9d16e76d..c8870c12983 100644 --- a/components/PageControl/src/MDCPageControl.m +++ b/components/PageControl/src/MDCPageControl.m @@ -214,7 +214,8 @@ - (void)setCurrentPageIndicatorTintColor:(UIColor *)currentPageIndicatorTintColo - (NSInteger)scrolledPageNumber:(UIScrollView *)scrollView { // Returns paged index of scrollView. - return lround(scrollView.contentOffset.x / scrollView.frame.size.width); + NSInteger unboundedPageNumber = lround(scrollView.contentOffset.x / scrollView.frame.size.width); + return MAX(0, MIN(_numberOfPages - 1, unboundedPageNumber)); } - (CGFloat)scrolledPercentage:(UIScrollView *)scrollView { diff --git a/components/PageControl/tests/unit/PageControlExampleTests.m b/components/PageControl/tests/unit/PageControlExampleTests.m index b29edf500b7..e20a2540d1f 100644 --- a/components/PageControl/tests/unit/PageControlExampleTests.m +++ b/components/PageControl/tests/unit/PageControlExampleTests.m @@ -98,6 +98,22 @@ - (void)testSizeThatFits { XCTAssertFalse(CGRectEqualToRect(CGRectIntegral(pageControl.frame), nativePageControl.frame)); } +- (void)testScrollOffsetOutOfBoundsOfNumberOfPages { + // Given + CGRect frame = CGRectMake(0, 0, 100, 100); + MDCPageControl *pageControl = [[MDCPageControl alloc] init]; + pageControl.numberOfPages = 3; + UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:frame]; + scrollView.delegate = pageControl; + + // When + [scrollView setContentOffset:CGPointMake(frame.size.width * pageControl.numberOfPages, 0) + animated:YES]; + + // Then + XCTAssertEqual(pageControl.currentPage, pageControl.numberOfPages - 1); +} + - (void)testCurrentPageGetsUpdatedWhenOffsetIsChanged { // Given CGRect frame = CGRectMake(0, 0, 100, 100); From b54b2c96978fdb15d44c15b565f94ab43e60a22a Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 12:28:28 -0400 Subject: [PATCH 080/129] [Typography] Set autoresizing masks on Read Me example. Summary: Closes https://github.com/google/material-components-ios/issues/390. Reviewers: #mdc_ios_owners, iangordon Reviewed By: #mdc_ios_owners, iangordon Projects: #material_components_ios Differential Revision: http://codereview.cc/D707 --- .../examples/TypographySimpleExampleViewController.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/Typography/examples/TypographySimpleExampleViewController.m b/components/Typography/examples/TypographySimpleExampleViewController.m index be58755cb0c..d4bb30abe6a 100644 --- a/components/Typography/examples/TypographySimpleExampleViewController.m +++ b/components/Typography/examples/TypographySimpleExampleViewController.m @@ -30,7 +30,10 @@ - (void)viewDidLoad { label.alpha = [MDCTypography titleFontOpacity]; [label sizeToFit]; + label.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds)); + label.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin); + [self.view addSubview:label]; } From a7bb0b07b351099959c6cb849349110e83d4022b Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 12:23:41 -0400 Subject: [PATCH 081/129] [Catalog] Catalog table view uses grouped style instead of plain. Reviewers: #mdc_ios_owners, iangordon Reviewed By: #mdc_ios_owners, iangordon Projects: #material_components_ios Differential Revision: http://codereview.cc/D705 --- catalog/CatalogByConvention/src/CBCNodeViewController.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/catalog/CatalogByConvention/src/CBCNodeViewController.m b/catalog/CatalogByConvention/src/CBCNodeViewController.m index e3d9db9910c..cb6a8788f41 100644 --- a/catalog/CatalogByConvention/src/CBCNodeViewController.m +++ b/catalog/CatalogByConvention/src/CBCNodeViewController.m @@ -106,7 +106,8 @@ - (instancetype)initWithStyle:(UITableViewStyle)style { - (void)viewDidLoad { [super viewDidLoad]; - self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; + self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds + style:UITableViewStyleGrouped]; self.tableView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); self.tableView.delegate = self; self.tableView.dataSource = self; From d612f1fe94ddfeb92637b4c4795bd2c2b29a179a Mon Sep 17 00:00:00 2001 From: Adrian Secord Date: Thu, 21 Apr 2016 14:35:48 -0400 Subject: [PATCH 082/129] Added touch tracking for demos/videos. Triple-tap to enable. Fixes #413. Summary: Added MDCCatalogWindow. Added touch logic. Reviewers: #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D710 --- catalog/MDCCatalog.xcodeproj/project.pbxproj | 6 +- catalog/MDCCatalog/AppDelegate.swift | 2 +- catalog/MDCCatalog/MDCCatalogWindow.swift | 100 +++++++++++++++++++ 3 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 catalog/MDCCatalog/MDCCatalogWindow.swift diff --git a/catalog/MDCCatalog.xcodeproj/project.pbxproj b/catalog/MDCCatalog.xcodeproj/project.pbxproj index 5823e08e3cf..b64f8303b65 100644 --- a/catalog/MDCCatalog.xcodeproj/project.pbxproj +++ b/catalog/MDCCatalog.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0B4A5E6D1CC9307A00D2AC5D /* MDCCatalogWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B4A5E6C1CC9307A00D2AC5D /* MDCCatalogWindow.swift */; }; 22F173D236D54720CF0F3357 /* Pods_MDCCatalog.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B8F1AA8D28612EA405B5C1A /* Pods_MDCCatalog.framework */; }; 5D090E571C9AEB8D0061344A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5D090E341C9AEB8C0061344A /* Localizable.strings */; }; 6642AB811CBDBE0900F5B1D7 /* CBCRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = 6642AB801CBDBE0900F5B1D7 /* CBCRuntime.m */; }; @@ -37,6 +38,7 @@ /* Begin PBXFileReference section */ 097F9AF44329F8F5FC325268 /* Pods-MDCCatalog.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MDCCatalog.release.xcconfig"; path = "Pods/Target Support Files/Pods-MDCCatalog/Pods-MDCCatalog.release.xcconfig"; sourceTree = ""; }; + 0B4A5E6C1CC9307A00D2AC5D /* MDCCatalogWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogWindow.swift; sourceTree = ""; }; 0D185DDA19244A33A78863F7 /* Pods-MDCCatalog-MDCCatalogUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MDCCatalog-MDCCatalogUnitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MDCCatalog-MDCCatalogUnitTests/Pods-MDCCatalog-MDCCatalogUnitTests.release.xcconfig"; sourceTree = ""; }; 0F092FBA334F9A6817C4B9ED /* Pods-MDCCatalog-MDCCatalogUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MDCCatalog-MDCCatalogUnitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MDCCatalog-MDCCatalogUnitTests/Pods-MDCCatalog-MDCCatalogUnitTests.debug.xcconfig"; sourceTree = ""; }; 12970BE3EA0028A12E1427AD /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; @@ -189,9 +191,10 @@ 665A34D91C6BD01900962055 /* MDCCatalog-Bridging-Header.h */, DEF64EA31C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift */, DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */, + DE5BD5AF1CB3F8FF00D8D75D /* MDCCatalogTiles */, 6681FDFC1CC586660013A0C7 /* MDCCatalogTileView.swift */, + 0B4A5E6C1CC9307A00D2AC5D /* MDCCatalogWindow.swift */, 664524B81C6BA62A001ADBF8 /* MDCNodeListViewController.swift */, - DE5BD5AF1CB3F8FF00D8D75D /* MDCCatalogTiles */, 665A34DE1C6BDAE700962055 /* Resources */, ); path = MDCCatalog; @@ -454,6 +457,7 @@ DE309CF31C8DEB8400E73247 /* MDCCatalogComponentsController.swift in Sources */, 6681FDFD1CC586660013A0C7 /* MDCCatalogTileView.swift in Sources */, DE19448C1CBD9E40009E0321 /* MDCCatalogTileDataInk.m in Sources */, + 0B4A5E6D1CC9307A00D2AC5D /* MDCCatalogWindow.swift in Sources */, 664524B91C6BA62A001ADBF8 /* MDCNodeListViewController.swift in Sources */, DE19448D1CBD9E40009E0321 /* MDCCatalogTileDataMisc.m in Sources */, DE19448B1CBD9E40009E0321 /* MDCCatalogTileDataHeaderStackView.m in Sources */, diff --git a/catalog/MDCCatalog/AppDelegate.swift b/catalog/MDCCatalog/AppDelegate.swift index 18fd32960ff..c6fe6bb76c4 100644 --- a/catalog/MDCCatalog/AppDelegate.swift +++ b/catalog/MDCCatalog/AppDelegate.swift @@ -22,7 +22,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - self.window = UIWindow(frame: UIScreen.mainScreen().bounds) + self.window = MDCCatalogWindow(frame: UIScreen.mainScreen().bounds) let tree = CBCCreateNavigationTree() diff --git a/catalog/MDCCatalog/MDCCatalogWindow.swift b/catalog/MDCCatalog/MDCCatalogWindow.swift new file mode 100644 index 00000000000..763c82411b2 --- /dev/null +++ b/catalog/MDCCatalog/MDCCatalogWindow.swift @@ -0,0 +1,100 @@ +/* +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 UIKit + +/** + A custom UIWindow that displays the user's touches for recording video or demos. + + Triple tapping anywhere will toggle the visible touches. + */ +class MDCCatalogWindow: UIWindow { + var enabled = false + + private var views = [NSNumber: UIView]() + + override func sendEvent(event: UIEvent) { + let touches = event.allTouches()! + for touch in touches { + switch touch.phase { + case .Began: + if (enabled) { + beginDisplayingTouch(touch) + } + continue + case .Moved: + updateTouch(touch) + continue + case .Stationary: + continue + case .Ended: + if (touch.tapCount == 3) { + enabled = !enabled + } + fallthrough + case .Cancelled: + endDisplayingTouch(touch) + continue + } + } + + super.sendEvent(event) + } + + private func beginDisplayingTouch(touch: UITouch) { + let view = MDCTouchView() + view.center = touch.locationInView(self) + views[touch.hash] = view + self.addSubview(view) + } + + private func updateTouch(touch: UITouch) { + views[touch.hash]?.center = touch.locationInView(self) + } + + private func endDisplayingTouch(touch: UITouch) { + views[touch.hash]?.removeFromSuperview() + views[touch.hash] = nil + } +} + +/** A circular view that represents a user's touch. */ +class MDCTouchView: UIView { + private let touchCircleSize: CGFloat = 80 + private let touchCircleAlpha: CGFloat = 0.25 + private let touchCircleColor = UIColor.redColor() + private let touchCircleBorderColor = UIColor.blackColor() + private let touchCircleBorderWidth: CGFloat = 1 + + override init(frame: CGRect) { + super.init(frame: frame) + commonMDCTouchViewInit() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonMDCTouchViewInit() + } + + private func commonMDCTouchViewInit() { + self.backgroundColor = touchCircleColor + self.alpha = touchCircleAlpha + self.frame.size = CGSizeMake(touchCircleSize, touchCircleSize) + self.layer.cornerRadius = touchCircleSize / 2 + self.layer.borderColor = touchCircleBorderColor.CGColor + self.layer.borderWidth = touchCircleBorderWidth + } +} From 1229fc7b266eb939976a652a695f3d0b201cfea3 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 15:37:16 -0400 Subject: [PATCH 083/129] [FlexibleHeader] Add statusBarCanOverlapHeader property to MDCFlexibleHeaderView. Summary: This property makes it possible to use Flexible Header in popovers and non-fullscreen modals. Closes https://github.com/google/material-components-ios/issues/349. Reviewers: ajsecord, #mdc_ios_owners Reviewed By: ajsecord, #mdc_ios_owners Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D673 --- .../FlexibleHeaderConfiguratorExample.m | 4 +++ .../src/MDCFlexibleHeaderView.h | 15 ++++++++++ .../src/MDCFlexibleHeaderView.m | 28 ++++++++++++++++--- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m b/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m index 3a21cc2b68a..9f6ed2ef59e 100644 --- a/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m +++ b/components/FlexibleHeader/examples/FlexibleHeaderConfiguratorExample.m @@ -42,6 +42,10 @@ - (void)field:(FlexibleHeaderConfiguratorField)field didChangeValue:(NSNumber *) case FlexibleHeaderConfiguratorFieldHideStatusBar: { self.overrideStatusBarHidden = [value boolValue]; + + BOOL statusBarCanBeVisible = !self.overrideStatusBarHidden; + headerView.statusBarHintCanOverlapHeader = statusBarCanBeVisible; + [UIView animateWithDuration:0.4 animations:^{ [self setNeedsStatusBarAppearanceUpdate]; diff --git a/components/FlexibleHeader/src/MDCFlexibleHeaderView.h b/components/FlexibleHeader/src/MDCFlexibleHeaderView.h index ef06d051c87..ba1dc77778b 100644 --- a/components/FlexibleHeader/src/MDCFlexibleHeaderView.h +++ b/components/FlexibleHeader/src/MDCFlexibleHeaderView.h @@ -328,6 +328,21 @@ typedef NS_ENUM(NSInteger, MDCFlexibleHeaderScrollPhase) { */ @property(nonatomic) BOOL canOverExtend; +/** + A hint stating whether or not the operating system's status bar frame can ever overlap the header's + frame. + + This property is enabled by default with the expectation that the flexible header will primarily + be used in full-screen settings on the phone. + + Disabling this property informs the flexible header that it should not concern itself with the + status bar in any manner. shiftBehavior .EnabledWithStatusBar will be treated simply as .Enabled + in this case. + + Default: YES + */ +@property(nonatomic) BOOL statusBarHintCanOverlapHeader; + @property(nonatomic) float visibleShadowOpacity; ///< The visible shadow opacity. Default: 0.4 #pragma mark Scroll View Tracking diff --git a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m index 68a268e8ed8..de1fe7c92bc 100644 --- a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m +++ b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m @@ -173,6 +173,7 @@ - (instancetype)initWithFrame:(CGRect)frame { _trackedScrollViews = [NSMapTable mapTableWithKeyOptions:keyOptions valueOptions:valueOptions]; _headerContentImportance = MDCFlexibleHeaderContentImportanceDefault; + _statusBarHintCanOverlapHeader = YES; _minimumHeight = kFlexibleHeaderDefaultHeight; _maximumHeight = kFlexibleHeaderDefaultHeight; @@ -397,12 +398,20 @@ - (CGFloat)fhv_projectedHeaderBottomEdge { } - (CGFloat)fhv_accumulatorMax { - return (self.hidesStatusBarWhenCollapsed ? _minimumHeight - : _minimumHeight - kExpectedStatusBarHeight); + BOOL shouldCollapseToStatusBar = [self fhv_shouldCollapseToStatusBar]; + return (shouldCollapseToStatusBar ? _minimumHeight - kExpectedStatusBarHeight : _minimumHeight); } #pragma mark Logical short forms +- (BOOL)fhv_shouldAllowShifting { + return self.hidesStatusBarWhenCollapsed && self.statusBarHintCanOverlapHeader; +} + +- (BOOL)fhv_shouldCollapseToStatusBar { + return !self.hidesStatusBarWhenCollapsed && self.statusBarHintCanOverlapHeader; +} + - (BOOL)fhv_canShiftOffscreen { return ((_shiftBehavior == MDCFlexibleHeaderShiftBehaviorEnabled || _shiftBehavior == MDCFlexibleHeaderShiftBehaviorEnabledWithStatusBar) && @@ -439,7 +448,7 @@ - (void)fhv_recalculatePhase { _scrollPhase = MDCFlexibleHeaderScrollPhaseShifting; _scrollPhaseValue = frame.origin.y + _minimumHeight; CGFloat adjustedHeight = _minimumHeight; - if (!self.hidesStatusBarWhenCollapsed) { + if ([self fhv_shouldCollapseToStatusBar]) { adjustedHeight -= kExpectedStatusBarHeight; } if (adjustedHeight > 0) { @@ -892,6 +901,17 @@ - (BOOL)hidesStatusBarWhenCollapsed { !_trackingScrollView.pagingEnabled); } +- (void)setstatusBarHintCanOverlapHeader:(BOOL)statusBarHintCanOverlapHeader { + if (_statusBarHintCanOverlapHeader == statusBarHintCanOverlapHeader) { + return; + } + _statusBarHintCanOverlapHeader = statusBarHintCanOverlapHeader; + + _statusBarShifter.enabled = [self fhv_shouldAllowShifting]; + + [self fhv_startDisplayLink]; +} + - (void)setShiftBehavior:(MDCFlexibleHeaderShiftBehavior)shiftBehavior { if (_shiftBehavior == shiftBehavior) { return; @@ -900,7 +920,7 @@ - (void)setShiftBehavior:(MDCFlexibleHeaderShiftBehavior)shiftBehavior { shiftBehavior == MDCFlexibleHeaderShiftBehaviorDisabled); _shiftBehavior = shiftBehavior; - _statusBarShifter.enabled = self.hidesStatusBarWhenCollapsed; + _statusBarShifter.enabled = [self fhv_shouldAllowShifting]; if (needsShiftOnScreen) { _wantsToBeHidden = NO; From 25c20a9abf9a6c9d30397dc7387646ead5fcafc4 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 13:01:59 -0400 Subject: [PATCH 084/129] [Typography] Typography hero demo is now a UITableView. Summary: Closes https://github.com/google/material-components-ios/issues/389. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D709 --- .../TypographyExamplesViewController.swift | 218 ------------------ .../examples/TypographyFontListExample.swift | 152 ++++++++++++ 2 files changed, 152 insertions(+), 218 deletions(-) delete mode 100644 components/Typography/examples/TypographyExamplesViewController.swift create mode 100644 components/Typography/examples/TypographyFontListExample.swift diff --git a/components/Typography/examples/TypographyExamplesViewController.swift b/components/Typography/examples/TypographyExamplesViewController.swift deleted file mode 100644 index 1c7c683f83d..00000000000 --- a/components/Typography/examples/TypographyExamplesViewController.swift +++ /dev/null @@ -1,218 +0,0 @@ -/* -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 MaterialComponents - -class TypographyExampleViewCell: UICollectionViewCell { - - var cellSize = CGSizeZero - let textView = UITextView() - let label = UILabel() - let offset = CGFloat(-5) - - override init(frame: CGRect) { - super.init(frame: frame) - self.backgroundColor = UIColor.whiteColor() - textView.text = "Example Text" - textView.editable = false - textView.scrollEnabled = false - - textView.contentInset = UIEdgeInsetsMake(offset, offset, offset, offset); - self.addSubview(textView) - - label.text = "Font Style Name" - label.alpha = MDCTypography.captionFontOpacity() - label.font = MDCTypography.captionFont() - label.frame = CGRectMake(0, - frame.size.height - (label.font.pointSize + 2), frame.size.width, label.font.pointSize + 2) - label.autoresizingMask = [.FlexibleTopMargin, .FlexibleWidth] - self.addSubview(label) - } - - required init(coder: NSCoder) { - super.init(coder: coder)! - } - - func populateCell(text : String, font : UIFont, opacity: CGFloat, name: String) { - textView.text = text - textView.font = font - textView.alpha = opacity - label.text = name - - let fixedWidth = self.frame.width - textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.max)) - let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.max)) - var newFrame = textView.frame - newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height) - textView.frame = newFrame; - cellSize = newFrame.size - } - - static func cellSize(width: CGFloat, text : String, font : UIFont) -> CGSize { - let textView = UITextView() - textView.text = text - textView.font = font - - let fixedWidth = width - textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.max)) - let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.max)) - var newFrame = textView.frame - let height = newSize.height + MDCTypography.captionFont().pointSize - 8 - newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: height) - textView.frame = newFrame; - return newFrame.size - } - -} - -class TypographyExamplesViewController: UICollectionViewController { - - let strings = [ - "Material Design Components", - "A quick brown fox jumped over the lazy dog.", - "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "abcdefghijklmnopqrstuvwxyz", - "1234567890", - "!@#$%^&*()-=_+[]\\;',./<>?:\"" - ] - - let fonts = [ - - // Common UI fonts. - MDCTypography.headlineFont(), - MDCTypography.titleFont(), - MDCTypography.subheadFont(), - MDCTypography.body2Font(), - MDCTypography.body1Font(), - MDCTypography.captionFont(), - MDCTypography.buttonFont(), - - // Display fonts (extra large fonts) - MDCTypography.display1Font(), - MDCTypography.display2Font(), - MDCTypography.display3Font(), - MDCTypography.display4Font() - ] - - let fontOpacities = [ - - // Common UI fonts. - MDCTypography.headlineFontOpacity(), - MDCTypography.titleFontOpacity(), - MDCTypography.subheadFontOpacity(), - MDCTypography.body2FontOpacity(), - MDCTypography.body1FontOpacity(), - MDCTypography.captionFontOpacity(), - MDCTypography.buttonFontOpacity(), - - // Display fonts (extra large fonts) - MDCTypography.display1FontOpacity(), - MDCTypography.display2FontOpacity(), - MDCTypography.display3FontOpacity(), - MDCTypography.display4FontOpacity() - ] - - let fontStyleNames = [ - - // Common UI fonts. - "Headline Font", - "Title Font", - "Subhead Font", - "Body 2 Font", - "Body 1 Font", - "Caption Font", - "Button Font", - - // Display fonts (extra large fonts) - "Display 1 Font", - "Display 2 Font", - "Display 3 Font", - "Display 4 Font" - ] - - init() { - let flowLayout = UICollectionViewFlowLayout() - flowLayout.sectionInset = UIEdgeInsetsMake(10, 0, 10, 0) - super.init(collectionViewLayout: flowLayout) - self.collectionView?.registerClass(TypographyExampleViewCell.self, - forCellWithReuseIdentifier: "TypographyExampleViewCell") - self.collectionView?.backgroundColor = UIColor.whiteColor() - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { - return strings.count - } - - override func collectionView(collectionView: UICollectionView, - numberOfItemsInSection section: Int) -> Int { - return fonts.count - } - - override func collectionView(collectionView: UICollectionView, - cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { - let cell = collectionView.dequeueReusableCellWithReuseIdentifier("TypographyExampleViewCell", - forIndexPath: indexPath) as! TypographyExampleViewCell - - let itemNum:NSInteger = indexPath.row; - let sectionNum:NSInteger = indexPath.section; - - var text = strings[sectionNum] - let font = fonts[itemNum] - let opacity = fontOpacities[itemNum] - let name = fontStyleNames[itemNum] - if (font.pointSize > 100 && text == strings[0]) { - text = "MDC" - } - cell.populateCell(text, font: font, opacity: opacity, name: name) - - return cell - } - - func collectionView(collectionView: UICollectionView, - layout collectionViewLayout: UICollectionViewLayout, - sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { - - let itemNum:NSInteger = indexPath.row; - let sectionNum:NSInteger = indexPath.section; - - var text = strings[sectionNum] - let font = fonts[itemNum] - if (font.pointSize > 100 && text == strings[0]) { - text = "MDC" - } - let size = - TypographyExampleViewCell.cellSize(self.view.frame.width - 20, text: text, font: font) - return size - } - - class func catalogBreadcrumbs() -> [String] { - return ["Typography and Fonts", "Typography"] - } - - class func catalogDescription() -> String { - return "The Typography component provides methods for displaying text using the type sizes and" - + " opacities from the Material Design specifications." - } - - class func catalogIsPrimaryDemo() -> Bool { - return true - } - -} diff --git a/components/Typography/examples/TypographyFontListExample.swift b/components/Typography/examples/TypographyFontListExample.swift new file mode 100644 index 00000000000..1564e70242f --- /dev/null +++ b/components/Typography/examples/TypographyFontListExample.swift @@ -0,0 +1,152 @@ +/* +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 MaterialComponents + +// This example is primarily a visual example of the various Typography fonts. The code below is a +// standard UIKit table view configuration using Auto Layout for height calculation. + +class TypographyFontListExampleViewController: UITableViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + self.tableView.separatorStyle = .None + + self.tableView.rowHeight = UITableViewAutomaticDimension + self.tableView.estimatedRowHeight = 50 + } + + override func numberOfSectionsInTableView(tableView: UITableView) -> Int { + return strings.count + } + + override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return fonts.count + } + + override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + var cell = tableView.dequeueReusableCellWithIdentifier("cell") + if cell == nil { + cell = UITableViewCell(style: .Subtitle, reuseIdentifier: "cell") + } + cell!.textLabel!.text = strings[indexPath.section] + cell!.textLabel!.font = fonts[indexPath.row] + cell!.textLabel!.alpha = fontOpacities[indexPath.row] + cell!.textLabel!.numberOfLines = 0 + cell!.textLabel!.lineBreakMode = .ByWordWrapping + + if cell!.textLabel!.font.pointSize > 100 && indexPath.section == 0 { + cell!.textLabel!.text = "MDC" + } + + cell!.detailTextLabel!.text = fontStyleNames[indexPath.section] + cell!.detailTextLabel!.font = MDCTypography.captionFont() + cell!.detailTextLabel!.alpha = MDCTypography.captionFontOpacity() + cell!.selectionStyle = .None + + return cell! + } + + override init(style: UITableViewStyle) { + super.init(style: .Plain) + + self.title = "Font list" + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + let strings = [ + "Material Design Components", + "A quick brown fox jumped over the lazy dog.", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "abcdefghijklmnopqrstuvwxyz", + "1234567890", + "!@#$%^&*()-=_+[]\\;',./<>?:\"" + ] + + let fonts = [ + + // Common UI fonts. + MDCTypography.headlineFont(), + MDCTypography.titleFont(), + MDCTypography.subheadFont(), + MDCTypography.body2Font(), + MDCTypography.body1Font(), + MDCTypography.captionFont(), + MDCTypography.buttonFont(), + + // Display fonts (extra large fonts) + MDCTypography.display1Font(), + MDCTypography.display2Font(), + MDCTypography.display3Font(), + MDCTypography.display4Font() + ] + + let fontOpacities = [ + + // Common UI fonts. + MDCTypography.headlineFontOpacity(), + MDCTypography.titleFontOpacity(), + MDCTypography.subheadFontOpacity(), + MDCTypography.body2FontOpacity(), + MDCTypography.body1FontOpacity(), + MDCTypography.captionFontOpacity(), + MDCTypography.buttonFontOpacity(), + + // Display fonts (extra large fonts) + MDCTypography.display1FontOpacity(), + MDCTypography.display2FontOpacity(), + MDCTypography.display3FontOpacity(), + MDCTypography.display4FontOpacity() + ] + + let fontStyleNames = [ + + // Common UI fonts. + "Headline Font", + "Title Font", + "Subhead Font", + "Body 2 Font", + "Body 1 Font", + "Caption Font", + "Button Font", + + // Display fonts (extra large fonts) + "Display 1 Font", + "Display 2 Font", + "Display 3 Font", + "Display 4 Font" + ] +} + +// MARK: Catalog by convention +extension TypographyFontListExampleViewController { + class func catalogBreadcrumbs() -> [String] { + return ["Typography and Fonts", "Typography"] + } + + class func catalogDescription() -> String { + return "The Typography component provides methods for displaying text using the type sizes and" + + " opacities from the Material Design specifications." + } + + class func catalogIsPrimaryDemo() -> Bool { + return true + } +} From f49fda0f141ec7c228016d67ca758eda038aebf2 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 14:17:44 -0400 Subject: [PATCH 085/129] [Catalog] When wrapping examples in an App Bar container use an absolute frame rather than a relative one. Summary: This avoids situations where the example's frame may have already had some form of offset. Closes https://github.com/google/material-components-ios/issues/405. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D708 --- catalog/MDCCatalog/MDCNodeListViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/catalog/MDCCatalog/MDCNodeListViewController.swift b/catalog/MDCCatalog/MDCNodeListViewController.swift index 4684ef141db..1a51eeb8913 100644 --- a/catalog/MDCCatalog/MDCNodeListViewController.swift +++ b/catalog/MDCCatalog/MDCNodeListViewController.swift @@ -280,8 +280,8 @@ class MDCNodeListViewController: CBCNodeListViewController { var contentFrame = container.contentViewController.view.frame let headerSize = headerView.sizeThatFits(container.contentViewController.view.frame.size) - contentFrame.origin.y += headerSize.height - contentFrame.size.height -= headerSize.height + contentFrame.origin.y = headerSize.height + contentFrame.size.height = self.view.bounds.size.height - headerSize.height container.contentViewController.view.frame = contentFrame vc = container From dd3c159cec529ef21af00198964fc29e86349b52 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 12:24:54 -0400 Subject: [PATCH 086/129] [Catalog] Remove footer from catalog table views. Summary: This footer was covering cells at the bottom of the table view. Closes https://github.com/google/material-components-ios/issues/415. Reviewers: ajsecord, #mdc_ios_owners Reviewed By: ajsecord, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D706 --- .../MDCNodeListViewController.swift | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/catalog/MDCCatalog/MDCNodeListViewController.swift b/catalog/MDCCatalog/MDCNodeListViewController.swift index 1a51eeb8913..e3eac8b310a 100644 --- a/catalog/MDCCatalog/MDCNodeListViewController.swift +++ b/catalog/MDCCatalog/MDCNodeListViewController.swift @@ -39,7 +39,6 @@ class MDCNodeListViewController: CBCNodeListViewController { let descriptionSectionHeight = CGFloat(100) let additionalExamplesSectionHeight = CGFloat(50) let rowHeight = CGFloat(50) - let footerHeight = CGFloat(20) var componentDescription = "" enum Section: Int { @@ -86,8 +85,10 @@ class MDCNodeListViewController: CBCNodeListViewController { override func viewDidLoad() { super.viewDidLoad() + self.tableView.backgroundColor = UIColor.whiteColor() - self.tableView.separatorColor = UIColor.clearColor() + self.tableView.separatorStyle = .None + appBar.headerViewController.headerView.trackingScrollView = self.tableView appBar.addSubviewsToParent() @@ -161,10 +162,12 @@ class MDCNodeListViewController: CBCNodeListViewController { let sectionView = UIView(frame: sectionViewFrame) sectionView.backgroundColor = UIColor.whiteColor() - let lineDivider = UIView(frame: CGRectMake(0,0,tableView.frame.size.width, 1)) - lineDivider.backgroundColor = UIColor(white: 0.85, alpha: 1) - lineDivider.autoresizingMask = .FlexibleWidth - sectionView.addSubview(lineDivider) + if section == 1 { + let lineDivider = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 1)) + lineDivider.backgroundColor = UIColor(white: 0.85, alpha: 1) + lineDivider.autoresizingMask = .FlexibleWidth + sectionView.addSubview(lineDivider) + } let label = UILabel() label.text = sectionNames[section] @@ -201,12 +204,6 @@ class MDCNodeListViewController: CBCNodeListViewController { return additionalExamplesSectionHeight } - override func tableView(tableView: UITableView, - willDisplayFooterView view: UIView, forSection section: Int) { - let footerView = view as! UITableViewHeaderFooterView - footerView.contentView.backgroundColor = UIColor.whiteColor() - } - override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if (section == Section.Description.rawValue) { return 1 @@ -238,11 +235,6 @@ class MDCNodeListViewController: CBCNodeListViewController { return rowHeight } - override func tableView(tableView: UITableView, - heightForFooterInSection section: Int) -> CGFloat { - return footerHeight - } - // MARK: UITableViewDelegate override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { From acfb21d6e85cbd8bda1129f3b7c52e0b93c50363 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 15:45:47 -0400 Subject: [PATCH 087/129] [Catalog] Move the cell deselection into Catalog by Convention. Reviewers: iangordon, #mdc_ios_owners Reviewed By: iangordon, #mdc_ios_owners Subscribers: iangordon Projects: #material_components_ios Differential Revision: http://codereview.cc/D698 --- .../src/CBCNodeViewController.m | 18 ++++++++++++++++++ .../MDCCatalog/MDCNodeListViewController.swift | 11 ----------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/catalog/CatalogByConvention/src/CBCNodeViewController.m b/catalog/CatalogByConvention/src/CBCNodeViewController.m index cb6a8788f41..2d949525a9c 100644 --- a/catalog/CatalogByConvention/src/CBCNodeViewController.m +++ b/catalog/CatalogByConvention/src/CBCNodeViewController.m @@ -114,6 +114,24 @@ - (void)viewDidLoad { [self.view addSubview:self.tableView]; } +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + NSIndexPath *selectedRow = self.tableView.indexPathForSelectedRow; + if (selectedRow) { + [[self transitionCoordinator] animateAlongsideTransition:^(id context) { + [self.tableView deselectRowAtIndexPath:selectedRow animated:YES]; + } + completion:^(id context) { + if ([context isCancelled]) { + [self.tableView selectRowAtIndexPath:selectedRow + animated:NO + scrollPosition:UITableViewScrollPositionNone]; + } + }]; + } +} + - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; diff --git a/catalog/MDCCatalog/MDCNodeListViewController.swift b/catalog/MDCCatalog/MDCNodeListViewController.swift index e3eac8b310a..7487d726d99 100644 --- a/catalog/MDCCatalog/MDCNodeListViewController.swift +++ b/catalog/MDCCatalog/MDCNodeListViewController.swift @@ -98,17 +98,6 @@ class MDCNodeListViewController: CBCNodeListViewController { super.viewWillAppear(animated) self.navigationController?.setNavigationBarHidden(true, animated: animated) - - if let selectedRowIndexPath = tableView.indexPathForSelectedRow, let _ = transitionCoordinator() { - transitionCoordinator()?.animateAlongsideTransition({ (context) -> Void in - self.tableView.deselectRowAtIndexPath(selectedRowIndexPath, animated: true) - }, completion: { (context) -> Void in - if context.isCancelled() { - self.tableView.selectRowAtIndexPath(selectedRowIndexPath, animated: false, - scrollPosition: .None) - } - }) - } } // MARK: UIScrollViewDelegate From 29f35a9add41e797c34acb0cf7b9714bd235140d Mon Sep 17 00:00:00 2001 From: Adrian Secord Date: Thu, 21 Apr 2016 15:45:42 -0400 Subject: [PATCH 088/129] Added fade-out of touch indicators for better recording. Reviewers: #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D712 --- catalog/MDCCatalog/MDCCatalogWindow.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/catalog/MDCCatalog/MDCCatalogWindow.swift b/catalog/MDCCatalog/MDCCatalogWindow.swift index 763c82411b2..193a2070c1d 100644 --- a/catalog/MDCCatalog/MDCCatalogWindow.swift +++ b/catalog/MDCCatalog/MDCCatalogWindow.swift @@ -24,6 +24,7 @@ import UIKit class MDCCatalogWindow: UIWindow { var enabled = false + private let fadeDuration: NSTimeInterval = 0.2 private var views = [NSNumber: UIView]() override func sendEvent(event: UIEvent) { @@ -66,8 +67,16 @@ class MDCCatalogWindow: UIWindow { } private func endDisplayingTouch(touch: UITouch) { - views[touch.hash]?.removeFromSuperview() + let view = views[touch.hash] views[touch.hash] = nil + + UIView.animateWithDuration(fadeDuration, + animations: { + view?.alpha = 0 + }, + completion: { finished in + view?.removeFromSuperview() + }) } } From d0683a0e0bbe73fb36641c9ea92a5e715c438d19 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Thu, 21 Apr 2016 15:38:00 -0400 Subject: [PATCH 089/129] [Catalog] Change ink demo shapes to represent pseudo button/FAB, move layout code into supplemental Summary: Screenshot http://codereview.cc/M26 Reviewers: featherless, #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D711 --- components/Ink/examples/InkTypicalUse.m | 80 ++++++ .../Ink/examples/InkTypicalUseExample.m | 243 ------------------ .../supplemental/InkTypicalUseSupplemental.h | 25 ++ .../supplemental/InkTypicalUseSupplemental.m | 138 ++++++++++ 4 files changed, 243 insertions(+), 243 deletions(-) create mode 100644 components/Ink/examples/InkTypicalUse.m delete mode 100644 components/Ink/examples/InkTypicalUseExample.m create mode 100644 components/Ink/examples/supplemental/InkTypicalUseSupplemental.h create mode 100644 components/Ink/examples/supplemental/InkTypicalUseSupplemental.m diff --git a/components/Ink/examples/InkTypicalUse.m b/components/Ink/examples/InkTypicalUse.m new file mode 100644 index 00000000000..60f9f34fc69 --- /dev/null +++ b/components/Ink/examples/InkTypicalUse.m @@ -0,0 +1,80 @@ +/* + Copyright 2015-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 "MaterialInk.h" + +#import "InkTypicalUseSupplemental.h" + +@interface InkTypicalUseViewController () + +@property(nonatomic, strong) NSMutableArray *inkTouchControllers; // MDCInkTouchControllers. + +@end + +@implementation InkTypicalUseViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + CGFloat spacing = 16; + CGRect customFrame = CGRectMake(0, 0, 200, 200); + CGRect unboundedFrame = CGRectMake(spacing / 2, + spacing / 2, + customFrame.size.width - spacing, + customFrame.size.height - spacing); + + // ExampleShapes is a custom UIView with several subviews of various shapes. + self.boundedShapes = [[ExampleShapes alloc] initWithFrame:customFrame]; + self.unboundedShape = [[UIView alloc] initWithFrame:unboundedFrame]; + + [self setupExampleViews]; + + _inkTouchControllers = [[NSMutableArray alloc] init]; + + for (UIView *view in self.boundedShapes.subviews) { + MDCInkTouchController *inkTouchController = [[MDCInkTouchController alloc] initWithView:view]; + inkTouchController.delegate = self; + [inkTouchController addInkView]; + [_inkTouchControllers addObject:inkTouchController]; + } + [self.view addSubview:self.boundedShapes]; + + MDCInkTouchController *inkTouchController = + [[MDCInkTouchController alloc] initWithView:self.unboundedShape]; + inkTouchController.delegate = self; + [inkTouchController addInkView]; + + UIColor *blueColor = [UIColor colorWithRed:0.012 green:0.663 blue:0.957 alpha:0.2]; + inkTouchController.defaultInkView.inkColor = blueColor; + inkTouchController.defaultInkView.inkStyle = MDCInkStyleUnbounded; + [_inkTouchControllers addObject:inkTouchController]; + [self.view addSubview:self.unboundedShape]; +} + +#pragma mark - Private + +- (void)inkTouchController:(MDCInkTouchController *)inkTouchController + didProcessInkView:(MDCInkView *)inkView + atTouchLocation:(CGPoint)location { + NSLog(@"InkTouchController %p did process ink view: %p at touch location: %@", + inkTouchController, + inkView, + NSStringFromCGPoint(location)); +} + +@end diff --git a/components/Ink/examples/InkTypicalUseExample.m b/components/Ink/examples/InkTypicalUseExample.m deleted file mode 100644 index f6bdb2b5eaf..00000000000 --- a/components/Ink/examples/InkTypicalUseExample.m +++ /dev/null @@ -1,243 +0,0 @@ -/* - Copyright 2015-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 "MaterialInk.h" - -// A set of UILabels in an variety of shapes to tap on. -// -// Something like: -// -// .----.----.----.----. .----. -// . . . . -// . . . . -// . . . . -// . . . . -// . . . . -// . . . . -// . . . . -// ----.----.----.---- ---- -// -// .----.----.----.----. .----. -// . . . . -// . . . . -// ----.----.----.---- ---- -@interface ExampleShapes : UIView -@property(nonatomic) CGFloat padding; -@property(nonatomic) CGFloat ratio; -@property(nonatomic, strong) UIColor *shapeColor; -@property(nonatomic, copy) NSString *title; -@end - -@interface InkTypicalUseViewController : UIViewController -@end - -@interface InkTypicalUseViewController () -@property(nonatomic) CGFloat shapeDimension; -@property(nonatomic) CGFloat spacing; -@property(nonatomic, strong) ExampleShapes *boundedShapes; -@property(nonatomic, strong) NSMutableArray *inkTouchControllers; // MDCInkTouchControllers. -@property(nonatomic, strong) UIView *unboundedShape; -@end - -@implementation InkTypicalUseViewController - -// TODO: Support other categorizational methods. -+ (NSArray *)catalogBreadcrumbs { - return @[ @"Ink", @"Ink" ]; -} - -+ (NSString *)catalogDescription { - return @"The Ink component provides a radial action in the form of a visual ripple of ink" - " expanding outward from the user's touch."; -} - -+ (BOOL)catalogIsPrimaryDemo { - return YES; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - - self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1]; - _inkTouchControllers = [[NSMutableArray alloc] init]; - - self.spacing = 16; - self.shapeDimension = 200; - CGRect customFrame = CGRectMake(0, 0, self.shapeDimension, self.shapeDimension); - self.boundedShapes = [[ExampleShapes alloc] initWithFrame:customFrame]; - self.boundedShapes.title = @"Bounded"; - self.boundedShapes.autoresizingMask = - UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | - UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin; - self.boundedShapes.shapeColor = [UIColor whiteColor]; - for (UIView *view in self.boundedShapes.subviews) { - MDCInkTouchController *inkTouchController = [[MDCInkTouchController alloc] initWithView:view]; - inkTouchController.delegate = self; - [inkTouchController addInkView]; - [_inkTouchControllers addObject:inkTouchController]; - } - [self.view addSubview:self.boundedShapes]; - - CGRect unboundedFrame = CGRectMake(self.spacing / 2, - self.spacing / 2, - customFrame.size.width - self.spacing, - customFrame.size.height - self.spacing); - self.unboundedShape = [[UIView alloc] initWithFrame:unboundedFrame]; - self.unboundedShape.autoresizingMask = - UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | - UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin; - self.unboundedShape.backgroundColor = [UIColor whiteColor]; - MDCInkTouchController *inkTouchController = - [[MDCInkTouchController alloc] initWithView:self.unboundedShape]; - inkTouchController.delegate = self; - [inkTouchController addInkView]; - UIColor *blueColor = [UIColor colorWithRed:0.012 green:0.663 blue:0.957 alpha:0.2]; - inkTouchController.defaultInkView.inkColor = blueColor; - inkTouchController.defaultInkView.inkStyle = MDCInkStyleUnbounded; - [_inkTouchControllers addObject:inkTouchController]; - [self.view addSubview:self.unboundedShape]; - - UILabel *unboundedTitleLabel = [[UILabel alloc] initWithFrame:self.unboundedShape.bounds]; - unboundedTitleLabel.text = @"Unbounded"; - unboundedTitleLabel.textAlignment = NSTextAlignmentCenter; - unboundedTitleLabel.textColor = [UIColor grayColor]; - [self.unboundedShape addSubview:unboundedTitleLabel]; -} - -- (void)viewWillLayoutSubviews { - if (self.view.frame.size.height > self.view.frame.size.width) { - self.boundedShapes.center = - CGPointMake(self.view.center.x, self.view.center.y - self.shapeDimension); - self.unboundedShape.center = CGPointMake(self.view.center.x, - self.view.center.y + self.spacing * 2); - } else { - self.boundedShapes.center = - CGPointMake(self.view.center.x - self.shapeDimension / 2 - self.spacing * 2, - self.view.center.y / 2 + self.spacing * 2); - self.unboundedShape.center = - CGPointMake(self.view.center.x + self.shapeDimension / 2 + self.spacing * 2, - self.view.center.y / 2 + self.spacing * 2); - } -} - -#pragma mark - Private - -- (void)inkTouchController:(MDCInkTouchController *)inkTouchController - didProcessInkView:(MDCInkView *)inkView - atTouchLocation:(CGPoint)location { - NSLog(@"InkTouchController %p did process ink view: %p at touch location: %@", - inkTouchController, - inkView, - NSStringFromCGPoint(location)); -} - -@end - -@interface ExampleShapes () -@property(nonatomic, strong) UILabel *titleLabel; -@end - -@implementation ExampleShapes - -- (id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - _padding = 8; - _ratio = 4; - - for (int i = 0; i < 4; ++i) { - UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; - [self addSubview:view]; - - if (i == 0) { - _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; - [view addSubview:_titleLabel]; - } - } - } - return self; -} - -- (void)setShapeColor:(UIColor *)shapeColor { - if ([_shapeColor isEqual:shapeColor]) { - return; - } - - for (UIView *view in self.subviews) { - view.backgroundColor = shapeColor; - } -} - -- (NSString *)title { - return _titleLabel.text; -} - -- (void)setTitle:(NSString *)title { - _titleLabel.text = title; -} - -- (void)layoutSubviews { - CGFloat totalLength = MIN(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)); - - // There are two shapes in each direction, so three margins total. - CGFloat available = totalLength - 3 * self.padding; - if (available <= 0) { - return; - } - - // Construct the shape with the right size but at (0,0) and transform later. - CGFloat shortLength = available / (1 + self.ratio); - CGFloat longLength = self.ratio * shortLength; - - CGRect bigSquareFrame = CGRectMake(0, 0, longLength, longLength); - - CGRect vertFrame = CGRectMake(CGRectGetMaxX(bigSquareFrame) + self.padding, - 0, - shortLength, - longLength); - - CGRect horzFrame = CGRectMake(0, - CGRectGetMaxY(bigSquareFrame) + self.padding, - longLength, - shortLength); - - CGRect smallSquareFrame = CGRectMake(CGRectGetMaxX(bigSquareFrame) + self.padding, - CGRectGetMaxY(bigSquareFrame) + self.padding, - shortLength, - shortLength); - - // Offset the frames so they have the correct leading padding. - CGRect frames[] = {bigSquareFrame, vertFrame, horzFrame, smallSquareFrame}; - for (int i = 0; i < 4; ++i) { - frames[i] = CGRectOffset(frames[i], self.padding, self.padding); - } - - NSArray *subviews = self.subviews; - NSAssert(subviews.count == 4, @""); - for (int i = 0; i < 4; ++i) { - UIView *view = subviews[i]; - view.frame = frames[i]; - if (i == 0) { - _titleLabel.frame = CGRectMake(0, 0, view.bounds.size.width, view.bounds.size.height); - _titleLabel.textAlignment = NSTextAlignmentCenter; - _titleLabel.textColor = [UIColor grayColor]; - } - } -} - -@end diff --git a/components/Ink/examples/supplemental/InkTypicalUseSupplemental.h b/components/Ink/examples/supplemental/InkTypicalUseSupplemental.h new file mode 100644 index 00000000000..f8acf8d3e00 --- /dev/null +++ b/components/Ink/examples/supplemental/InkTypicalUseSupplemental.h @@ -0,0 +1,25 @@ +/* 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 + +@interface ExampleShapes : UIView + +@end + +@class InkTypicalUseViewController; + +@interface InkTypicalUseViewController : UIViewController + +@property(nonatomic, strong) ExampleShapes *boundedShapes; +@property(nonatomic, strong) UIView *unboundedShape; + +@end + +@interface InkTypicalUseViewController (Supplemental) + +- (void)setupExampleViews; + +@end diff --git a/components/Ink/examples/supplemental/InkTypicalUseSupplemental.m b/components/Ink/examples/supplemental/InkTypicalUseSupplemental.m new file mode 100644 index 00000000000..3820e08b6b3 --- /dev/null +++ b/components/Ink/examples/supplemental/InkTypicalUseSupplemental.m @@ -0,0 +1,138 @@ +/* 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 "InkTypicalUseSupplemental.h" + +#import "MaterialTypography.h" + +// A set of UILabels in an variety of shapes to tap on. + +@interface ExampleShapes () +@end + +@implementation ExampleShapes + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + CGFloat padding = 8; + CGFloat bigViewFrameHeight = 130; + CGRect bigViewFrame = CGRectMake(padding, + padding, + frame.size.width - 2 * padding, + bigViewFrameHeight); + UIView *bigView = [[UIView alloc] initWithFrame:bigViewFrame]; + bigView.backgroundColor = [UIColor whiteColor]; + [self addSubview:bigView]; + + CGFloat buttonViewDim = 50; + CGFloat pseudoButtonViewHeight = 40; + CGFloat fabPadding = 6; + CGRect pseudoButtonViewFrame = + CGRectMake(padding, + padding + bigViewFrameHeight + fabPadding + padding, + frame.size.width - 2 * padding - buttonViewDim - fabPadding * 3, + pseudoButtonViewHeight); + UIView *pseudoButtonView = [[UIView alloc] initWithFrame:pseudoButtonViewFrame]; + pseudoButtonView.backgroundColor = [UIColor whiteColor]; + pseudoButtonView.layer.cornerRadius = 5; + pseudoButtonView.clipsToBounds = YES; + [self addSubview:pseudoButtonView]; + + CGFloat pseudoFABViewFrameLeft = + padding + frame.size.width - 2 * padding - buttonViewDim + padding - fabPadding * 2; + CGRect pseudoFABViewFrame = CGRectMake(pseudoFABViewFrameLeft, + padding + bigViewFrameHeight + padding, + buttonViewDim + fabPadding, + buttonViewDim + fabPadding); + UIView *pseudoFABView = [[UIView alloc] initWithFrame:pseudoFABViewFrame]; + pseudoFABView.backgroundColor = [UIColor whiteColor]; + pseudoFABView.layer.cornerRadius = 28; + pseudoFABView.clipsToBounds = YES; + [self addSubview:pseudoFABView]; + + self.autoresizingMask = + UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | + UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin; + } + return self; +} + +@end + +#pragma mark - InkTypicalUseViewController + +@implementation InkTypicalUseViewController (CatalogByConvention) + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Ink", @"Ink" ]; +} + ++ (NSString *)catalogDescription { + return @"The Ink component provides a radial action in the form of a visual ripple of ink" + " expanding outward from the user's touch."; +} + ++ (BOOL)catalogIsPrimaryDemo { + return YES; +} + +@end + +@implementation InkTypicalUseViewController (Supplemental) + +- (void)setupExampleViews { + self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1]; + + CGRect boundedTitleLabelFrame = CGRectMake(0, + self.boundedShapes.frame.size.height, + self.boundedShapes.frame.size.width, + 24); + UILabel *boundedTitleLabel = [[UILabel alloc] initWithFrame:boundedTitleLabelFrame]; + boundedTitleLabel.text = @"Bounded"; + boundedTitleLabel.textAlignment = NSTextAlignmentCenter; + boundedTitleLabel.font = [MDCTypography captionFont]; + boundedTitleLabel.alpha = [MDCTypography captionFontOpacity]; + [self.boundedShapes addSubview:boundedTitleLabel]; + + self.unboundedShape.autoresizingMask = + UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | + UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin; + self.unboundedShape.backgroundColor = [UIColor whiteColor]; + + CGRect unboundedTitleLabelFrame = CGRectMake(0, + self.unboundedShape.frame.size.height, + self.unboundedShape.frame.size.width, + 36); + UILabel *unboundedTitleLabel = [[UILabel alloc] initWithFrame:unboundedTitleLabelFrame]; + unboundedTitleLabel.text = @"Unbounded"; + unboundedTitleLabel.textAlignment = NSTextAlignmentCenter; + unboundedTitleLabel.font = [MDCTypography captionFont]; + unboundedTitleLabel.alpha = [MDCTypography captionFontOpacity]; + [self.unboundedShape addSubview:unboundedTitleLabel]; +} + +- (void)viewWillLayoutSubviews { + CGFloat offset = 8; + CGFloat shapeDimension = 200; + CGFloat spacing = 16; + if (self.view.frame.size.height > self.view.frame.size.width) { + self.boundedShapes.center = + CGPointMake(self.view.center.x, self.view.center.y - shapeDimension - offset); + self.unboundedShape.center = CGPointMake(self.view.center.x, + self.view.center.y + spacing * 2 + offset); + } else { + self.boundedShapes.center = + CGPointMake(self.view.center.x - shapeDimension / 2 - spacing * 2, + self.view.center.y / 2 + spacing * 2); + self.unboundedShape.center = + CGPointMake(self.view.center.x + shapeDimension / 2 + spacing * 2, + self.view.center.y / 2 + spacing * 2); + } +} + +@end From b2f270ec496d4e97d16f953ebd5e70ffa7b2eed1 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Thu, 21 Apr 2016 16:39:16 -0400 Subject: [PATCH 090/129] [Catalog] Remove old, unused icon assets Reviewers: ajsecord, #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D714 --- .../Assets.xcassets/App Bar.imageset/AppBar.pdf | Bin 33852 -> 0 bytes .../App Bar.imageset/Contents.json | 12 ------------ .../Assets.xcassets/Buttons.imageset/Button.pdf | Bin 32638 -> 0 bytes .../Buttons.imageset/Contents.json | 12 ------------ .../Flexible Header.imageset/Contents.json | 12 ------------ .../Flexible Header.imageset/FlexibleHeader.pdf | Bin 14866 -> 0 bytes .../Header Stack View.imageset/Contents.json | 12 ------------ .../HeaderStackView.pdf | Bin 44565 -> 0 bytes .../Assets.xcassets/Ink.imageset/Contents.json | 12 ------------ .../Assets.xcassets/Ink.imageset/Ink.pdf | Bin 13460 -> 0 bytes .../Assets.xcassets/Misc.imageset/Contents.json | 12 ------------ .../Assets.xcassets/Misc.imageset/Misc.pdf | Bin 45426 -> 0 bytes .../Page Control.imageset/Contents.json | 12 ------------ .../Page Control.imageset/PageControl.pdf | Bin 11943 -> 0 bytes .../Shadows.imageset/Contents.json | 12 ------------ .../Shadows.imageset/Shadows.pdf | Bin 28745 -> 0 bytes .../Slider.imageset/Contents.json | 12 ------------ .../Assets.xcassets/Slider.imageset/Slider.pdf | Bin 13268 -> 0 bytes .../Contents.json | 12 ------------ .../SpritedAnimationView.pdf | Bin 20088 -> 0 bytes .../Switch.imageset/Contents.json | 12 ------------ .../Assets.xcassets/Switch.imageset/Switch.pdf | Bin 26650 -> 0 bytes .../Typography.imageset/Contents.json | 12 ------------ .../Typography.imageset/Typography.pdf | Bin 13653 -> 0 bytes 24 files changed, 144 deletions(-) delete mode 100644 catalog/MDCCatalog/Assets.xcassets/App Bar.imageset/AppBar.pdf delete mode 100644 catalog/MDCCatalog/Assets.xcassets/App Bar.imageset/Contents.json delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Buttons.imageset/Button.pdf delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Buttons.imageset/Contents.json delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Flexible Header.imageset/Contents.json delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Flexible Header.imageset/FlexibleHeader.pdf delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Header Stack View.imageset/Contents.json delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Header Stack View.imageset/HeaderStackView.pdf delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Ink.imageset/Contents.json delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Ink.imageset/Ink.pdf delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Misc.imageset/Contents.json delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Misc.imageset/Misc.pdf delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Page Control.imageset/Contents.json delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Page Control.imageset/PageControl.pdf delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Shadows.imageset/Contents.json delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Shadows.imageset/Shadows.pdf delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Slider.imageset/Contents.json delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Slider.imageset/Slider.pdf delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Sprited Animation View.imageset/Contents.json delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Sprited Animation View.imageset/SpritedAnimationView.pdf delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Switch.imageset/Contents.json delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Switch.imageset/Switch.pdf delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Typography.imageset/Contents.json delete mode 100644 catalog/MDCCatalog/Assets.xcassets/Typography.imageset/Typography.pdf diff --git a/catalog/MDCCatalog/Assets.xcassets/App Bar.imageset/AppBar.pdf b/catalog/MDCCatalog/Assets.xcassets/App Bar.imageset/AppBar.pdf deleted file mode 100644 index cb130a1247a2db317ee86047bfad94936949636a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33852 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 YcvE;w)whrc3v6|5I_4Nh$hqnN16}c~PXGV_ diff --git a/catalog/MDCCatalog/Assets.xcassets/App Bar.imageset/Contents.json b/catalog/MDCCatalog/Assets.xcassets/App Bar.imageset/Contents.json deleted file mode 100644 index a832b900cbb..00000000000 --- a/catalog/MDCCatalog/Assets.xcassets/App Bar.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "AppBar.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/catalog/MDCCatalog/Assets.xcassets/Buttons.imageset/Button.pdf b/catalog/MDCCatalog/Assets.xcassets/Buttons.imageset/Button.pdf deleted file mode 100644 index 4f4248878f8dd215fcd52026e3cf7b4d0f1e90d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32638 zcmeIb1$a|Q)HjY7in~K_3GK~|r<6iGZIU)g+ca${5KkI+!QG0xFS@w1=)&Ss+}(;T zR=ha>xi?82mfd~7|MPwCv)PipckY=pGru`==FH4FH|<0$P6QlDs^0Ey&Z%?N!(j{# zV^L&QkB)|fYNuQ!cgit%kS2jda;@5dA)wbuKC2pHf@emHHYTRJ+N?tIAaudJ0<~G| z)L~#EjzoqMT?!|XK_(PHXwDGlm>7rCu9lmshevn!TIEWE+KEx9wR&^6*10?Pw8rRF z-CC!RggBv+Uh((cUPp$!Y@i#CT0cvm?set+gCfMI8mM zkz}Yf1|@MCy0xYw4=^&J2*b44)fiGFIYNmeQ!o@-But`Eh?FiEJPsy7I08h#Mc@fh zL|haRj|qNS$Ap8cXuC=iC1G)b#eutSt#wYPH41_}9#5o)5NWY%A(%#^K{z~w$43B) z2#3$?lzSu0jy3@%AwDd%LuuDroqCHIgYcCrEG}oa)~%6(@;`xgsg(KUSY37_T8c^u zsf}ur+U#^da3lp2wEvJt3d z?6f#^odQim83wZzN(`ORc?2v5na%&bx0W$7NP<% zKx7suvnY>V<qijL(jT5oC1^I%BKgy>! zfhjfyqGduvI$fM-NQ;wb=?uD-O;4mVV(D}iL=n>+$y}XV#ijY>T$(4AC3FetbfVZ` zjHhF9bo9fZ1A64crnhh7rFW%^k<8#DrZXTckvou;>;((_`VI3|S1ikVW?(|HLdhQN*TG6Icvk5{Ds9 zVtYLbuGgXDd8wNGe;ziE;Zer3=(to+LMm66Ea6Bj=yQ}|K4IZZKY0yNK|{q_It-di z_pl{_Cevdjbg@tZn#QKf(bn_mNbAIaDE}ko&^@tS01J;vH-P4WXWCd+ak>CQF`Wro z3vf}!GQ}1FAZ3CASahwJ3DN)}fRR}UI!h=4_;X?~s}kld*R z39K@!h-#UYqT#_<4IzQ8OO^BVu_nHr=!?&^V3`I|qSRVmy;0jngaLY=0a}!qV#GiOIeceJVx3WDrD&@#;9X)~iZ3c(iDf z<1*7YT)oPm;v^HOwgkV&r?sLCaafUG7nf*o3X_saB8gFznj*C-WE@y6gC)i^lgc7b zvDy_}*rmY#Rr67XSe7v^CB;b6ri1$B@=BFjx4qC7s{|$+P}I*85S+$U-6Tn42#tH-|J+kz@3QOdHlppPt{f;ssY-8m{LELX0Glc^^OBuhIjqA$TDza) zkELW9#c^H>U8FIZJUEmg*O+OHBf2teE*vdQ#EhkiQXogF0-_7;2~-%O(DV?AlZkaH zL{z%L&mp<2kWnKdWRemfQi_UXhdd0dS3{-8Iy5lK&{6LU@F-^RXH?4`t|6K|Tk?FN5580c1!Kk%^EGXh%4| z9v&vjgND>Y?}cPMY?4rBa#QUjEL9UnfcSn;9+i${M;X%NkOq}nkMHuJGmBNhLr0r| zGA!B_DjjJHohjhalp2!Ii!_g3q%K$})s2;;LiS83&Lr^5uu>|d78xjzF^zyS1eK=~ zC>+RFh==c|;H1(450e`zfD>gH=n@oDUz!ywlbN7onj)US0k|;CT49<5$KzU*@eT<^ zDTGaKoI@$$v0#-GpJtCE=>&SRlx9y5!W5_MXU8(ns<-M!AM;hp2LrOYIX8 z-8!rh$5WW8Jgf?<)0z}$4UHrkp6~KXfAHlNLq08#H!@8Ok%*AZ;7?Kk`J;=xkeJ56 z^B`cvklbWhKsmVNX#~no43$irS4v`G5gGL2>`4%v13E7uUP%z8@u)Hnlt^Yd(Hi=( zEQro>qi;t5|miJ1@_tP6oTAM(%=*{rbkY&;AkG$7^l=o(<};0Y@AeW z<~x(IY`rN#AQEK?hb+V*NO~KS9NJ5)5hrL(fYP z+hmZM&Zg+3JdMYfV)C2Rf=rP}m1(2J^LQpAL#2qNB-6N56Io)X(X1v*yiH(dq6~3j zr^)H3CrOA#l{;CW)8I{bi_qpJIq*r`I0;ehBxuxxG$ohCF&ddNo>MB7DS_+?@tNwx zc&B(Rba zy(SICXMtd~O-9xLTt&$OJ><4nDSDXdBCGZB0zm>>PE0cc!nwnusT9Yri^P>%SaR*&XJNVb6V955omTi$B^uW zScxtn;NWKqwOlAR(924b$?Nl))Ujq9)uYz9@lu+SYVhLCMokh;=`b6BLB>x=#Ojqc zHP2>a+67{PM4f`CDRny7i~_51_+hS=twb3L!Dg6+191dSB`yKyi09%`usXHGs*>mp z2D8-1wD39N1P+}ka;khvmxq+eR}#%wjs@nLGg&;QnB-$5iiAcFAvG{kXe@}&g4FJ0 zpyJJ$SX)wjJl+klr~o*zN(Xr%4^x3PlIk(-}sagyHP2{1g_&5lb zK}6WEq!~0`4T~nQBoIVI5tTw?F_Z*>AB@v9n8Yx+Vj+Rq6h~3}6Co21R_M|K_Mya- zmL}3`QoWffH!nWTu4Nf?X+(+J?NfR2%0yvGqRz|1QKT9?-)s^Sq+XKHX?G{vIVzq@ zAg2;jXqjxCHq#(6+EUOO(ioY^!gv})CdP}zezru!W%0~@ENry7_$g+#)k|c?DOg60 zM}}26S+F7*N{DxB@Em)b5+9EjsE7(%d?HQ;EZoGvSk6pSn>7rp5*M4A#Ff~@?z99a zj!HEt6ZL{jz7wkl8&U!jmq;ZOs0IN8$2Q27Ivh#jwPdo$++>o0%#Y);=`J2VGaxpJ zFk3GYP_#D4lm;-gnl&1-19T9Oeg<9zDKm+X+DDN>UcJnNB?_E8u-b{!s8dB)ApL`^jI&8z)4`CO@=s1vpbVUwvtkGiexw6<&ul!5XZ(caYhlxWNR!1j*DYD z#W;GrGSg@hno{U`1ySYmdx)e|2UqOpD5dlilZs$BsEsTiOq0vt_;?phkQkWt+{7do zSFfZ?twcNQGbQjcsflhIo5IqFl36^W3XWdd;;wVWP0nNyXBjb{VDo?Cc zLox_rr3nOw(m@dg#D=T1`a}j;WlQxkxe2lHS~_G&jWhXG2|$c3GLl$Bfyp|^?}ems zCKE*l>98U)&ywJQh!B-P6TpgiAtg91p-ukb1mz{C+Ne4RF*S%}l@+qNfe}^uoaa&) z1Nsm!x7d7eQZj{|AmCHYPDxx6ERq=1B4d(OrkA7`l{yiRD}(7AhnHmaU=0Q}(XQ|( z3$Qem%&jn`kW>;0z|_hNEFLKwy;3Mtrb3Byj@f7wk@ZOwn98-2oeDe&?5NYsIw#)l z(`6b}$?o(=9@)UznK|Z*&S=Pn4!|m}0Q((CS$h zo7SAF_ISKnhL0D|w)%`x6Io$)D}2OEz1|G8u-@tQqP?t@5YmhimD9k8=Wq!M2A9r= zqm%4{IKG%37iSgwzzF2K#cp7X5bQ883gUnlmPvN z*{)QZ`0iK@Fm&k&31-w}^02sIBg~PB6%aL66$4L#0=*1WbG{Yw1A__JC}a*cnTMsR zEgC9S11FNKE*K(eGO;=`%OS#w09&-lYI_>cFh4fGLQl13GZ3J7JIBisa3Q*vj!VGP za)E?JXGvUmMVdZAjv5sx!@n{wWNbQ3qPNR98amjX(7Uxlsmd$!aQQBx zoyg+Ilu{}~Z+G#rbgvX;2v#_MO<9;oL8p@eqtZ4%P==*F_@`+o!~b?gN(K98G5xPo zN*C6j#SfuFG5lA(P^`57rJVxN4BZNjZ31U9#ZPVi&IuuMidX7c7jjN06h_6G9ZtDf zsg7mExLkTw6jepRsT3qi1Rh5qMX2#ad4!xyAxBWiB$84IHWA1~5?a5ac?+r-8F;6u1Z+PECv;(uj%(g@yn}s8mFO6rFHwdBIF;y;qhF>` z?r_9d?G_6-_CrpA1F#i)ULrqBZP&ZiDvsS^LcJ^g`ou6A^*@Nx7xV{$n;yZ90?Wx7 zoH{~7qyXGB6j}t82zrP}l2gEn4Ns=YY0*%L>;-i~&Yc5~Ri*UG-{4*-duiM)|F~lU zwJRlmX~o#T+cyC=#hw?i2z6ee&M9>G8x&lg-T}NVe1#q$@FAzZiE3NX)X)QPp8vN_ zg;58QD7g}muNa3M$O+PYz-c6uR#4@FQ{ezXDdIsSDhMPh2_HdGQ>j2Gh_nc~Qm%+l zl5rXekxbMm@!$ZtME38O;nAthMVtmmQ8?f<0DK4nP6Gi}DAo6wI4l~cM{ZZswLl5{ z?3sOlbJ48dFG8sU4isut3>0czD4}?k()H#49liH+O7Y+I9^z>6zpeKIeOh!#hQ@cv zAsk)EPbw9lJ&GUkNwUk$4lqlo%}QTPlHFArry?{ZnZ=_%k}+_89fAoZDTQ(`LYt`f ztAA2+f%yYCctv?)U@A2dhklSz7f35802xz(R9y^AA{C?56;+=>N|#yhj1gG?gpLqP zG^v1VeuE0tyAZ2Try!nVrFru(KF+)I&oSegld)E zWkRYBJlqPN7F55m4iAxVxv{^3e(xCMw?X2xJ9(P`M9yLIyLH zIuiZ?VBf(c8muCsz}%y9Db@C%VFbM3F-E<@F1PzIFrqc15s%XXs$dfWl_=!6jSlue zDsT%29_diNOL}Q~AOPb3Cn14$8G(4Y zz_Kzw&JKAL%tE(285VCkE)s`9{-J9_V88$iCIey^)oRsupE7{~53E`ThD1e5f>6^9 zL!zNK$jV0k-K-*Hk?ahH0*1mb93Wzl?R9d&+TN)K*W5%Lh74+juug6vP}X zXq;+01U&6QP>ot?QK_L|xFeoMDF|ugjlL(zjxP^a+w}?Cugl1{QMpgI)ZDiDmodN0 zKXbN0tME)3=4*?tdiJ-$V{>9PpBpXh9rMkYaCx03>)eNGcPeLPwbv3`-PTua`gvmZ z*XNUL-`Y)3U%Azy(ZdDnD=ll4)0_Hins%y(HFQg@CNp%6J`d_7w%^S0E{`C(S10(} zwp>q`N;=RdJ%%x(&Gv~+`Zb(+gfmccENe!&$@ZmM=k$I{;~pQmaA}!+{*o8l6%`lh zo^`GxZn~uP?jzf$HM>*ay>U~Mq`b{317?nGd2!63MV>kiLFUo!>-S}(%IDNK%xr+$ z@#gK;M|^RcWAUpMHDsK5J2kg-o9T!Bhj^`KX8ACd{)g;?j=otjbN+R^Q;GzxV1A-bMOq+Q5p9%5XPK*;8^%pi&qb6p0W^?bd@}KQ04`#b}T{9*7_O zeXv2tEZBd?07VM+5~O2E~Ot776@eLg@cu zGYZLaERbaiI$Thx4F%G2Kzv+*pxMIF!KQVD)5xGjG#ZtP3?UqbNCJOgErTQx(B@LX zC547jn!-V0I1I}YE)FjVKRRTJphre@3rs}5i3lPT@D)Oi5(o(nB1feBOXNbMzXWoj zkzPbC{1`b@3;Y~%L=-t15oruSj({POK`Z|fDMX_bL5c?W5P^una3qaD!oUOyNFZS- zK=24eaz17Or4+=Bf+PJc&`LlP)N6$V2T_8cqftc)r0sixLxLlLt73j_P2nV5B!z$s zltA@~NOM3dz%7iYV)AcskuYeSGQ3!VF_b`|05(KS!96*WLc|1_qgaqI!LoyQ%J8E1 z#&DQGjwFDN2ofL(lt_FK%0kGZtn+aW6s$yvQQ$*02~ogspz;BJg~HvShL#law784$;1Mo)V&)PVIFL>DpjF!%xi zK%a&L0GQ*Tuu{025u-0;8WiwGnGy1fa-@S<7ii#&LhO-*AbTbb)E5jpJPFKfK)u2w z#0CKu;QTlPV!}UqTD-lX)&-ga#bXGOFpi9;7L<&NRS4?X#30BFArz0q+8ZGz9RrYq z5=6l#2_hj_i6qid_@G3g++hHuW_4iLx@f~VsDXH-JbMGhF?Kc3$UkNEE@2MtiYK(GFs$}MRtQVS_uX2?|M+U35#u+>Xd zzi5j}VqIX5AhT2{<`lJv@&12C0n>se{r?#SOoCxx=Lf7r5(Nh=fuB^sNXvtW<-;FX zDIt3AI}4>!!ABbyWcB}-GNuJ~1-_$<(WfDC_}9vqhARF-${0-uVfRVq(~SZ9)7lwI}9xq%xZFW&Aq^LWJVVXA?R z2!x3nDP2-tp2A*w_0jSAS>JA%En=D+9RGOspaW+Y*KpR!xG|ym<(iYbmm9Us`||F= zu??YKySy*E-#Z+(>y`Inzfs4U=V+Jy{^dB1s^2+p={SA%tSz?FS^8?}r}2F&G%7Qo z%9fOpDh!1T0XH4spi+C)ssf7x?5JUYnT*Whp$P$P_s{f2kPE5|1N{Lu&P>S86{C0; z5(8ZbtX<^7WimTZ*#32=;qjFGP7A(->!&P1t@}dIe4BaDpo(b~ z8daF%44b(y)?md|u5_&|rq%Ma%KF}ahUZ=X688T7z5a~KWd~I%SF5j%pD^iRY}0`s z_g1;v_QWXulq20UTh4QcFGToUv%<)On%B!4F!yNF9r)>G@M}Nj?U=N+n(J;We{8q4 zEkFLY{b;>K$7cHo`?e0;t)B0DFiRW!Qrvu0bydq3@`VF)x4JSV!~HQ+Z}jLM|KQKJ z+jcBJoY1SuOsVPhuHkhmwD>Y=K%>WcZ z8O1l!kd-7#{DUbxe<$sq?FXT43nEc$I{wkdWvTOVpt>Pgi))O+O8-Aa^Z%HlDP*+$ zv*G{umSX7Mc2JQ1iSdCLPCp}u!EJ9Kj(@}WApMl_LBr?IW5}Wn>Wz)>))2Qo89IQLBn11=6tK9zEhLlr_H`$Ig=B`9qy|;d!O~F z{PDEyW~E-En=sgF-ut<&M>Rap_?UH^Y1W5zIVEoX%kjCrb#)?%gZB0OLx5S8Q?B8* zKPpCA2UqQRyIjN1Gk%#BUTsfq;`;p@X~hb?XECquZhyyPVqB)-@;E0H!z6dcc*aFE ze`C-z-?=F+yI%P{J9mx5^G<7bH@XX|(EfhAm=DUzRXJ}G29+(dYvw0sDH92z(U7hw z&2+)z0Zaj-H)Oj6VEdWA{Fgcfp#CG{<)=EG0RIi+rD!)_+F%|cs(0vR--*2wN5#{Qtsyuuj=<*#Fpc>)Y~wB!^Wu1 zXSB;6Mk!Z8$%N^HFmH)B-Vc1=>+oq3yAH2B>zK-!HKxbc9@yFEx|gd~W?1X&oRTsY zkfET=gwDrk1vWjfi)uG$STh{4h{D6ds(=q-7gebehFV8e!h;4#yvjKn{chx)&KVmhzVl0XCrm ze(GEjY~vwDOUPXMn+KEs&Rjxm!=F1o`=2>@3^X=~&%d{y3T;i5SaJOi+E4%Ky8ku0 z4s|C9$scm4pN_}j@k9&(PXW#&AOMp94t+q#g-NXn?vt}YkfIz!p#;`&B0F$ZPiQB=4|0bZ@rr@?bYTKN1zRN60b(OZ z5a4tO?6H{X;HAI$_Z;x%I42VX;R7ES)T<`stZV`0f&)2Jm&k)8R8s!Qa1z)pMD-;4 zg%*r%-#F6A;5-;HGfMtPfq|zDx@{PEM#fP9M)*QoY=IJkEuuS=$aWP_f@JXSaBFb) zi2}pG)(AX`z*AtvuSN^rGmb}Vk2-HePzZPo@(dht0?!JB0ocz%0uay`j*bCnFoAH4 zPJt&NGyySx(B&bTg6>_FiY3AcgjJ}4|9t2)1W8H>?2eIeI4Bmp=p4MV#;gVXjDr%L zYLgTL<8gsy8$w$EGI%jvFkj)*mq0~=IFzyzm%rVBBL_7-+EyAknF@M~h4V(wmFk`K zk{j&KesZ#0t16!aCyB|UQtyQ*0?`r|02x1j#;BBZyYtsnRh*YWV1#OE}f^{>hH?!n|txg%~g5H z(}exHHDvtKtdV5ovV*Sj-(2*Gvoaav~-OQe`xUC^>|7&HmV)M(?cH0N+*;H}R@uhWpVPAi`_-nr}OY1tC9!jr! zpjlaI?%n3NvNNT#ckqWl`y8=gSoGww6>6?atNd=+kQlz%wcyskN2kBms(ITrhu&gd z!}XJUl>dC>(yt$9R5}_#``?G(Oq+22+LMGQ zjVIO9kFPe)*y6({9$)qFNZCoVCT%}6a^1NbzwhZZ;nI_L{wFg|9Zs&h)-deJucGo{ zw=47u>pr;Q{`Iuq4v)WF{~TPmYLjhi4~^URHfD?I_Kx1$=GR?&`iN`1HRCwe86(LCkhdCKFpqXl;wtbemBTA zo?A5BKw0Byb7{*}ziFs-%|&v-Mc4W?GrgtxD<%$x2z(d+()gO2)JKTSU}CC69WyzSJEQ`>6ppltv4pjU3M zoUf<5^~Se&*W%SUYuM2*hihK1`MiAe@&-xa7pqFMpLQKG$G)JN^~1UB9BAv#=zC|E zWe<&b~8!}r@I8L}uIE5aeb?NqxJNyf}-?+QD&kSFL>5p-5EpKvWsD|+Ot(x^q zPMePYevKN)uhez4d^gV2=uY~&Y~2JHp2aZS?iVEvwdqISbeH=Atf5_*bSfx8uSdNLt)aEL&evd)!dp z3$k$0QR47rXNj|G)W|{Nr z`eMb$;m^;kb1pb}I`!k2lb_;S)*Lx*?x8k5~%*S%t0KD_kBaNVV{)XF1o<311VT{pQ~ z=Wt^E_*X>g!E+y~&)-Y9v)~0pJ#pWjc54_Ry6MSR>Cs^)U-o=2xjFB3^aSnRiUa0g zy}KV>E=RjD_O@YLM{U0<{h*Z}%J$>z&pa z-;T!*s5$8t_wJq-abE{@XK{wN|BAKTP1YSMQ!Qz+mtfg@@p7eWcXPd1OWiH2>-_#~ z`9@YduA)QM-jj{SN=a$z+vghn%2_^xt&B-5pR{qgJ%z+kD=1IbR$5uFU!?^HTU@T& zS8lnvx9bHAX=e9_87oEgSG3CP_-=3aF)ga^-~GPB(_ee-eTeIIW$HYGmy)~p;meBC z)`h=d3u>J>Cf=Vs`&hDX<`M3PQ61mgVp<(@hfi6*CoTng>FHN7tPg(Rsgw)U|9?>T4n8CmUUmXMrDHkWy^^J%xOu)5Znj<7UI_7(g#f{FP?Ndy2ppz6EW-f#xb?0X0BrOG_GUh z_Ees1*?Fi}Fyg`=G9kZ<0Eau5DOx=k-f`i!EaK?xM@#Pf)^X>9)6I_3YDC0hj~s)O zoc*!Qx7X-9SJPwm8j*2_kaG7~?QvVTyZg3WwXmN)sehA^8FlKB+IC!-fO&mGy1(9> zMUAh=Tp%CUOzpsG^s>sk${CmHjGLV_M?9t0!fVf@`>)FuZzhCa`?#w5DAI9Q+UE3v zRwpzExQ+UFsGoaHHBar@tac6gi_`0T*Vi%L=l1x#xNPd+1D(4htjX;(;cT0JgFAMM zn-Tu3inL8fa=R1LJGOf)QCL@>>2xNOIGUtbqHmIZr7V`noxLWiMLktYMFxAvnkG>% zr%Cz_tKIJCmN6@Fx~vbf5eJ&Qte@Oumts}c;9;XON8Q0zaj$yXXM9Hu_e%QoZxb*d zT2>ie<>WH?ojWJ`HWlu#M!mD?fxl^zg&BWje21&m#*AIQ#Qo*z+ZvvFUEa2@e675&^jITe&+KRbz=F(TTS8uO6DXR9|c+|0~UH{Z*@~)Ai%8#JM*B@IgzLm7PqIt&}tTs~)l?^)`6;rp~uO0U;9Q3g@vEHgf zt!Fo>owJb)$EF)nlvjr@o4xr^q(MJ<%X#UEI&F^i{qnr->g+8amFxQFZGZTDQ{L9R z4=1;uJT!_c-8ZOhigS^(25HUu^(PnBS#_q_%*ofEY;T+XHf!8l-L0D!-YoJxe|xj9 z$Ggarp89cI-e=Eq;VSax1Vhz9Y9i{FeA-hgEZ?+WbYwV_ow2 zvW;#nuUH8lKa$pQ#F{9(^|Up5*2G&lT_d-9hhJ7{PwUfJxQo-4Y@eJo8lRugM1I?ieHb^$x4T*{1FA$2dLaR$ut6QKfS>>*2`^=F@gV=Z#zV zZ76TAWWH z$&wQnqn@3;8h<{w`-(liQwH|5?0VGA^?p71^YtZF(>~tu>}r`I+Df|dWao6(xoayI z4woI=^tJkdV~lSj>9e!4pH@D5YpHwUvQ4!oRJ?NF$f7D7XWV9@cjV2y8dEMU{V<@W z{_E5je!oli>-CSCbhYE!iQM;Lr_+0kG<2x`a7rIy%v!=fXyJy?J)>IKtPa^}f}t6!B16Lvg6~m9e$!_g9EpT4B?D>Z|04SB#BPF zV2XB`yt9SS9bT(g+2ugmp8lh^g(=o#9d!O)am7@6^ooYrpDxR<|ItyAyteCxi8)WQ z%4g+tew9>r*{oapD>ynosPlT##rD0&E@{Oysh-8OGWTA5{B!jI9m8-bzvZwyDSIVf z{^R6?^E($aR*EAx)n2S^o%W2??$){uH}@M29qn)W7?agAms38>zR+M-mz|SVyj#pN zE0^A!GIOb7_RIRwobuC~^v1utaJ=g%%6-nRmD(45K9)PkX?y4qdsjpbo%T)nYQ>cc zrv1yWWShFZiJrS~!FIR#>E0%9YwuX0cz2=?E4JO3^WyWO3DR$SWv=hUMO z&3Hw8IJFgDv!1&nr+!!Wm9grNt(r}(Sb0kPfp6&-cT{_#s`VrzcC>cF$v>j*PG9zQ zl;GUZaaSx&Pe* z`a30N#LRP}v%8(Vl4W02>B7ZsNpo(Txv=YSzb;8Dt90DFaXt4r{$`zfyo#N(jK;|g z-r#SHnut@aud#IFsJGs#XO_`3*mIOmx5(Z$c6`oqmt8jJen-+e<+#T~c&`u7rA(MS zX7mHj^PLADwd41jq0`QKSW8ZRJ?F&kf!7AChMx`p0&?)3`;ah+T4DoXD&A^hVE@ z@>WmMPxpF~JhgY3HMXhM`u4wY^i=s(sf)vvkik8KG+q(^6_a9d`;&Ws(ZhbhpQuonY|Ngz{O?I|? z`gK~AqSxVW@59 zl3L!K+oR={TF;cn&&Kqa{7&08^KB!;uti5IAGYov%sRT?`k|Bk?WT@|v<>VF^0Crk zXD*h_x-erAZ^48v_MXw(F$a&_@74X}P1*6AAL_4c-u38WH4oleJI8cyLDzdLRd1&E zKi+DAs|($7A?x712`|TgeR`@)m!yT4)ypRGBR}=+aQOP+FT0x)E<~O`+PiV~lgkm~ zyf@+S5B^=bl|R_VFL;u{+w!5;klKg4kN7>>pwVx|#SM4dy0Cavzd>H?yM-Ma-aS#b zkv8WJ?++q3yl=ZIOmzp^#Xk+F$G4jCW&I*%+vUuDIV;9KgL`?R?=39zZcX~dN!erW zkk^U^B0YjNik8y2VXY3dX5Fh$^46qBCdNGYJbXa6UiKAJV8Q%{yT+}4*tx>Y z2JkVi_Ib;Lb2+uH*~ph-?kW_*qf1nxO26$%c-UmxyhsplUvzd`Zr@GcWZLD1{i@?#%7GUYLyqmMpg-i?GH(OSwR7i^>YJ0< zpByLC_dV3#8^7>TpVWE6scX0)b8kPO{VBatnYfrrzBD0q<@TXF zvb6MShdrmc5f7cec45rjadhU!S>N9`@y5IlUwrZ08>B9ln_*vbWefqGUFyrAf)+^4!tK3fQVp5i`o1DF0+^*^RBNL7$ zMYn1B5pI$9ZT)nHT`+K$&feTtGke>+Qnlm3Q zHXUeBj%n0iT(^2o-n_1xuB`o?p=g;quV3EWdB|r$ZoiQmc;YH+67^pf-Tx~3Qoh?1 z@72%r3WtwxJzwi{8ydSCyr-UTW9u=NMUP;-XnHwsPnSJabF(uSo?2hi|8@>-db=*9X09Y@)&9RW zS#@IWAt zqw4f!xt3hmcz%kFpTTYZ>yGhFp8qmSI&sgDkypxm6%EMx}uS)>*2QO@RMVi zpSyOMn)^XGJ-7LrU7w9nQJ}@rV~|tNJIrUB5EnqN&^^>|y%rko+mE?yt=a6Ya6>yGx?r^v45+5P{hLf*6DLAML5 zj}M(m7(9b$8KO})-8v)Z;xp3;%B+#@A(x)x79{3b1;@8G>%M)%4%_mZ-;Vtm|2lo| z%r^}te_Oa`#jNI*Kj$)L*khKTTCu{_P4W5RNzsws=SLioSI+BsnR0k9OnpK0 zT=yyK<#Hj-i*LGa-_6F&NV(eIbL~f#8s|c$liR0`d-W*!UAL#TdypExTXm@2gOtmk zX7|2AvKj7oIaM=n*R`gRhx-Wl_vZLo&&2I{&`sH(PSX<;=sQ0UuS4&e?>Ic^)b=gz zzNG76_Xf9mIje3Dt*MfK@Y_G0?CpA_+sPi>ZQjRmMGe#W=aIYS%1towz>8wevD_|b2cM{fvLo;m(D-Ya7o_BdH2Yxot}Jp^p^kR z;lcqoKbHO6ea(WYY3mMs{BZWM%V~Q2{Ml9M)^?Faf9DQET)@pJZyNn9&s_{Z9f_gz@Lk?W2tze~8h@!%VEVo1wUnCqF34Bfwm zo!MA5V&~F^?m6f042Y{V$o5N%w?;yjy7-j|&xcT2fARXaCuHQh&vYd%&!N06pVKI@ zds_8{)eq;Ddn|d?(>S2X`t~0QJI}mpvS-$!9gTN&e^&pKtJjVL`@b1u`8m&~QR3+H zj{8qkf701Jr0bKs&Ez#1Y-nB1;1MMCn<48X&&bjg=Fmowo7YfMuPLu+u zIsp_-ApFC!L%B;94r0QSoT2#e3l0nc|0Y?x{iM|LY$g;$Xa5_POqpQGABX%k{ ztn%8`n(E**28OE#UPlJLiGedRjHY^cC=CYZ*T@HBE=WV%nTw=>h+O2Trbt@gF7O@V z2j~!4{Y$#i^8S~GB7itSBA>#2^j{i^$bY!}Z;%a`h@M!ZJsQ$EFcHMP7tI@g?)pK%i|gIuNW}f{s8ybbJwh;8n=Tj8`m;fP_*OOCuvObVbsD zmPC96il)It5W-z74G$wupT+2KCE5XlALl9F4wwdBJY6g=3@|9p54>xHRJ<-QnN*S= zg;)Z6m`pAuFAaIiMv*#$<9>XJ`hlNwDULG;o+^ncg$$R#nLwKQm5>7(1z(a5eN#b^ym)X|d|m}< zNRV$aIs)PqS}YB`zPLmhp``r5;J1y6)8TO?>Ws(ZOW=pc6H1{YAThv2>w+hjlwCY{ zk8*K30=bkl@HXFKbOao(u)P6(%NzKH*pVN&N8i2*>=#I4u>ijmV2eqBGqT2lMA8Rt zSn1I;0*=9Au^2ce3$aqb4_dG&Og5WDq?71WA`Om-W)dg_Is+I#6dDt}?U_O&l1OYo iOQH}+FoR8tK^$VhJKpV1)MNtalgZWFwPPo6s{embIC|Fr diff --git a/catalog/MDCCatalog/Assets.xcassets/Buttons.imageset/Contents.json b/catalog/MDCCatalog/Assets.xcassets/Buttons.imageset/Contents.json deleted file mode 100644 index 7ecf57fb329..00000000000 --- a/catalog/MDCCatalog/Assets.xcassets/Buttons.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Button.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/catalog/MDCCatalog/Assets.xcassets/Flexible Header.imageset/Contents.json b/catalog/MDCCatalog/Assets.xcassets/Flexible Header.imageset/Contents.json deleted file mode 100644 index 19cedb5e49b..00000000000 --- a/catalog/MDCCatalog/Assets.xcassets/Flexible Header.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "FlexibleHeader.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/catalog/MDCCatalog/Assets.xcassets/Flexible Header.imageset/FlexibleHeader.pdf b/catalog/MDCCatalog/Assets.xcassets/Flexible Header.imageset/FlexibleHeader.pdf deleted file mode 100644 index 58eb31ed37920cdc6b21f67a634b47bff22b6aea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14866 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 CFWf!= diff --git a/catalog/MDCCatalog/Assets.xcassets/Page Control.imageset/Contents.json b/catalog/MDCCatalog/Assets.xcassets/Page Control.imageset/Contents.json deleted file mode 100644 index cd6826b7b0c..00000000000 --- a/catalog/MDCCatalog/Assets.xcassets/Page Control.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "PageControl.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/catalog/MDCCatalog/Assets.xcassets/Page Control.imageset/PageControl.pdf b/catalog/MDCCatalog/Assets.xcassets/Page Control.imageset/PageControl.pdf deleted file mode 100644 index 8dcdcf9a69bbca40878e2bf0fdae50d8c03701c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11943 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 diff --git a/catalog/MDCCatalog/Assets.xcassets/Shadows.imageset/Contents.json b/catalog/MDCCatalog/Assets.xcassets/Shadows.imageset/Contents.json deleted file mode 100644 index 02aadd138ed..00000000000 --- a/catalog/MDCCatalog/Assets.xcassets/Shadows.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Shadows.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/catalog/MDCCatalog/Assets.xcassets/Shadows.imageset/Shadows.pdf b/catalog/MDCCatalog/Assets.xcassets/Shadows.imageset/Shadows.pdf deleted file mode 100644 index 3da78dfc9773596846fb688bc3f7948ef2b82490..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28745 zcmeIb2Ut_d7dNb~1wjNX=qhT&21dB)1u>ci2@n!OsLE=3LV8cIV6SUibrpM8?5kMU ziWRV;u8IX$*WP>AckWH-*mnQV_k8dBcpr>6bIMZk&*!-f-myIi|| z;B$>XEkq}RA70bj{F$xFLgpfe2lv_}E)UTIaKusAH43MJYkR+u4`7Y8AnWymy|#cGy|b-vz_y`4s}R3o>dByy!%-z)gh zpL>E)YFV$~6e179Gcx5WH7Cm~Pt1xJNwYLksw}uS%{$T=;nW#*pqwbDPOG;>IC}+) zK~dxp;2utff>8*GRnse&4nIJp@&qWR!7N7+!%1OM42g^)Q^T=DGMPZ`fx=<1LlLiBT8$A9h2&bfPOi6FAZ$1ms*Mjwr<_r52}fiTE;Z;Nr`QOXFrQi9Io$_<+ zJl>a?b-G$PEmontLZG#l*MjJeP;N2U%u+cZ?^Ij0EmpQ2NYc|X-vGq612NVLrPgX~ zKuN_`gSmFVN(5h$s7UY?fm$JB?eer(S!!MFd6%`KTPlD|uD1Yf0*Y3P4gp<={xVGM z`5P-Kc@rSl-i4>`N z?vX%F5oHF7CAP|Gz$%c!FnC}RL|76q2@ync7={pm!9+qeXe-qWd1jEQ6|QeWsi3Te z!2}l4DzAY-jKGp3@VLq)sYF>DhQTZ{7_>CE9pqN%HHyhZEFUU_Czh)9R)a+q>aH3B z=wYjdC{o#U61`ZhwN#TcEc~iscDHI-%fZi$VzWgK_vBu|WnH==Hth2xrML5(l ztBQsrl8|y$N~$PTO(vCGtyEcQI4rJ$%aiscHjnzo2-wjTe4fNF@u_uSfTTgxOo%|I zCnRdpV}wdNgRW%L6X^^toz8;D33N+Rw8}1vre=wwsSYlSXXDZ7ganN?o{q-Qk&8hG z^zg-|ckSe)_oOGl%-~9(Gaxj9&eB9_B)AwYJz9h$(-n|}&IJ#Z32X*EiOo4AY^EW+;_$EV?0<=`h41fGh@`$D%vne+eu)LBOU{_$&rb6vapov7HV{w9_Kx zI4O$qe-1W>;gH6&=$JH+Lt3;dNf;$GAkPuNa>B|?-*^vEKt&UjbS$VY-N6>Rt4!w# z=?Od`s2ZCtMrzNY!?jBQMCDgP6y3p%24dkb=^9Wy@Jz{NRi|?UCeWFnwm>dYE;GRp z2S}M90~TGGzyxVPA^=Fw1C7NK0{KUAgcU>?EEsTE%dtI1|(Y1_GF5 z<*0bhbTx%3bSdp@Z9rY0#KKPG&U0`ganqYip^p$*@?U)c2truUd2_WJC*5d7XoOI za2!!Qj*yTT&x}frQA_RYtQeM%rBsO%lUyn4G;$o1ffppk%VXF|rz}b1P$E^1$xM%m zR?9T9s3bha#LsfLltu&)gBE0|ViGl0o+ybZ5Nc&IqFLIQloTydovt(5r22G{LSy1qf*l(*Dy|1h8rz{|PS@0jyHysMKU^()ygKAtZ~XTlTnu@=f_&*HUTFpRc_-Y%SZ%gGTxk7^BV$KLEv;ITSP4IDYYp1 zu_o}(T2t<|$b>*J8C~%SX(kNb`LRjnWV=qN#pxZKxHuIn zlcY~#u~=$8nT>fqVo6LA7Q#z7XbaK8&k~UFgea^+3`QfVf-Q)pkYb3!DxIUeBfz}|DPgR| z2*qj90v%fq(YXc%RZKM7umZA~V8fB4A-kP|v8Tl)lQQ{oxlw4M@{&K_`w_tMzMPV& zHnDA4l(;mLgGF&hn+;i+klHE0Rw*jgNlBo%vJ84Hr<}7q2L!OZFS$f6x>!sFYD7ta zQfy+fRfHy?|XOO#hbq6lC$6J*Jxs1y_#9-_w({zqTMoxlkb~RaIk)Y8) z));##nPa5#r4(wSPOV6fMgVIQ?!T2Q0$7`HzgMniY9nu-9TStc|VB3DSZb}Xd7AwtcxK@^IvHciF4 z0i7g;NG}j184Y@zP+fW4F4y^L13E^UoFKzfwZLjH&}j+^-Ra^$bUsgw09Msl3LVdJ zva$qxwLIxd)`bgkQz4EFB34mW1h8CV%Ov|BgzBy`EZG|BY7?kAI=PHZF;uH=<*zA1 zbb=ktCqs0eTc`+NbsK<1|9hb#fF8+$<_D5>Fl(3rI_KMkiU87qX+g_H3Ug|_3}=L7 ziYOdD4G(tKbXpRWMim$lJAkQf2WoUC*aRSez^qlZ10pC>BcRYJ>1-%f=OFVSyFy8_ zTNP*mnk!EyVnnXY&kZeQfax3}6CwZ`KxermwGROtgFufctL{t6iU3wN(y0)@@)4%Iqk-v}iikC_)Mk!=7p=yjjkqkZhHQ#xT4(`{mWWo6ISN8F2Jh4Wbz^dQ#GAabrOgn`omJnPD z3R^%*16J9Tu82_(qa6^&u17oZXofYOk}g0a{BBkUy)1V!0tnG@Iy4Uq^7N`@pvDm) zFf~+4M2ct;qy**XAjP+rmmYLEdVH1QyJhIE!&mv~(R|2JmA`uRqY%K#GN@X8HC4ft z>o}m&m1|fg9JQ3hAPO;%NMa{p>FKOEXRKR>(GIR5kwsuRTzY{y9d0mq3LcMBWI9{~ z175;2C&#jQ!Yqy0fQ$ahG~Qihk7iqBVlZM534D0wU{9x0Ey+ociDOlp?It>fMFnF3 z+Q4!M@PMAA6wo1Hf2%)106+g2dBTcHcHt5@lBgsRON?tWv^? z2SBl{v0z1&mZk!QNQ{oPQMvMO`a}RL$U$8&Dz%9q1j{Eo-B_!Es%6Smi4Fn$GRgNn zMF1-shcAtD-$I7~R-TSkD*g8r&667f_+Lv^xj+cue=Swz0wI9^s&180DghvX-y-bF z)%sG65WsIG{uVj}5R@LG3!wCh%~AKxXZ5|&@7afk_sVPCQHFQpdAta&-eMK&rE)Hd zX0xef5d^7>Op)W{VFU@86ecDTaA8ymRU9V4ViYng1-Koha-@7!c`J(O!DShwHrRs( za6v}lG8h^Q<``nIu}&coNC00>36p??FbM@OBV#ZGj2P?Qg$B@-*lR%Z&1&Gt6l*Kd zAVnv#a2N^|N1);vQ7i@)OT`lC6e1PF!efaT3X@V+>{|G1}zq0t=jV%{4BXyZI{cU%my9ef&BWud?ezdi%?gz2P`)=kei$=#!{qMVwhY; zk%kd)I6{~NgTaR3=k8#y*uv5vYM^(J?NF#YeP5u>xOZct``2< zvN3xGWHD0C0spMkuC@x2B0Uk+Lr3bM1VBboj+*CES>;dqaeV6YAaiT_u zrLca{EMlN1aPtAjYFS!Ekt=*JZh~6WgFukri4-C(j4Y>6!U#kHHB2lOOTwfijDk!c z5foA!aMRRa|9l<}m0VxNHAjrV;3M$BQ|ETgxuFoM&x2SD3adkGmeZBM2z;w&%#B=? z_48RsRlrRtm(ifI+La|#XQ|y@{{N))zR4*5x7LH*nb`lwTF>34Rl8(a|E}4EBP+!c zL?l|pW~_F`F6S42qVy<#9E7c zX#5<-mvBU8vEBkk3AtYCqKVA5+K{Ty)C7x1;)rNiL=12lJP9=^d15C_o2br`f1~9B z;|BkKYKq&edK~98rc12`_SmNSjqAs%TC(2rP>V95`fw<_`PtFd-?`nf`^qJ|mMzFqw-0CO~GB%FUkf2?Xq+ zv}%c2Y<8iru<}O2;WZUhp3=ArQ7Og@(M5MEh+qT3b5MEoTqQLvulE1`rn;E^_c;K> zADMiYSbvD?hq%5=0^ddaL%V*6>$@cIUBo}M>whM$O7Zt#+oD&n14P%uQT?9qd~gTH z^~16Mp7?&I!KSyOuu!a8X6Z|W&je(+L;Kyc`|=|J@FNck-E3tToP9Cj7!>>unWBRU z44A=WKn$&1F8l0Lra^B7vs!TYfCA?PAyYRLo{D_o$z^KsXITaCTE*%)wNMsoY5|8E z*iLISSYlb_;66GLgCc-Z!O)3V6ahX10pH?KApRfR(r6HvgBj&!t6C28@|b`gIS2us zr1B-1a;X(0N9jN^0pX3(0T@IqG;l#8vs(ArA|es(u-GaNQXuv8h%HL3waU#9hztZl zz;dYpc<{;&SKvt%2Pin>tdn*%&Brtu`6}|@u0G9s5_)H-hb?R8&pFy;S@VU<6H9l$ z9C|=F@1K3U-yL6G68nJ?jG9JI`FNk`!H~RO7=1y@Ff*$Saxu@~vbN$#& zl5)=M&Gc$~A1`FJY#V!^(D^_-Dwq3d%*_t7*SsPgIrJ8VKfILK^Yv-edPBV?b+D2i z>{_%2Zl^M}NrYH(yBb99M>8NUN&zoMp;*uVi9k-3mY3x^gvitb03yGfeF+3ufADzt zlRyHWcq*(!ZnYqgkQVUBh)9BXVyjv0>$S~npAS_%s z6?8fHo`4CbmWhn$)_sr2NW}jpG9uD_fMlwRj995NeWS>To?ZsfzAiGVTUI1k%s?}M z2tg$=i2R7~Ed{;>R^uzOL!{x6-IrC=ZSfGbtLVE(k;_D{wps*oGt;00kt(nqqCgcI zhH?uEbO^XpBX$wzwuZHIc@7fG4p$1|BE}G?EwBh+_Z|TRCqICaOUo?-js)Vmz@isQ zg%WvUiv~q>+odugB6{jBzQ=w+DR78A3X8>5=p$#`{on6pU5jYK?i;f75@Tgh!Fluh zj|SHkOQ%KFh>q`PHy@n0$p3-7;9^Yam5(U_{hMd|Wi4o%J!}p2&bdsxc=a#$X6~R# z`mI;y|F$>!)!`1Q3rw4OD&BON|M~*uVC?*Mj<%`~d+)8DJaxo{!620dDA8Gv2Z?We$Tn+XA|zk>O{ zcI@9qe0$W$zP@UnhWXlTO}@nIaqWon(AoIL0Sd#Bzr9!;< z_J&io*sl4_)?KM1TM~V3VwV)Hi@fyg?f0vGU!%JF)4D^nUAol80kLYRMat@PgHrnq zEZjzEdoc6BiYD{vt1Z1q0a>?(Uz~RC)D_cR_TMP}UoYk=U0Vk!3-;gWelokm+2u)7 z^}_=GzWRJNbN`cltGBG+B;46HtwURyNG4q0eE+3p-6kEm*o?P-RM(_NR|Z-0N~Y2? z%t8(KVO!jczc&BYH?eDWgfBT|IXOBvsH;~zH% z$b(lBmOpKn^P=wr>BG$>%6s~`d&BSbw4Vi#@su3v4Z-wf-@ zaFCBjFB^K7IeSpsE}NW}(6r?j0(3W@tzC9u)RvQhC5*l*AKjv`yh>j_JR|&kN)Z)1 zZGpra`yPp8;t5wU;8 zEN7vaIT@q#Z0nYup%mwlk2lyHvyk4i|Es)h>B@I8y|P@rB?ArYWSH^Kl6j`H~eJ_buW9_ zljL;V4P^^klO?I=5*4&kqb)E{yrSNRR|&cxpm)UIXP+Y7YI0`#FGm=U`Wa{UHC-wI znu=K;`l8L{JiIC}Bd$FvGUdo!Vu#{3DNnZ;Q+d+De@#XjFSXyI;H<8%S~7xWBu}2Ut$-P#9Y&ln|JU3H>N_XG z-UZ4uo!T(l<7X~v#n?S=b;mWKO}B*wiLUDT2W~nZbjZhuhkLa&z9H>9w9K~c$hn0{ zH|Az?l5*p_o(*jwnzy>UZ_1=n@q^i0;`h%m`BaW2xl>h3Ay+H;)a)863&IHhZ7+dPm94$>}c}vcX%EHsCiB zk&zl2@N>dyzp)ED7l-fkUrg`{9=|x~XD{#0b0$cJFE;NOJB2f5$JqSkJ{>oDb)Vk2 zSlzv#bMKN{Yi=C9pPQRwNwz%P^6B1-H62d8+nxRX3IFbePpdzry?y3*_vo7AR{uSv zj%O=UTjUSGoXGC|8cKL-3eVhk=GiS%YLBK(KRusf>OpGSHQ;f8-#&kI(+T?zH*eN# ztZ^LQ=Vu-Vb>O-QGuG+1Eo8K-Y2wI{EMc=8#^8Mob1#h7K=3ed%)#bk zJL6`|KgtnrJkJTrW3Na(+9C=!FQ#iiW2d&?^nqjlzBaD2_q=`A>ZmV0Z{q(?uQ30k zoLkH51_h3Y?-+31)Z3gbm9-h+YuwjkH!3=$aMZwIBpIjk(SXOB`h@RQoIK)3WeuD( z3LDY_CIx@+G;Vm(!OPUt@5GQTVgw6*QP-owc8~XZ^m_;wP9xF%8}X|G`S` zzJBGJ;j1+Bg+x(OT1RLG_dqahR^f1JWMJCEE18sf;||5$X#BKGmm7i%=90zR*}0-s zCmWu~BcR)~y}R&fGw$x8GYgka_1gAl(Y`iaoAQ@p>IfchT-jt*%E~jdx7gRlpBqP; z7k}}}@`TkbUI@}o#n$!0gs=t$-JCS~Qk!8crRB{V_S2H($rHD%USSwu%|8MSYof9WCLjrbLNG}-sGOW zh~hTb=XcNW1mh@kiWYahLXBbVKQ!%jkRT*-!hG5mSn9>9C;lu}DI=j1H-!}dGrLQP{IXP~2*y^?yC(X;ab%j>)a_*l!Cq0;$ zG{I$F+JE&bbjxR_clWzQ4tvW93GGK8wv&EYjycw2ZArW1Blb&Y7EL70y!ZO?-a8xC zhaa2OB;#&F*RY{;7~7x4Ep9WlFz&=u5g{{cbhp4YZ6|%`^r|5D@VyTs!`Gj)WL=|` zIJBv)x{e-5nAq~Sxz?^TUiXp8y?oFub%U2APS9LDzGU+JP0uF8b|?vlj-QC^FnR7p z&4k1ygLUHz8RCZbqJ8;G|DY$Bk}U6rno2f4q!^m_Qy;T`f4 z%E>GJl6us;KnVW0=y&v}^t=($O+|Bhj_IbPWnb;uy2#D58b>GvwFskj;Ak;AN=a|^Y`8Tn~d5YU>&u8(SWOW-#>Sr+l^DUDGpt| zY;T+fy9z!f_%hATI0jclmYaW#@DrRgL-E746i=2?< z2lHkHo|dG?O;BUQDt-B+3iHyaw(l7bHZ}%-alkPjjX0HgcwcGhXfe1aJzGy8K`F^g z?w{#YGQVh`t6h^$MT_o}nl=5Ue^$o8vCAKc{yJiI-CvLy@!|DOThX1T{azawQ;&p| z1W!FV@l*bFvp((C;TJpltBW(2>5mY$N=kdtdhPM9&-~k1KjUv>Nb7A8y?b5Ry#8p; zXlr5S9ci1XhZ+wHFZ~0xSKVqN%?5mFq^@fyQDERyM&8PRzFWl1L zx0}PYZJq@1ME%ZQq79h4Wlhhfm*$_?js9e-qdWHzU837*vnU;O%7am0)**|K77+U4{1`}ZSWy*#z$)}_lPtzk|2(P^DNq$g$M&RTE? z)v08Yp=3ep9^cWO2Vk?WoV~`iHVv6R{oGdj^WCKQ<2`z$|DLjjba9~dg7&W&@}g_` zN0&aa8KO0Vx3j#=x0k+tmmWl$T6j${A$DQc&Fju#B(rDDoaj3~(5z$D zlv2V!A=uQ&sd36>!v{@G4MxvM5hc^l68X7pR;^kyWoBV#NkE4<`Kba{WZ<(8s6F9h zCXS(Y-Fxh4;LUM9oktxM=H3tGE;~5tw|b`en4Z&oFybEV2O2i796fITzMzTw8g3g9 z*ndi=7B}*D^=XUi-0Y^;>oioW0eBT=^o0GFPVcsSc0}zpeDL)R&U#Z>%ZsP|92ytc zcHD?9w_8le$>%IjEco=dzNtN=z8E!Le`fgFh1Wi1uRh%@Zjhg}Ue8BEZXOub>x1;< z^tJ`A-~G?&_;*y$vcY|&{x2s*?SGhl^D3{7GB8b`id_*LIb!yJqzy-N8chj$`ryvo zJ~>JI$FI0rXJgNuAuk5VkN4^F=cWz=*GOBX9-Uu!3jd;${Z;qDs}qOTt#e@c>sz(V z8!K&ndUjT*mGn)rBo`!x3U@$&L+ z)TlAIyuhDVdjlf?cuZG~SVWWnP8iCIS;IBbLaDk5o$W_07ojP@DDNv=?v(UMT zC@ToxfEYX|9)4Ye7Gly-@C!ls0?%$7U%&(N%|yDJ&a;c)-djU2wH~mPS-LU^pcd-85j%%&kSxEWI z#9V!0Qh7V;$9m?+dgjM^=Er*GJ0^)g)-ylWGe6cdKh`rp)-(SXtY?r(uV*6o@0Jf> zrJd!A85I?>rzklI|MV#&5GH>;`_&s~@-{yv;628R?OQBTkts_oY65;?a z_hf&5qaA~*;)I=k&%B!AKhn=XjkG%;qep>i)~oCyU1O%C=buU+Q|9U|w>xx{FZxJ0 z-D${&xBLU>+pj(*B^AyJ8WJ_&k3-C{lY<-YyRb5=(TxVDHvIEalwOoOir4As)CC=r z!kRuEbaqJeJ4%bthw57|Ui9!=yzce(X1`ya*l?Ndwsc{=QRf@2KHFz&JZ;&CK+@VJ z@ui!e%wH@l>KMa*dS&mbPUlFGDbs@0FB1BPEN$hLz4KA4&IR==8TIrzS7h;CFrCfUp)@6#GJkbmv`ObThQ2>s~nR?SM6> zhbY1lOV-RI)*U_bVb=}?3&qOf=~<&j$eOe-^1n`+IEFiT#Dh1da5ry8TngBUEqYKY zHZC!CSa)oMa8p#H!5i*0*wnDgoby+wZn^n3VZ!rm-4x;bTb{)~vTq=i6y-{H7Rgn& zl5;y3Y{L%tv+erciiKS|kGgPDgZ*tlASU6=&5qY&=DzHe@I*c+rSsia-KK{2pd8{{ z&+p!ebkYA;>SWxrMt<}+PiWhPf6b(COcW3O!0Py7@aQ6i_M_kE@$*AOlJ~C`lVeiR z(-Xos-=9%Db8x3@;o}cc7ed;~muRO?+?YTrtor3m@5JKCdfw>R!$VfQZ@fz|{ngEj zSnubXe;$2zT(dp}`?d*>A~`MhnuIOB#~uHCL&w(pX$jp;CDPW{B0iS3#O`=EZ6$70 zU+*gm`|RpkPqFWHpRjJ;>4$pG$#xunuYa^>hADi(g@+VkkA$V?8aBtuuN5k^TZSz= z^$RV8s99Dprr*i#XJyUXM(%6miGPO9q zUY&iX8~h%&ro;5VPnRPKj`p7-;?<_hyP0Mpal+^>Tf2pyft6m^`NWi8b19J5lZ4J4pH7{287+%x zG$BbHHf_J(*jC9o9}7PD-G%BltkZ+de_C?~1qlX%ZOUg{fg~harF@41>F(cRg8t*u z8MYQ*a0PywvBDDIR{?oKM!xC^tY)B27scWQ`s0Mu{>8oP?eBjQ%Ki98i);E|$BO4`JNOH))~Vyg z=8h0{}Eyfqqq4#o+$p~e2~}8#+`9z4=mMZDeO&` zoVs-XXrA88=rYaAi8&z7-ubg;)=y^FN>Of3{_}=Q3M3gzr=FGI+B`o$Jt~y#>Nn+? z>F2<*DHdNb8VTSG=2*JC9rv)P8-o zuV4Lhp{<%+)eKpGSd2fenbC~aq-j?st@X%-IA29S`@6>i%Ery@CO#DOXmk0>_>s(Q zCw6HM5O6JqQ95|Bx%v%Q zxS{SWXx`qVO-m0cdJLH!L8JESx#IkVI|Zh@A?+_-dQ3ffe&lpNzlQrIBbL;Avvf$o zh-k@}J8>^@GxzLXblygD3T(_2Q2xZJj6;tFV;-sfO5 z`lZD2sF;KK56-e9cDH?6;v3Om{Loz;nCn`P^qO({P|IwJefstto!S@9$ebuS8}nw^ zwm%Dl4(5Msc|A`!w$H9E_tu@C#UV<~>)0=a7y2&lXCU;|jo&m=WpKjc28LB8li_ z@72L=`ehs#vUpAOH03$g=CiKBpGKG;t)G`XV;e0t>Rk4s`$2O{{WsfF-b|BZEX(Mm zKkjEAvf|RYk`vFT-?BfLpU`?#?SCKn-U%AV?a+DliLd817t80Z(R9h{_GjTkAwiWB!jb@8DG5xM}e)~Bu#^m34@-gt?#-pp)3tF80 zwP1VTg;=>^oO#zN?dH(GGD&yk*GO|pCaj_I@T&~oZ5bmA)z`=O4(eIh?{a|OjhM|* ze`GFu-{(?>e3C@`X}l>tl!dbM#edN2C*5vhH>k+-cW>Jf)*`mPi61&Vq@~Z+lva0M z>yK#2w_AkN+nBZ#$LjtKdc>8lTu}$pKVn!;yWJCyrM?fb4KiohzDdoU3yrAyGt`*t8!wI-lRtIs1C@1*ert|;0S(`jqbc1eF6G$4JmXy~s^4iyi?O(|k7i#gU}Ik`12@Lur5HG$JkEj+xO z5EEK>efWq*_dc|4TK`%7W;vM>A-Na6=a^F}yhG%art@BBe z-567c*R*$B&A|>C%sivl*Zmg(fvx;}n!sKRA0Lkk1NL9|z%C50#vU(5Bd^B5c~R!S z@M_TDb1sZp{TGBv8~*=p0fHjAqaeT4e}Ryf4d6@^35oOp{tJ@Z4e~Yr1wxNZ7i#lg zARuMnUyW)8@2Q7l(mf6Fol(u@tF3?IzwpF4f0O^>zXdHLb@WJt`R#$?wYx1UDNBub zs{f(e;>X&M*7v&8r@_xc~qy^zCYWg{VcB&jbB174_v!sEy!3Ydow z@x%-Wyww7{`_mJx!3OVv5`!0gxT7@$W^l+#?kM{LURnVNk$4Xg{979#ablO;jKYRO zp4WpQ&nf~hkh5@}>?_Z-c;Hty)JClfjuin@8VY$2Wm}};Z99Al8Y50dez{{54pZW-4MZiBh zg296bZ6cP8{LV)=45nKsT{ z51OGm9pG}OmEi$83fN`)DxI66D({apL;^Kr`a>@NO)gj*iCSGJu{a79EJ5I_T<>ut z5`2`kS{j*LrsHM(14p9N&~Y3ISb=K1SR55zq*uYif+3;?9u^Cq2dzSf19qi)AH)%G z_!{NIg0~e`pYK=)h1DsbmVCg2gfjIA#=^%>plnrbUt&1O_3B#b#r%bOI5Fr(^I0 mI*>Ynf~T{wd%?h>qV)wY+ZFjeC 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 B9&Z2u From 43cd7fea3208134fbcf324d11492a9fd182506b4 Mon Sep 17 00:00:00 2001 From: Adrian Secord Date: Thu, 21 Apr 2016 16:27:55 -0400 Subject: [PATCH 091/129] Added MDCPageControl initWithCoder:. Fixes #61. Summary: Fixes #61. Reviewers: #mdc_ios_owners, cjcox Reviewed By: #mdc_ios_owners, cjcox Projects: #material_components_ios Differential Revision: http://codereview.cc/D713 --- components/PageControl/src/MDCPageControl.m | 55 ++++++++++++--------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/components/PageControl/src/MDCPageControl.m b/components/PageControl/src/MDCPageControl.m index c8870c12983..3f1b36dda34 100644 --- a/components/PageControl/src/MDCPageControl.m +++ b/components/PageControl/src/MDCPageControl.m @@ -66,34 +66,45 @@ @implementation MDCPageControl { - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { - CGFloat radius = kMDCPageControlIndicatorRadius; - CGFloat topEdge = (CGFloat)(floor(CGRectGetHeight(self.bounds) - (radius * 2)) / 2); - CGRect containerFrame = CGRectMake(0, topEdge, CGRectGetWidth(self.bounds), radius * 2); - _containerView = [[UIView alloc] initWithFrame:containerFrame]; - - _trackLayer = [[MDCPageControlTrackLayer alloc] initWithRadius:radius]; - [_containerView.layer addSublayer:_trackLayer]; - _containerView.autoresizingMask = - UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | - UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; - [self addSubview:_containerView]; - - // Defaults. - _currentPage = 0; - _currentPageIndicatorTintColor = - [UIColor colorWithWhite:kMDCPageControlCurrentPageIndicatorWhiteColor - alpha:1]; - _pageIndicatorTintColor = - [UIColor colorWithWhite:kMDCPageControlPageIndicatorWhiteColor - alpha:1]; + [self commonMDCPageControlInit]; + } + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonMDCPageControlInit]; } + return self; +} + +- (void)commonMDCPageControlInit { + CGFloat radius = kMDCPageControlIndicatorRadius; + CGFloat topEdge = (CGFloat)(floor(CGRectGetHeight(self.bounds) - (radius * 2)) / 2); + CGRect containerFrame = CGRectMake(0, topEdge, CGRectGetWidth(self.bounds), radius * 2); + _containerView = [[UIView alloc] initWithFrame:containerFrame]; + + _trackLayer = [[MDCPageControlTrackLayer alloc] initWithRadius:radius]; + [_containerView.layer addSublayer:_trackLayer]; + _containerView.autoresizingMask = + UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | + UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; + [self addSubview:_containerView]; + + // Defaults. + _currentPage = 0; + _currentPageIndicatorTintColor = + [UIColor colorWithWhite:kMDCPageControlCurrentPageIndicatorWhiteColor + alpha:1]; + _pageIndicatorTintColor = + [UIColor colorWithWhite:kMDCPageControlPageIndicatorWhiteColor + alpha:1]; UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; [self addGestureRecognizer:tapGestureRecognizer]; - - return self; } - (void)layoutSubviews { From fe107a900cde37fd4f4321d8cf333a20e040669f Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 16:12:50 -0400 Subject: [PATCH 092/129] [FlexibleHeader] When injecting insets, set the contentOffset rather than change it relatively. Summary: This resolves a bug when changing the tracking scroll view that could cause the new tracking scroll view's content offset to be set to an incorrect value based on the old scroll view's content offset. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D696 --- components/FlexibleHeader/src/MDCFlexibleHeaderView.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m index de1fe7c92bc..82c8b6dd809 100644 --- a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m +++ b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m @@ -355,8 +355,9 @@ - (MDCFlexibleHeaderScrollViewInfo *)fhv_addInsetsToScrollView:(UIScrollView *)s // _adjustContentOffsetIfNecessary doing the right thing most of the time. if (oldContentOffset.y == 0 && CGPointEqualToPoint(oldContentOffset, scrollView.contentOffset)) { - _contentOffset.y -= _maximumHeight; - _trackingScrollView.contentOffset = _contentOffset; + CGPoint contentOffset = scrollView.contentOffset; + contentOffset.y = -_maximumHeight; + scrollView.contentOffset = contentOffset; } _contentInsetsAreChanging = NO; } From 589c2bf3396a9844ca91f1a8e92a96fc92eaace1 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 16:15:57 -0400 Subject: [PATCH 093/129] [FlexibleHeader] Pull the instructions view out of the typical use example. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D675 --- ...FlexibleHeaderTypicalUseInstructionsView.h | 25 ++++ ...FlexibleHeaderTypicalUseInstructionsView.m | 129 ++++++++++++++++++ .../FlexibleHeaderTypicalUseSupplemental.h | 4 +- .../FlexibleHeaderTypicalUseSupplemental.m | 105 +------------- 4 files changed, 158 insertions(+), 105 deletions(-) create mode 100644 components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.h create mode 100644 components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.m diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.h b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.h new file mode 100644 index 00000000000..4c16df1ec1e --- /dev/null +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.h @@ -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. + */ + +/** 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 + +@interface FlexibleHeaderTypicalUseInstructionsView : UIView +@end diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.m new file mode 100644 index 00000000000..81983370168 --- /dev/null +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.m @@ -0,0 +1,129 @@ +/* + 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. + */ + +/** 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 "FlexibleHeaderTypicalUseInstructionsView.h" + +@implementation FlexibleHeaderTypicalUseInstructionsView + +- (void)drawRect:(CGRect)rect { + [[UIColor whiteColor] setFill]; + [[UIBezierPath bezierPathWithRect:rect] fill]; + + CGSize textSize = [self textSizeForRect:rect]; + CGRect rectForText = CGRectMake(rect.origin.x + rect.size.width / 2.f - textSize.width / 2.f, + rect.origin.y + rect.size.height / 2.f - textSize.height / 2.f, + textSize.width, textSize.height); + [[self instructionsString] drawInRect:rectForText]; + [self drawArrowWithFrame:CGRectMake(rect.size.width / 2.f - 12.f, + rect.size.height / 2.f - 58.f - 12.f, 24.f, 24.f)]; +} + +- (CGSize)textSizeForRect:(CGRect)frame { + return [[self instructionsString] + boundingRectWithSize:frame.size + options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading + context:nil] + .size; +} + +- (NSAttributedString *)instructionsString { + NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; + [style setAlignment:NSTextAlignmentCenter]; + [style setLineBreakMode:NSLineBreakByWordWrapping]; + + NSDictionary *instructionAttributes1 = + @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline], + NSForegroundColorAttributeName : [UIColor colorWithRed:(CGFloat)0.459 + green:(CGFloat)0.459 + blue:(CGFloat)0.459 + alpha:(CGFloat)0.87], + NSParagraphStyleAttributeName : style}; + NSDictionary *instructionAttributes2 = + @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline], + NSForegroundColorAttributeName : [UIColor colorWithRed:(CGFloat)0.459 + green:(CGFloat)0.459 + blue:(CGFloat)0.459 + alpha:(CGFloat)0.87], + NSParagraphStyleAttributeName : style}; + + NSString *instructionText1 = @"PULL DOWN\n\nMDCFlexibleHeaderViewController\nallows the\ + blue area to stretch\nwhen scrolled down.\n\n\n\n\n\n"; + NSMutableAttributedString *instructionsAttributedString1 = [[NSMutableAttributedString alloc] + initWithString:instructionText1]; + [instructionsAttributedString1 setAttributes:instructionAttributes1 + range:NSMakeRange(0, 9)]; + [instructionsAttributedString1 setAttributes:instructionAttributes2 + range:NSMakeRange(9, instructionText1.length - 9)]; + + NSString *instructionText2 = @"PUSH UP\n\nIt also adds a shadow\nwhen scrolled up.\n\n\n\n\n\n\n"; + NSMutableAttributedString *instructionsAttributedString2 = [[NSMutableAttributedString alloc] + initWithString:instructionText2]; + [instructionsAttributedString2 setAttributes:instructionAttributes1 + range:NSMakeRange(0, 7)]; + [instructionsAttributedString2 setAttributes:instructionAttributes2 + range:NSMakeRange(7, instructionText2.length - 7)]; + + [instructionsAttributedString1 appendAttributedString:instructionsAttributedString2]; + + return instructionsAttributedString1; +} + +- (void)drawArrowWithFrame:(CGRect)frame { + UIBezierPath *bezierPath = [UIBezierPath bezierPath]; + [bezierPath moveToPoint:CGPointMake(CGRectGetMinX(frame) + 16, + CGRectGetMinY(frame) + (CGFloat)17.01)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 16, CGRectGetMinY(frame) + 10)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 14, CGRectGetMinY(frame) + 10)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 14, + CGRectGetMinY(frame) + (CGFloat)17.01)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 11, + CGRectGetMinY(frame) + (CGFloat)17.01)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 15, CGRectGetMinY(frame) + 21)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 19, + CGRectGetMinY(frame) + (CGFloat)17.01)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 16, + CGRectGetMinY(frame) + (CGFloat)17.01)]; + [bezierPath closePath]; + [bezierPath moveToPoint:CGPointMake(CGRectGetMinX(frame) + 9, CGRectGetMinY(frame) + 3)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 5, + CGRectGetMinY(frame) + (CGFloat)6.99)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 8, + CGRectGetMinY(frame) + (CGFloat)6.99)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 8, CGRectGetMinY(frame) + 14)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 10, + CGRectGetMinY(frame) + 14)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 10, + CGRectGetMinY(frame) + (CGFloat)6.99)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 13, + CGRectGetMinY(frame) + (CGFloat)6.99)]; + [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 9, + CGRectGetMinY(frame) + 3)]; + [bezierPath closePath]; + bezierPath.miterLimit = 4; + + [[UIColor colorWithRed:(CGFloat)0.459 + green:(CGFloat)0.459 + blue:(CGFloat)0.459 + alpha:0.87f] setFill]; + [bezierPath fill]; +} + +@end diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.h b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.h index 6bb99c135bf..4133233c002 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.h +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.h @@ -21,11 +21,11 @@ #import -@class ExampleInstructionsViewFlexibleHeaderTypicalUse; +@class FlexibleHeaderTypicalUseInstructionsView; @interface FlexibleHeaderTypicalUseViewController : UIViewController -@property(nonatomic) ExampleInstructionsViewFlexibleHeaderTypicalUse *exampleView; +@property(nonatomic) FlexibleHeaderTypicalUseInstructionsView *exampleView; @property(nonatomic) UIScrollView *scrollView; @end diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m index dc99a50559f..eda5eb5419d 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m @@ -21,14 +21,9 @@ #import +#import "FlexibleHeaderTypicalUseInstructionsView.h" #import "FlexibleHeaderTypicalUseSupplemental.h" -#pragma mark - FlexibleHeaderTypicalUseViewController - -@interface ExampleInstructionsViewFlexibleHeaderTypicalUse : UIView - -@end - @implementation FlexibleHeaderTypicalUseViewController (CatalogByConvention) + (NSArray *)catalogBreadcrumbs { @@ -79,7 +74,7 @@ - (void)setupExampleViews { [self.view addConstraints:arrayOfConstraints]; - self.exampleView = [[ExampleInstructionsViewFlexibleHeaderTypicalUse alloc] + self.exampleView = [[FlexibleHeaderTypicalUseInstructionsView alloc] initWithFrame:self.scrollView.bounds]; [self.scrollView addSubview:self.exampleView]; @@ -108,99 +103,3 @@ - (void)setupExampleViews { } @end - -@implementation ExampleInstructionsViewFlexibleHeaderTypicalUse - -- (void)drawRect:(CGRect)rect { - [[UIColor whiteColor] setFill]; - [[UIBezierPath bezierPathWithRect:rect] fill]; - - CGSize textSize = [self textSizeForRect:rect]; - CGRect rectForText = CGRectMake(rect.origin.x + rect.size.width / 2.f - textSize.width / 2.f, - rect.origin.y + rect.size.height / 2.f - textSize.height / 2.f, - textSize.width, textSize.height); - [[self instructionsString] drawInRect:rectForText]; - [self drawArrowWithFrame:CGRectMake(rect.size.width / 2.f - 12.f, - rect.size.height / 2.f - 58.f - 12.f, 24.f, 24.f)]; -} - -- (CGSize)textSizeForRect:(CGRect)frame { - return [[self instructionsString] - boundingRectWithSize:frame.size - options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading - context:nil] - .size; -} - -- (NSAttributedString *)instructionsString { - NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; - [style setAlignment:NSTextAlignmentCenter]; - [style setLineBreakMode:NSLineBreakByWordWrapping]; - - NSDictionary *instructionAttributes1 = - @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; - NSDictionary *instructionAttributes2 = - @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; - - NSString *instructionText1 = @"PULL DOWN\n\nMDCFlexibleHeaderViewController\nallows the\ - blue area to stretch\nwhen scrolled down.\n\n\n\n\n\n"; - NSMutableAttributedString *instructionsAttributedString1 = [[NSMutableAttributedString alloc] - initWithString:instructionText1]; - [instructionsAttributedString1 setAttributes:instructionAttributes1 - range:NSMakeRange(0, 9)]; - [instructionsAttributedString1 setAttributes:instructionAttributes2 - range:NSMakeRange(9, instructionText1.length - 9)]; - - NSString *instructionText2 = @"PUSH UP\n\nIt also adds a shadow\nwhen scrolled up.\n\n\n\n\n\n\n"; - NSMutableAttributedString *instructionsAttributedString2 = [[NSMutableAttributedString alloc] - initWithString:instructionText2]; - [instructionsAttributedString2 setAttributes:instructionAttributes1 - range:NSMakeRange(0, 7)]; - [instructionsAttributedString2 setAttributes:instructionAttributes2 - range:NSMakeRange(7, instructionText2.length - 7)]; - - [instructionsAttributedString1 appendAttributedString:instructionsAttributedString2]; - - return instructionsAttributedString1; -} - -- (void)drawArrowWithFrame:(CGRect)frame { - UIBezierPath *bezierPath = [UIBezierPath bezierPath]; - [bezierPath moveToPoint:CGPointMake(CGRectGetMinX(frame) + 16, CGRectGetMinY(frame) + 17.01)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 16, CGRectGetMinY(frame) + 10)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 14, CGRectGetMinY(frame) + 10)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 14, CGRectGetMinY(frame) + 17.01)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 11, CGRectGetMinY(frame) + 17.01)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 15, CGRectGetMinY(frame) + 21)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 19, CGRectGetMinY(frame) + 17.01)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 16, CGRectGetMinY(frame) + 17.01)]; - [bezierPath closePath]; - [bezierPath moveToPoint:CGPointMake(CGRectGetMinX(frame) + 9, CGRectGetMinY(frame) + 3)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 5, CGRectGetMinY(frame) + 6.99)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 8, CGRectGetMinY(frame) + 6.99)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 8, CGRectGetMinY(frame) + 14)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 10, CGRectGetMinY(frame) + 14)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 10, CGRectGetMinY(frame) + 6.99)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 13, CGRectGetMinY(frame) + 6.99)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 9, CGRectGetMinY(frame) + 3)]; - [bezierPath closePath]; - bezierPath.miterLimit = 4; - - [[UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f] setFill]; - [bezierPath fill]; -} - -@end From bb831db0cda8df10f4a6e0416eec7d5940cb8637 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Fri, 22 Apr 2016 09:58:26 -0400 Subject: [PATCH 094/129] [Catalog] Add MDC app icons to catalog Reviewers: ajsecord, #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D722 --- .../AppIcon.appiconset/Contents.json | 36 ++++++++++++------ .../logo_mdc_ios_color_29pt.png | Bin 0 -> 786 bytes .../logo_mdc_ios_color_29pt_2x.png | Bin 0 -> 1645 bytes .../logo_mdc_ios_color_29pt_3x.png | Bin 0 -> 2617 bytes .../logo_mdc_ios_color_40pt.png | Bin 0 -> 1109 bytes .../logo_mdc_ios_color_40pt_2x.png | Bin 0 -> 2367 bytes .../logo_mdc_ios_color_40pt_3x.png | Bin 0 -> 3673 bytes .../logo_mdc_ios_color_60pt_3x.png | Bin 0 -> 5715 bytes .../logo_mdc_ios_color_76pt.png | Bin 0 -> 2252 bytes .../logo_mdc_ios_color_76pt_2x.png | Bin 0 -> 4740 bytes 10 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_29pt.png create mode 100644 catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_29pt_2x.png create mode 100644 catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_29pt_3x.png create mode 100644 catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_40pt.png create mode 100644 catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_40pt_2x.png create mode 100644 catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_40pt_3x.png create mode 100644 catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_60pt_3x.png create mode 100644 catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_76pt.png create mode 100644 catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_76pt_2x.png diff --git a/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/Contents.json b/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/Contents.json index eeea76c2db5..eb396b35fc2 100644 --- a/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,63 +1,75 @@ { "images" : [ { - "idiom" : "iphone", "size" : "29x29", + "idiom" : "iphone", + "filename" : "logo_mdc_ios_color_29pt_2x.png", "scale" : "2x" }, { - "idiom" : "iphone", "size" : "29x29", + "idiom" : "iphone", + "filename" : "logo_mdc_ios_color_29pt_3x.png", "scale" : "3x" }, { - "idiom" : "iphone", "size" : "40x40", + "idiom" : "iphone", + "filename" : "logo_mdc_ios_color_40pt_2x.png", "scale" : "2x" }, { - "idiom" : "iphone", "size" : "40x40", + "idiom" : "iphone", + "filename" : "logo_mdc_ios_color_40pt_3x.png", "scale" : "3x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "logo_mdc_ios_color_40pt_3x.png", "scale" : "2x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "logo_mdc_ios_color_60pt_3x.png", "scale" : "3x" }, { - "idiom" : "ipad", "size" : "29x29", + "idiom" : "ipad", + "filename" : "logo_mdc_ios_color_29pt.png", "scale" : "1x" }, { - "idiom" : "ipad", "size" : "29x29", + "idiom" : "ipad", + "filename" : "logo_mdc_ios_color_29pt_2x.png", "scale" : "2x" }, { - "idiom" : "ipad", "size" : "40x40", + "idiom" : "ipad", + "filename" : "logo_mdc_ios_color_40pt.png", "scale" : "1x" }, { - "idiom" : "ipad", "size" : "40x40", + "idiom" : "ipad", + "filename" : "logo_mdc_ios_color_40pt_2x.png", "scale" : "2x" }, { - "idiom" : "ipad", "size" : "76x76", + "idiom" : "ipad", + "filename" : "logo_mdc_ios_color_76pt.png", "scale" : "1x" }, { - "idiom" : "ipad", "size" : "76x76", + "idiom" : "ipad", + "filename" : "logo_mdc_ios_color_76pt_2x.png", "scale" : "2x" }, { diff --git a/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_29pt.png b/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_29pt.png new file mode 100644 index 0000000000000000000000000000000000000000..61e685232d6de46ddbb5cf125c41551aa179583b GIT binary patch literal 786 zcmV+t1MU2YP)S@$%_Pw$g9u{cXvPcTi4myncnH{_ju5=jXPxb zul;{_)wh33?IJV7|DeFTWA6W<=nv-75@wKQ;=skN)F=ZYMEI;nO9|Y3Zt}OeO4Iv2 zj(~1F9YvpZ^h+IZS7&1Rkxior-d}_ZPE7vZK75;!yc-A(`lYt~Y8Sjd=#fB~;f`1H zzqh~HgxVSzK%N_u@TEhkIGr;5VV9ggfaLv}`WxJ@Qu4mg_NyWksPck)?#M8^1UMW;t!}R9g1qfp-u=$l@NaRqQ%h6N#E1IFvY+pz#k}mS zE6X!$+Z={s_Ezi5NMe{_Z29q3^-Hd{yiuSZ?d4iS6^Y=L9!`7nT&o|AQo`?N)ucRk z(DWyJ-$zAme$M@Npkdn1T6?4;iEiZ0wVs-tai!#W`X{Srp1SB}C2p?PPYKDLCjmV8 zdGRAxX*w!HU$&iDi`91+OYcu+9Z}-aZhfvl4))SG3YlFAI;nBwD?jv&>ME+ZyCds& z+-O_p$j2AH?&&0|j*IxTFE$_ug2J4>Ji}FG-dS&*8)%8H^%I+A#g$Ti6B@bom#I+Z zb6oJJx3a#Ui75Jlaj%a8bPRy9T<#3(ELN$PQddB zT6}2fwDnDlQJNrd!1lbQcpuQ=I_>P53+#RwUUz1E&*gIfaQr`i%>Ajp18SfB>i|D> QY5)KL07*qoM6N<$f^MakMF0Q* literal 0 HcmV?d00001 diff --git a/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_29pt_2x.png b/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_29pt_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..21333b511a7dfe42db762891fa5d29737abdd925 GIT binary patch literal 1645 zcmV-z29o)SP)|-DjMF+SG!j{KE98EaY=075H|2v$e^>M#_$?pHZm*_LE}Zt893qel zx~!)jJNL4g3AE;R<@9g^msn`rv}3Q5z{=x(5O06HTnEYWps@381V#HcR|#|1RMOot zJt+6>M0aW<@ff*hjXvl@i?8!QCCVLqi4?Sg-ifh2F0R?*#aT0&D4cawIsH+(8^?_} zpgY-2JQnT=1B2rptRhe=?5{a?zm>-giE)T`HGL6nWFI+K=fX~#+C<`}T~kIx#C71N z74K~o?Qf4znbTV6N6GcEY(9`-+FMQHru}WQ88>ZclnqQPI&2Kr*glBuVAw~;ZFG8b z^dLSId?lrkM4_ncz3htuqi@ggItl;?qMDUx57r_tnsxTR0&&f9Y9@zRuHgq)2b}c8m8e|R!BT7 zmtlW~v=OyJuIQLiyr)HUgqt>y8>+F}DfXeb*Dcs-V`~YVb^196 z&KeTuRBkE~9neLntbta@9edvO;9NKD%f?u<+USi!P!M>?{qUGU&xV0n~*D?QqU{f1faZWVP$sjeRIzk7{~?j z+=nYYJf*)rnI;HDts!cQd`y;Xe2#QtzI0N7a#JxnE$D5vO_}PrKd#XOiD=wIE|j6I zE~mv>8wv%zmFN;LzRGbYv8l>|?@=KuXZQ7;Gu zg&Z-;M`udM!D%@w=$lp7{MJD3TWZ_q@pkqexvia8YjOhtWoXNbJ>fAOZzEQic+Pp* z=xjJGw89kX7Ssw$Zl>?f^|5TO-izbztiW=A5v@a^Fv($^Y*eOne6D0dpp~opq6;Bv zE3^i3-&EOT(GG^a8@JxUWa+#xo;9HkYh7GOgv52C&^Hsfz8ZJ@_6co$}uaKg>D_xTqEM@tm^}YCE(Ba^FyK zXTZbYxn(vQW91%lS69%2&Au!Eg}oKu`Sp-2<$scu{nEP4JBwOHUH$Sv9nT86phY(b zVJ^wAIBRAbv#FfKaX&q#gGyd+B)HyPU7`4QVr1`>$i8Xc|8xqq!rq&4N~*S?*4MkT zl0E_MH4B5|YHK_=>!dRdR0(@4rsKN->D;t^(f~KIUs|t47g1}(_&m@r_Ehq$e(vzI zEoC?;74$nt4SprB4;B}JX_dWGffe@MWC1HoGO6G_wFR}lc8tqH z_-^CsT@0|oc5Z8Xr2{Sf?iK>y{)x>*E>b=pA_#qycv=RltZ^vPVVUvDkx8D$3biZ=f_)0)#v r1pk#17wX}K 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 bpg=(b^<1wNljl0r00000NkvXXu0mjf$EMKa literal 0 HcmV?d00001 diff --git a/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_40pt.png b/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_40pt.png new file mode 100644 index 0000000000000000000000000000000000000000..fc339020a6070dea215bd5aeb99710d98c6f112d GIT binary patch literal 1109 zcmV-b1giUqP)X!Sz#fzi&5DXmn(Kkv2ELD+j}#M(Zl!0t9v&y!JqNl%du79p-_#+ z)Xw^peT<$OlI|UrM1)n10zEJ!0!dGggs+^o3vDU3ytqtZG~NsY&sQ^3d}CUaWIqNf zl$(-I>;8CE>;!OcDA)1^0a%LFNL*Z;#;1$0-JkJ6yrbY)==sbsmVzi_cdU}VIxS1G zua|YCur?a${jel<2>f%P4AsVWpZVO!EfW;+#*t zQ@&Y3TYk@-pgw4&-_6%3R=Fw1o% zG*d3-EAdQw7ZsPX@Av)0Oibpa{&;Qo-P?hH*Y9F z&kn}O0txI*lA=kHsIBm zt0s?pvq;F5n2t$;PDgvbQ8HQo58FSb)lJ2l>U-|mn)*$SgH!iuI(cu;NdNtRVb;Ox b;C1i@@IL&e^JSK800000NkvXXu0mjfR9hN| literal 0 HcmV?d00001 diff --git a/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_40pt_2x.png b/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_40pt_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7876f314f6f1dcc600282d4345f85b861feba1b0 GIT binary patch literal 2367 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 literal 0 HcmV?d00001 diff --git a/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_40pt_3x.png b/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_40pt_3x.png new file mode 100644 index 0000000000000000000000000000000000000000..964b2447b9ff1ca6651957ef404b56b60a7a5da6 GIT binary patch literal 3673 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 literal 0 HcmV?d00001 diff --git a/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_60pt_3x.png b/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_60pt_3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6b845f2e59ee7926d13458ec3d10c18372117ff GIT binary patch literal 5715 zcmZu#cTf|+vnIsQBp^jF1ThFGNHt)HNC~}2lO`ZY5JC--fb^D7r5YktK$_ANh;*bF zdPk}>r3Fzb(gm^b@Xfq=zc=%4zAJy+?#}Igdv~+3#)jHp7Csg_Iy$hfj)uwUT=TCp z(Vvc0mB|=7Ivze<4OKILz{XhYIV$QL9pzYP%gC6R6)Cvzm+mGB_w7{6_jNyR)`pT#SDQ;V8kdBt}iE(V4lXB2_wglFB^C z^Tww`@eV)f_QuCuxfpjvXvW5Z+`HY-$?<6pi!1aveDJQY^U&3ZMZy`EnNA{NK81yz zC5HF*-_9kJYR^umWLsm7SOP7liSr6uHph!D4C-Ojk-nI$Qt@lt677h3y(Cc+hqAdo z+DVj=e-J+avp?eOFZ40f>e(3qx3R%tAPnlha*7;2Bc+VK-@C z;PFGX4b5Xw3A9S79rIs}%Xrd$0E8=M_$F7{?JhhFracz*J&;Vcq9u>G)3kX5=;=)v5DW1aV}W7Tgl-@jgf zs0)}rKaKp0kZ65$DL%AFVk@g~t-CaE;WM54a&P9ieJ~`bc!^n4tuk@TW>J;lIP<~1 zOL=ZRB@7Gq6n{T4N#7L+p->KRyE6m;j3AN`1VmmkR$m9e(=e& zKK`>y1JgJ93I_#^x-?Y4h z!?MiPYvq2*Bu%V!FJ*O9CPsud_DSZ^j`n;$%s;V%rX@u_ozNOz`7}GtS?ifNGfNwi z$`8cr=tXbn;l@?^FWiB8er4u3jX+6vAJ&8F5it=RGN;H^CflYn8P}0Y8o_KF4){C_ zhP-Q1n`3au$AjO^nx0-=WTM_o6Dnb7_=nCu~0A%I*h-K6b(L@ewulxaUcBX%B@3+_I z5mvd$BfpG#g2y(yG^P}onUYoS$!I_S_aZKF>Z@Qe6~^Jt?KH?AE5Iv zT7PW)#@!)6dSkjf+o$y>@TE`p8$l+|mW{cB5eVrQ-g1tsp3BR85eXR#7bUgx)yyb~ zRCJ~Rey;sxIwsP;sXScxRn<%-lDl?RbHr+w@sU=b*4z77oOg)?EYASv+tsgZT!WNQ z5_2xEL-Z=u(6RO6-&yt{seh9Rkx~xDPkJz_yE{4d`#=yqOs^bF<~_W6zSavrDAOyU zV{NN=zcg5i5{S%(~?o{H-^E524j2h4EKm=k{XU6Uyat?6hv)s{7%8vsQIX zZtyN2n{Hp;0D(2Zt3jgo1WT*61*p3f;!UPJ)S+5s68s(b8Nw{d-G=^c!d0p94*)*f zXrFs28`EBH4)Be}^p{)E7S+(F=$Dk;6_^-9mh-1;BMK zXPXEjD>Qbt@`JwkVNX@r>ICgdH0_vCGFh_<_~i2G?!vd2KBMCYk6D2dI`uA z-*pimfimL8^zUaEvyx_y7=8R8likOg_}>TR;v{1~d9}rc^##LE|J}~VsNw40HmTu6 zITca#cgQ||RYqU*x3WK~^kJXk7OS_72Jx4-=-nEOdz8%~ zn0@hE%^$$8gIdY08G?1dK;^)rL@?w0oAciQj2b$^8#!qsXU4ZUAyu#xz>H_`nd&7Z z7fyruOV{H{$32ypDhx_V7zdr2#;x3il0aH7SFmx>X`q_J@}d^ImqQW~eJ&v{NAQ8j z;b@#;0xooeAbALZ2v-w9j_y!)wq^$hVx9?ScIjogw*6E;fkxKe&`H}LSSFrMW?qrt z&&lIy4-qm%)Mf1KkrWOW-u}qp(<54)M(T5&3gLCKh-SH}FVAFZhfD1H-Z0NoNqPMe1CVSowts}{uy zU%sO^ydVQr%Sdb-Z4cN!6+c4#r1sn`ChG`@V)>2J#zs*1kJ3vfi)Tfy-3>A>yDCQ< zij%&Y4yaw&TkY@`T}GX|By1Dd^2CUF35!zSI9tDr>0!|LwqvW;meKHfEj81qm8fb){yDj0Ui+@_-ZC2Xo_ZYM5Rbb zVS5}2l+W7xa8C9I7D>I$qG+7=?kN#`AXa4+EyAy^R+a!}(Z++XpWO&IGmvsHB7Ybz z&c1rKBEjN0W;>bpIRcG1vxi1l7K4*m2VECT(^$h#3GCnIk=>fVNLS1pwO`GVrat(f zve8Z$v;v1Uf!XKZYt^8of)En@RGa2K4#rg05_eQ;+q4XB#T?p;K{=aDL|&nMs+`;o z!PZFRS)^maKD`<0o_S0gX1L2R5jGcrxbH<~)H`btYBg*0r0@eeLA;=cBRju{zU{mB zmZGvm16(G?+z*F1Q!O#ffTT>MKfkGn!QrQW_WMK|B?_slN&&}mNp8Awl(`?xMYS5L z1N3g*-!IO;VRo|025X>8pRaG#9A3McLcADISVV23B_-N0aFZ&+D@6h#ly)U36k7&i z%Rr!$x=TAp*YiY$erl`lK*2xyp4?qizL+HM6$lxHcO*N48xs!L?PbfXt}t1mJd7OM zu}sCET6rQ{_8 zbW_;Qaz>7KM9B()s)wqtKgk4(JEHA!jD%8%$h-%&OI(Zo;eOZ`nxEI%b-!UG8f{0( zDWDHO^2}s5{{S&;z37D(mTmOwlnpK3Elz95$WhjfOVAh-=f_29fPkV81FnS;_XH*5 zSBaC7zt7XrJE^0ef&F!fjB%TA%6p^MT8WOgj4Vz1QA>r(rzuDUaB#*(dg_!iLdKW- znXK|yOU5~}6LG8jaemmkH-rtIjOQ95A#u#kz+cK1KX-7wH?*`E1e3)}-HvB;#%6+2 zMyd$bK?5;{@n2MW}IiKOq!a4qaExUkQf)x!wO=*dyrDP06z* zIrn1sdSN@b*zwL0Lu$Jm6b8IZ0)S`!L|SOcddI>qDzSaP2o)me>FQXt40ami>aS%cL=_P3$HV_)hznDiR#7RrH;qr z-4%D&5TWKS;c*~GKPfv!HTr5n0mzENHA_Xo*8^2hB-9L^H-GPY{l3K2Z1CXK+qa)Y zG&;mf*4Rdih_)TbK!eCW74rc#5)`6$Wu`L_ccv+U4G-a3Yu-VWC;V7d_Isb73aH&l za|a(p2$hrd8BA03!}QCDl_E}Az=v76lQpcyV^swHBxfS)7Zu@U4x*G$gspbW+azXQ zBc`o4KiJAyOMbm=+Ef(7pc(C6eZy1#NS|KxDz;#|+q(>b+@wr2*^K=;k30GLM`55- zhmZcWvqOh%Pjvi;PX(#OkN~l#77P;;rWGTQSUmM2R4FNZR$Z6R3*s&y>S~2fVl>eV z(;2Mh5EcL-)c`T;4cqU#wSo~+&4=AhHSjz`aZl#v{xw*K0{`txAB_JI$qPdAv~C&H z@~aP4<1&^#3L-q{ebK9ONfhQ_CaP#k01b9K)AE?Uq{?F*iExSi&p#)4UQ`p$mx3LU z&r{a=FdD1h*d`(pqU2YEVHbETzvOsohMl1TK~6wNDs~YFvP5l5%Ls1M?!zgl^4O zzVZa$J(^%T&C?Zw{>B0XBsF#I!xxi0Jgd_9dsZ?`lwl1LlnQ9>EgCi3K`l$RKTfCoJEEJwnGKY6-CG)m8&};vd%q*c$%>l)8A(d=u639PiS9;I3 zynw!K;;&8X-ZtGUC!qIp{?ULggKIpZz_rGm``%Byl;I7?yt}*BukebJ7!-+vS^w^) z;fL#Ymd*w7Qp89Ta+J3x8)x=zvxEv;Pt8!HJsQ--f!;0Tp=A4CSTvXCwek*onG21u z2CJYRUtwL~m9^}sdNTXm(Zv3aNIvu6Uw@un>JyEjY7Xn38EPlj<+tUHi4rA_C|iq# zhcrzw;5g)UTmFY?{D^4dv*>D78@t0`IF~+oP1Up@Cv!ad8UDIh=ABWNP>HNoon2#$bK*P|Z4VLKxP|b~qhm{dtZxyO&e%m=xB7V?;#fOAp{s+Y0V;FSibp4~%jc>C4d8?F zVq2F)?S0Y#U|Zju)68Qq6mTXpV^Dta`xc8OW;Cx6?&MduV3Xv5j4*XWP`GXJX64So zWVE;FGxSv0K!`Pjbn!PuuM&O@VyqW+1394p6lC)&b@L-9ohPy)Gg&^eoIZf=OZTp0 zA3b(OY_ofyie{KPW+-u>)f$=Pq40OC`oSBC9?}&y5R#3@P-X{OF50nF*D>;o>!GkK z;EcTTgDV>8l#9lbK7V96dI|d4cQU{Yy4RQJARnsGd^HDvJ~YK$UL`+W`+96bQMw+h zmbl87hTK=eZFn)w4jvTeqRW>%dM@nMcILId!OSFQyE zV#KuDREw*=O0ID@ttpkV!txwByzClLljB)a-n?Q4%b2yf=6fte9m7r!(nApGE{y_MG9UKKpG?T5M${eh)FTlz;j=Xh=OTWK-H{>3~3Z28MtI)sL z`6Ryg6(JpX_84wn;R_+b765ht)4&eEg@X31}!+ zSz7dNkn*)~=GeihDUO~cVXzRP%;(9gtYh&-cipT%qx#dVy#dJi;h4wTQAE}~gL@a4 z&-Mm9${26hmU`tm+D}j@fBUJw@FD!;fW1uQRk+=GchPsH%->xXRxJXRNsTI!JE2zJ ze--VznWVmyQW9U-&kJw9Up$u8m~L`+EKF^h5_YU=c(Ut9FA{b}ft{Tb% z>)J9G67^D*EA>lOey10Vz2e%dO?zvV7h+!YYJv~? zVXNw7!S@^Cw7MG(usr!-P4I;H^8II#_TQTDuTJ~F`2Xv;y=|}Q6PZFU4dE&(Pu0=s MY8q-(sG-CE3&)a=e=i0RDYd-Lvn9UvZQOf|4jE zN5ly!x170f5r1&w;ORcJ}@h~^J?Mw`BQK*IE@s$hr%QSVwGNKBJZ{jgqgY9k-Zom2Jx%^_B6l~ zLwb>_N0|03`%I~0o>;=w7Gx(>bhAlmK!xmU<=``cTy&JimcTYU$BKDvKbCY1VAk~g zrV6x00AIiP89+J_^aO&@UE7f*-$ou;ehXcB#G#WAsfE*2}j+8C256`Yv#~CTH)WTGPervHuYZAKK^8#Vo6T`S@C2<2$|XOSvAh-h?1+;`(#vD zH~-;5|8XM~(E}&1LIfpEXh;a)N<%?jc(4ECTTUeDu_AZ+Ns-u_Me6CuK z12zZCB4mL0`h&0)s81bP>`C*$&^!%SNuspS%$1FSunXD(O~`#8r6O(-=Y)6&_`#jv zw2|g49%I^LULsa?#nTJS2uZhn8-^v#wt6nu7FZTA1|K8;vK{TZ>QhAC@8$1a^Q6Wz z^H#A-7SDdDa@Pe=#5h?@Tl4Lk?x%(9hSZaoEet}@B2JJMKT<0=sSAykwHzO~1-1=% zP{vf=+m}mSc>?4K%b3?M1aNt|2aF|GNs=-S05gP13L9IU zjViM_F-C}^mVNVvr|9&M6<5XyH7l+kd&X|sVNiOXL#63_vqf zmv`dRK`Npphx9W;*I-F--DZg2+znGaQ<%hF*+|SB%gdz+q)}z0gYCa}%Z=4(Ah-Hb zo5f9V$BhC))0RdT_j-fr8Lfbt5S@Wt*$0lmnCr*S51N`#izn&VE5S*TLcyAgDbuJ~ zeCR%DG`_vB6;KxvP+?_*7!uwVN$qjF15)j5rpb>+G91sD9Lc04vV zigzfqs}1fr#2E00Wuyaylybga*KSm^QNMpXd=#V`QzOm>9?s&Hd6T9kaV+ktDUGM! zcm(#!red6s0igC9n(dAH=ONUDS}tz~+XI!0G>15Xyi_~^dHLHyM_SzEQHpHsmeKQp zvE)J`1HpFpv{p1n-*4S;KN+$UsBWBtOx~%Il`V9=cx7yMG%pDXHD4+NW5gIiqBm16 zbzfAzRz?kdYhT>&$WMU0*Hzl7VW#ZockG)>Jz`AbY4gC@;?}$rheE)S4;XiS7mONf z>~CE4=>J64J^j77sr7x~5YHC2op5hzj(|q-0Dy<_+2W>McIVDa0AdE9)f~Jm`v8#f zEp*ipSE?c(NdOC4H=0_a-B6dC$K;=v`-I1A<}h$>VVy@35M~d4n!>cIu~M>#G@+K) zuDgozkb8X*143p7zVLwQHf^7V+nkxzY+AqB1OLq0E^v;>tm@#xxv9~@B)y*h#i(Nn z)9gl|{eSRiuxFz!Z=ml+RH?)C- z*Ngseq*So#BIUgoF9{p6L-2NUxGk-I>h2045Mug~$}3&QaQJNWF>9+>nFXl{wMPBX zoxm5vkX2u3yPIhZ;@oe~H`m(1#JD#7Xql|FXKD>#43iv53QqLQ`6;7-N&W|jBJpgi zHdL4fzg3EdAe#YYzw?%{s3=RTjCM%{HwFdDemEy(T!z#jo(|jYaL&>~vr#u!YZo!r zgxZ=b)nQ1ba=sbYeWf5L|9a4h#|zo_>da1UqU``O1Vq%&Pwt7Q=f_72lSoB#^=gKY z079XLAm=Q~0Ekf3rDfJGomi+3)nw)pK%)P4()zOa0K~rB!FoPuE^}=}4&5r5SwV)iYmus*wK&ghG;1M|cH!A?gQ`ASYu7 zG|m|!<{9!Z7=n)*Qlkf(5vn;3~+AAR!?kAt50l aAt47=S5_h{W(g|Q7MvZLdomZiy4$qitApRw zs^yBWd65uL@&D-6#jV;a1F>$|taQ1^HZLzvsPMLvQL38p#k4R)Qf5*m zVaI>2+h(2lMC0`2#+Q$}b6kZ}+Q)*rT_GRsxrG*%QuaOA{&M$qYBY0%gw`yG&6Ek- z`=)!xJiuR+bB$Ys_eoL~C$Cs0S8zgh1@5jW3{j@sw^8+xrynTslg|}T% z{e@Tl@U0X|(HW@CjzRh6%GvXgiy%h0@0H#K4dZN8ykLijspJ#EfCn(FMfeoBkm*Q* zoc5U*Cq=T-KjT_qsj9W*>4dC2fX*wl%<1uS9?ikk+CN-iMBgH~oU^2%kAm~CbE1N( z>toBo%RQqE!2X1D&QJ7EYpA5ab9GGxvje{@j)<>t<|OaHvEKWP(&ZF5q>W%67xD01|YdhYb;MEEVHyi(kZu-wbt?Gs~+3yJOiAF0{9J7 z|61A-lauFCiOyeAuaa#7JG@x^nbr~~`$sW!ryy9Wbf7;@ZJh@Xe<1I?8&#&Z6qN~jpY7mSO-`e&7h!+-x_ z3GOq3z5_R}6{wJl+g&L-Jb`zk-Gzh0o$hBS;(fGCk=6PVLBjZPyiZMY2`(-^4<@Rj z=80;mD4zb7DTJ3*c(z)yey|KA)qBwLUUgLV0Yct<4W+M$?57#W_yGO9e0$AK?;;iQ z@6L*8E%}-0#-x!!-q1H1p+JdsGZC5 zaGn|ayibFdm(xmQaz%#g^WX{18t}fMmLoQ%^o||nP!!=eH_hgkaQ3-;-2M_9la(M* z7;{e5fEE3eEy@lfuJGPGoiY~|*v(epH>X(OYQGEnM7^CqxYg$6Qo zJ^`{S%Hy$`Q7(RSp5|dsBH?|WTfUAbk){D}Z=DM!EtoLYEQrjN?pvi(s^Zs%udL=t zFGqO?*`Ipjni_&Cq!q^qLHz?P(Q>R`z89*JiN;*O-WjL1-Dq!l{NVg)KAZ_U%r8m0 zKBj8LM&v$k4e(6t@&YRm@o&QVO{m)?^>WMGjZt&p+Z3wE=)id^mc6XRsv<^*@vVaV zwBSBLyB~3du;)096TyaG@`1lD{T2l{D3c$c13|n;ALPcYU$`?1{T}_{X*YzDxN|=>3Hz<~ z^2(=IIW7c)rh=z)pxAGIx_%fvY_%h~Co!k&!sI%&i@~`*XMPq3g;vi{9d@zicN%1_ zlCz%=CUKGAk7Jg%VU}FF2yo?+4HpNMVl8>?E*BAh5cFF26VirDl-Y0Y)BYAHadE^K zf}A`}t}FwhKO1=9Y<#qE7cooP*5A25rc)R{u7N_6CDwrwJ@dXqIMGp|t4yF~z{CT` zb|Xxr!)M5I4b}%}6zm5D^1fOX5#4Ke*y1yND+4xC-I~kF9QL%ndQev_7>oZC7DfX} z&x3oD57{ckQD`miZzVe0(^mih|46pM3Tps$wMC$xY-2G>IH>JWG;Iu#44t>vo+KD^ zWgRe8d#v77RKcj8Lu>O5Z0bqKHZ7bFETLH$d zjPbCc3#b(4m9RVIFh+rfO-kjtejeFuxX__;5MpiG_G0CDq8)}9xY4d5F(w9KLtBgx z4hHRplX=#uEDBNsd^Elal+oyfl|{nW>9hF6`u%`T)zSAg7;I>oj5#C2j9gHKDO)3PX3_2B$f`U#~1fj zNMTrU_OM0Uzm9E{0So`eIMt`9ztDpbXR~NPw&1W=!u*4}BipXZ;mfu&yapHFl!(0E zv8*cP81eOggnRqT|83&0!}bWRntZ+fmZ5Arp~7hi2aC6!ZrUffSGV=INY)urbRR)& zHf?(?@VD2e%GAW8g&rrOkdqvofvmU0RoOff3ewHHj?}w4`4B_$3f2IZI<;RLgFojj zfR^QEb3RvnDHBugM+Zau`2CFux{U})ZmoQFlKRjw4W&JqqNXM_CZ2AN{r2NwlFB|8 zEN1mjP`&(BgZ$47c=P5@ud*mvbLIxeOxl`IC3b&(MUz+j^A#?5OyQ!C7!tDwpt|$Q zRJ?uBx7sKU;8?K_Y&{m$zrvONj6H&gi}~Rj|I-zla_~nvXn8?>>;qMUUPRsd#YEeb zGKr`b>EUYAdXf}B#E;ex>UZqsY#j&r=l$rJ6$0IAeo5{1_s2^O>-G-lZ{f+sWWE?6 zbzAiXWSC#^?MA(S+~$VhOe{NczUHV9;b|a`sCH~gI$4+WH|4SZ2@E*tWLO+>4sqd} zU&2^p1akiZBz2#{KVcTookHYAup!3Ozkfbl06Cc$@ziv%_Q5JDBir-+(NQkL-q3>^em9>@ z#L8lFw70Nwzn6fd54RInwo5`<&jAKNvewY~j$-C2_KF+ZZFBeqhHSX)dQ-(#xx!7~ z>mC;h1@+AnBF-S`1sl~HKS9zk#D4CRX0508zmfjG8;><RW}djdrkC4WJj#JJqBlQTVz@mArB5cXj}e2tIO`_T+V% zatIQ&8{jmFRr&SSXxfF;BAwR_lD*@X!&44x?Gctvk`}5M zD73fZ(|N42!L>Q?Ow20mE}_ocQ(yjGF~{_i)$lv0Kbhz$JrCT;jkRH4!}MSt?q0ZK z=3}>_5SetBtu+uwp*0c~JmetyUY`Ith=&L((bDkep1zx9e*iQ-S%bAt^vUo2Ky`0o z5i+hh(Quq5rM!Z3g#SstNmIL&OrUv8KD&VYHq{4D7$e_i5g2N+GGN=&=1i!eeZcOr z4G)+}M8k>iK`T0@F_43*^x}tTFBB5CoR=*a4(b=bE<8SbM;zaOMs|fav}8 zb5=uZP}i|znl$V!gL@kYIoXyGUI_WeLqk`BNa^4g92AiGulK9gg{+731Cm?LeM|+o zD##fO9~}4et`8;6{dwlHmVbW0209;@cR|&_9O|XDS-?SWoz*D{Q;((3Do4#& z_i!NQ5=joJUni+LbZ@&phJBNhZ#**Z<50-8V~-3$KQ9#k5vC(mDWX6$zEo|q{ zVOo*9l!7l@cP)W=|M`GBkRH+c;dt=KK}<(cZqz;mN+FDcb{y{cjum_lY@K>ZFO}LY zrhB`NiKo}=rR`D0&U7|4u@k|08>Kj8_{BXz;;LsIt5UA<$#cd#*1FiC`#85i<`s!N z>{MbR3SrqTn0VduGQBwlhw`z0ab2YY;LcBrbV}~#6P2HOnDMNe9(&R=qi480 z_A_S3Ka+BW5I{$zmYZi8010~usVkY1>5r)kstzx#(GoI7J$4a9mt*wQq}Hit>dby7 zQ65>b1FEKva1`Uw-&lw5%U@2_mY$e42`l zZ_lRiH_=~Z;_rxey6F2z????UUIr%f4} zL;Xx7+BDp6sxZ3FI(rh;u0eo?a$=g6{LL51Ig35|Yy;Y=z=Bx%Z@=tEiqJEEE`fFruL73E^;^7 zuRSd+mEMltS9pqLyZElaiIab=eNR8;N&-oZ^}j?N+UOx&zNdD+UtiFG{YsXN{`P6W z*@df;AN{x=!X4xSxe6~UqMS_>-zt+)*us?&GM$ZakAZW`L~-TpNL!5eWwcTB!Mj#_ zi>MbfF!I_dVLZ@w_RqP|78?P~UE?M&5 zc)(%w5+uSOHNZErd0=4?*3he2#`oPI!$uuV&6OwT_@;=9X1fH#b~RAG$_{I*!lv}# z2JTEs0ncQ};U%%jEMGQxI0pa~B3!NWqn43(@NRFSWxW?Ny-3VWy(?C@3Am9hMo2T* z1oD8T_jCQPL2d);Kjk9GM`p2CQ3aW-0w!O;3)g31uu4sL@=|n*;-ddd!FLA3)ilPg zfemQ_NAIahEo&*tXz^2w1qQcqN|Xe1C0Ue<++SAUmXw7V-L*+E9KKk#sp@0^Tox@( z0}+^^!xj)JIkz4Q(Nrq}1i-t9qwi)jW;ZWCH1-~3XpkviNIVdy!0^ovhqDKndD zUsnhHMS58`5eVjAWp^aBNbdtI32$2KZEEsGED3JF!j2@G<<9G3``JyhAn_YIhJcv? z#56BQXq!D>^AWpQuE^LsJ|Lg&VvSiJLR@yVDVQ67VGTXCba_K!_d1#r?TuvU+3US4 zE{YF@jqG;d(yjz8KddR2Gse;xf%R1b1k8r;%+8&Y$d}Rb@81SHtyGfw;fpUL=O@4! zfljT;RV9G<5L(rHR>{I|Wx}7~jxGPe;_23hMtDlh4vzjs9Ehm2^tusa4V9s86j-*p zchpzTT%zvxe@xBloxit^|2ivb4v>lZy~vZfG7yKI_rzSruBKHhJVsQ1kH4$~BpGrT zYA=TE&4M!NFt_^N?CVN|uFmzf$#6HN%|kOSa=ae@S7U0L1KV9X?tQvOwu}(AG?Cu9 zk&;{#yP$xwGPB(K z2AS;k5Tn@HVzDR75A96mXtH7t11?m}mkRrH)0$m=&U&53eGLuFsy~UcKHD;o?AOqo z!@o|4bu4W6c)UBo`8RL)^Mn;^#3~(po(=H=GyhhM+RX2o_Ih?#rAh7@dH|R=`66}a z0tX^LM&vK~*x=^bqBvN9J_%Ch+E_f6QgqK_y8M6R9ywwzeE{_TO@&mN&6nzMV22sH PIfEh6RId_o|K Date: Thu, 21 Apr 2016 17:35:55 -0400 Subject: [PATCH 095/129] [HeaderStackView] added missing swift code snippet to readme. Summary: Also added clarification to overview. ongoing work for https://github.com/google/material-components-ios/issues/318 Reviewers: featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D715 --- components/HeaderStackView/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/HeaderStackView/README.md b/components/HeaderStackView/README.md index a59ea08a472..3c843a0b603 100644 --- a/components/HeaderStackView/README.md +++ b/components/HeaderStackView/README.md @@ -55,7 +55,9 @@ $ pod install ## Overview -This view's sole purpose is to facilitate the relative layout of two horizontal bars. +This view's sole purpose is to facilitate the relative layout of two horizontal bars. The bottom bar +will bottom align and be of fixed height. The top bar will stretch to fill the remaining space if +there is any. The top bar is typically a navigation bar. The bottom bar, when provided, is typically a tab bar. @@ -94,6 +96,7 @@ MDCHeaderStackView *headerStackView = [[MDCHeaderStackView alloc] init]; #### Swift ~~~ swift +let headerStackView = MDCHeaderStackView() ~~~ From f15f6d5db6bcb0ddc6a750683daa649775f1e049 Mon Sep 17 00:00:00 2001 From: Chris Cox Date: Fri, 22 Apr 2016 11:56:33 -0400 Subject: [PATCH 096/129] [Collections] Merge Collections, CollectionCells, and CollectionLayoutAttributes components. Summary: Adds Collections, CollectionCells, and CollectionLayoutAttributes components Reviewers: ajsecord, featherless, randallli, #mdc_ios_owners, iangordon Reviewed By: randallli, #mdc_ios_owners, iangordon Subscribers: lpromero, featherless, randallli, iangordon, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D725 --- MaterialComponents.podspec | 35 + components/CollectionCells/.jazzy.yaml | 5 + components/CollectionCells/README.md | 39 + .../examples/CollectionCellsLayoutExample.m | 198 +++++ .../examples/CollectionCellsTextExample.m | 102 +++ .../ic_description.imageset/Contents.json | 23 + .../ic_description.png | Bin 0 -> 136 bytes .../ic_description_2x.png | Bin 0 -> 202 bytes .../ic_description_3x.png | Bin 0 -> 279 bytes .../CollectionCellsLayoutExample.h | 22 + .../supplemental/CollectionCellsTextExample.h | 22 + .../src/MDCCollectionViewCell+Ink.h | 30 + .../src/MDCCollectionViewCell+Ink.m | 40 + .../src/MDCCollectionViewCell.h | 90 +++ .../src/MDCCollectionViewCell.m | 340 ++++++++ .../src/MDCCollectionViewTextCell.h | 75 ++ .../src/MDCCollectionViewTextCell.m | 197 +++++ .../src/MaterialCollectionCells.h | 19 + .../Contents.json | 6 + .../mdc_cell_check.imageset/Contents.json | 23 + .../mdc_cell_check.png | Bin 0 -> 128 bytes .../mdc_cell_check@2x.png | Bin 0 -> 188 bytes .../mdc_cell_check@3x.png | Bin 0 -> 254 bytes .../Contents.json | 23 + .../mdc_cell_chevron_right.png | Bin 0 -> 109 bytes .../mdc_cell_chevron_right@2x.png | Bin 0 -> 138 bytes .../mdc_cell_chevron_right@3x.png | Bin 0 -> 200 bytes .../mdc_cell_delete.imageset/Contents.json | 23 + .../mdc_cell_delete.png | Bin 0 -> 178 bytes .../mdc_cell_delete@2x.png | Bin 0 -> 244 bytes .../mdc_cell_delete@3x.png | Bin 0 -> 321 bytes .../mdc_cell_info.imageset/Contents.json | 23 + .../mdc_cell_info.imageset/mdc_cell_info.png | Bin 0 -> 222 bytes .../mdc_cell_info@2x.png | Bin 0 -> 412 bytes .../mdc_cell_info@3x.png | Bin 0 -> 579 bytes .../mdc_cell_reorder.imageset/Contents.json | 23 + .../mdc_cell_reorder.png | Bin 0 -> 85 bytes .../mdc_cell_reorder@2x.png | Bin 0 -> 102 bytes .../mdc_cell_reorder@3x.png | Bin 0 -> 113 bytes .../mdc_cell_selected.imageset/Contents.json | 23 + .../mdc_cell_selected.png | Bin 0 -> 377 bytes .../mdc_cell_selected@2x.png | Bin 0 -> 613 bytes .../mdc_cell_selected@3x.png | Bin 0 -> 908 bytes .../Contents.json | 23 + .../mdc_cell_unselected.png | Bin 0 -> 333 bytes .../mdc_cell_unselected@2x.png | Bin 0 -> 668 bytes .../mdc_cell_unselected@3x.png | Bin 0 -> 1000 bytes .../src/private/MDCCollectionCellResources.h | 51 ++ .../src/private/MDCCollectionCellResources.m | 102 +++ .../CollectionLayoutAttributes/.jazzy.yaml | 5 + .../CollectionLayoutAttributes/README.md | 39 + .../src/MDCCollectionViewLayoutAttributes.h | 115 +++ .../src/MDCCollectionViewLayoutAttributes.m | 86 ++ .../src/MaterialCollectionLayoutAttributes.h | 17 + components/Collections/.jazzy.yaml | 5 + components/Collections/README.md | 261 ++++++ .../CollectionsAppearanceAnimationExample.m | 80 ++ .../CollectionsCellAccessoryExample.m | 123 +++ .../examples/CollectionsCellColorExample.m | 86 ++ .../CollectionsCellSeparatorExample.m | 87 ++ .../examples/CollectionsContainerExample.m | 99 +++ .../examples/CollectionsEditingExample.m | 132 +++ .../examples/CollectionsGridExample.m | 142 ++++ .../examples/CollectionsHeaderFooterExample.m | 133 ++++ .../examples/CollectionsInlayExample.m | 77 ++ .../examples/CollectionsSimpleDemo.m | 90 +++ .../CollectionsSwipeToDismissRowExample.m | 104 +++ .../CollectionsSwipeToDismissSectionExample.m | 104 +++ .../CollectionsAppearanceAnimationExample.h | 22 + .../CollectionsCellAccessoryExample.h | 22 + .../CollectionsCellColorExample.h | 22 + .../CollectionsCellSeparatorExample.h | 22 + .../CollectionsContainerExample.h | 20 + .../supplemental/CollectionsEditingExample.h | 22 + .../supplemental/CollectionsGridExample.h | 22 + .../CollectionsHeaderFooterExample.h | 22 + .../supplemental/CollectionsInlayExample.h | 22 + .../supplemental/CollectionsSimpleDemo.h | 22 + .../CollectionsSwipeToDismissRowExample.h | 22 + .../CollectionsSwipeToDismissSectionExample.h | 22 + .../src/MDCCollectionViewController.h | 68 ++ .../src/MDCCollectionViewController.m | 563 +++++++++++++ .../src/MDCCollectionViewEditingManager.h | 81 ++ .../src/MDCCollectionViewEditingManager.m | 734 +++++++++++++++++ .../MDCCollectionViewEditingManagerDelegate.h | 329 ++++++++ .../src/MDCCollectionViewFlowLayout.h | 28 + .../src/MDCCollectionViewFlowLayout.m | 751 ++++++++++++++++++ .../src/MDCCollectionViewStyleManager.h | 280 +++++++ .../src/MDCCollectionViewStyleManager.m | 734 +++++++++++++++++ .../MDCCollectionViewStyleManagerDelegate.h | 139 ++++ .../en.lproj/MaterialCollections.strings | 4 + .../Collections/src/MaterialCollections.h | 23 + .../private/MDCCollectionGridBackgroundView.h | 29 + .../private/MDCCollectionGridBackgroundView.m | 60 ++ .../src/private/MDCCollectionInfoBarView.h | 184 +++++ .../src/private/MDCCollectionInfoBarView.m | 247 ++++++ .../private/MDCCollectionStringResources.h | 36 + .../private/MDCCollectionStringResources.m | 76 ++ .../src/private/MaterialCollectionsStrings.h | 20 + .../MaterialCollectionsStrings_table.h | 27 + 100 files changed, 8017 insertions(+) create mode 100644 components/CollectionCells/.jazzy.yaml create mode 100644 components/CollectionCells/README.md create mode 100644 components/CollectionCells/examples/CollectionCellsLayoutExample.m create mode 100644 components/CollectionCells/examples/CollectionCellsTextExample.m create mode 100644 components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/Contents.json create mode 100644 components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/ic_description.png create mode 100644 components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/ic_description_2x.png create mode 100644 components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/ic_description_3x.png create mode 100644 components/CollectionCells/examples/supplemental/CollectionCellsLayoutExample.h create mode 100644 components/CollectionCells/examples/supplemental/CollectionCellsTextExample.h create mode 100644 components/CollectionCells/src/MDCCollectionViewCell+Ink.h create mode 100644 components/CollectionCells/src/MDCCollectionViewCell+Ink.m create mode 100644 components/CollectionCells/src/MDCCollectionViewCell.h create mode 100644 components/CollectionCells/src/MDCCollectionViewCell.m create mode 100644 components/CollectionCells/src/MDCCollectionViewTextCell.h create mode 100644 components/CollectionCells/src/MDCCollectionViewTextCell.m create mode 100644 components/CollectionCells/src/MaterialCollectionCells.h create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/Contents.json create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_check.imageset/Contents.json create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_check.imageset/mdc_cell_check.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_check.imageset/mdc_cell_check@2x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_check.imageset/mdc_cell_check@3x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_chevron_right.imageset/Contents.json create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_chevron_right.imageset/mdc_cell_chevron_right.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_chevron_right.imageset/mdc_cell_chevron_right@2x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_chevron_right.imageset/mdc_cell_chevron_right@3x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/Contents.json create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/mdc_cell_delete.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/mdc_cell_delete@2x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/mdc_cell_delete@3x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_info.imageset/Contents.json create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_info.imageset/mdc_cell_info.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_info.imageset/mdc_cell_info@2x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_info.imageset/mdc_cell_info@3x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/Contents.json create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/mdc_cell_reorder.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/mdc_cell_reorder@2x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/mdc_cell_reorder@3x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_selected.imageset/Contents.json create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_selected.imageset/mdc_cell_selected.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_selected.imageset/mdc_cell_selected@2x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_selected.imageset/mdc_cell_selected@3x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_unselected.imageset/Contents.json create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_unselected.imageset/mdc_cell_unselected.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_unselected.imageset/mdc_cell_unselected@2x.png create mode 100644 components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_unselected.imageset/mdc_cell_unselected@3x.png create mode 100644 components/CollectionCells/src/private/MDCCollectionCellResources.h create mode 100644 components/CollectionCells/src/private/MDCCollectionCellResources.m create mode 100644 components/CollectionLayoutAttributes/.jazzy.yaml create mode 100644 components/CollectionLayoutAttributes/README.md create mode 100644 components/CollectionLayoutAttributes/src/MDCCollectionViewLayoutAttributes.h create mode 100644 components/CollectionLayoutAttributes/src/MDCCollectionViewLayoutAttributes.m create mode 100644 components/CollectionLayoutAttributes/src/MaterialCollectionLayoutAttributes.h create mode 100644 components/Collections/.jazzy.yaml create mode 100644 components/Collections/README.md create mode 100644 components/Collections/examples/CollectionsAppearanceAnimationExample.m create mode 100644 components/Collections/examples/CollectionsCellAccessoryExample.m create mode 100644 components/Collections/examples/CollectionsCellColorExample.m create mode 100644 components/Collections/examples/CollectionsCellSeparatorExample.m create mode 100644 components/Collections/examples/CollectionsContainerExample.m create mode 100644 components/Collections/examples/CollectionsEditingExample.m create mode 100644 components/Collections/examples/CollectionsGridExample.m create mode 100644 components/Collections/examples/CollectionsHeaderFooterExample.m create mode 100644 components/Collections/examples/CollectionsInlayExample.m create mode 100644 components/Collections/examples/CollectionsSimpleDemo.m create mode 100644 components/Collections/examples/CollectionsSwipeToDismissRowExample.m create mode 100644 components/Collections/examples/CollectionsSwipeToDismissSectionExample.m create mode 100644 components/Collections/examples/supplemental/CollectionsAppearanceAnimationExample.h create mode 100644 components/Collections/examples/supplemental/CollectionsCellAccessoryExample.h create mode 100644 components/Collections/examples/supplemental/CollectionsCellColorExample.h create mode 100644 components/Collections/examples/supplemental/CollectionsCellSeparatorExample.h create mode 100644 components/Collections/examples/supplemental/CollectionsContainerExample.h create mode 100644 components/Collections/examples/supplemental/CollectionsEditingExample.h create mode 100644 components/Collections/examples/supplemental/CollectionsGridExample.h create mode 100644 components/Collections/examples/supplemental/CollectionsHeaderFooterExample.h create mode 100644 components/Collections/examples/supplemental/CollectionsInlayExample.h create mode 100644 components/Collections/examples/supplemental/CollectionsSimpleDemo.h create mode 100644 components/Collections/examples/supplemental/CollectionsSwipeToDismissRowExample.h create mode 100644 components/Collections/examples/supplemental/CollectionsSwipeToDismissSectionExample.h create mode 100644 components/Collections/src/MDCCollectionViewController.h create mode 100644 components/Collections/src/MDCCollectionViewController.m create mode 100644 components/Collections/src/MDCCollectionViewEditingManager.h create mode 100644 components/Collections/src/MDCCollectionViewEditingManager.m create mode 100644 components/Collections/src/MDCCollectionViewEditingManagerDelegate.h create mode 100644 components/Collections/src/MDCCollectionViewFlowLayout.h create mode 100644 components/Collections/src/MDCCollectionViewFlowLayout.m create mode 100644 components/Collections/src/MDCCollectionViewStyleManager.h create mode 100644 components/Collections/src/MDCCollectionViewStyleManager.m create mode 100644 components/Collections/src/MDCCollectionViewStyleManagerDelegate.h create mode 100644 components/Collections/src/MaterialCollections.bundle/Resources/en.lproj/MaterialCollections.strings create mode 100644 components/Collections/src/MaterialCollections.h create mode 100644 components/Collections/src/private/MDCCollectionGridBackgroundView.h create mode 100644 components/Collections/src/private/MDCCollectionGridBackgroundView.m create mode 100644 components/Collections/src/private/MDCCollectionInfoBarView.h create mode 100644 components/Collections/src/private/MDCCollectionInfoBarView.m create mode 100644 components/Collections/src/private/MDCCollectionStringResources.h create mode 100644 components/Collections/src/private/MDCCollectionStringResources.m create mode 100644 components/Collections/src/private/MaterialCollectionsStrings.h create mode 100644 components/Collections/src/private/MaterialCollectionsStrings_table.h diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec index ce511b6e2ac..8cd56f6a993 100644 --- a/MaterialComponents.podspec +++ b/MaterialComponents.podspec @@ -75,6 +75,41 @@ Pod::Spec.new do |s| ss.dependency "MaterialComponents/Buttons" end + s.subspec "CollectionCells" 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.resource_bundles = { + "Material#{ss.base_name}" => ["components/#{ss.base_name}/src/**/*.{png}"] + } + + ss.dependency "MaterialComponents/CollectionLayoutAttributes" + ss.dependency "MaterialComponents/Ink" + ss.dependency "MaterialComponents/Typography" + end + + s.subspec "CollectionLayoutAttributes" do |ss| + ss.public_header_files = "components/#{ss.base_name}/src/*.h" + ss.source_files = "components/#{ss.base_name}/src/*.{h,m}" + ss.header_mappings_dir = "components/#{ss.base_name}/src/*" + end + + s.subspec "Collections" 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.resource_bundles = { + "Material#{ss.base_name}" => ["components/#{ss.base_name}/src/Material#{ss.base_name}.bundle/*"] + } + + ss.dependency "MaterialComponents/CollectionCells" + ss.dependency "MaterialComponents/CollectionLayoutAttributes" + ss.dependency "MaterialComponents/Ink" + ss.dependency "MaterialComponents/ShadowElevations" + ss.dependency "MaterialComponents/ShadowLayer" + ss.dependency "MaterialComponents/Typography" + 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/components/CollectionCells/.jazzy.yaml b/components/CollectionCells/.jazzy.yaml new file mode 100644 index 00000000000..3e16f474889 --- /dev/null +++ b/components/CollectionCells/.jazzy.yaml @@ -0,0 +1,5 @@ +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: CollectionCells +umbrella_header: src/MaterialCollectionCells.h +objc: true +sdk: iphonesimulator diff --git a/components/CollectionCells/README.md b/components/CollectionCells/README.md new file mode 100644 index 00000000000..c9922922682 --- /dev/null +++ b/components/CollectionCells/README.md @@ -0,0 +1,39 @@ +--- +title: "Collection Cells" +layout: detail +section: components +excerpt: "" +--- +# Collection Cells + +## Installation + +### Requirements + +- Xcode 7.0 or higher. +- iOS SDK version 7.0 or higher. + +### Installation with CocoaPods + +To add this component to your Xcode project using CocoaPods, add the following to your `Podfile`: + +~~~ +pod 'MaterialComponents/CollectionCells' +~~~ + +Then, run the following command: + +~~~ bash +$ pod install +~~~ + +- - - + + +## Usage + +```objectivec + +// Example usage + +``` diff --git a/components/CollectionCells/examples/CollectionCellsLayoutExample.m b/components/CollectionCells/examples/CollectionCellsLayoutExample.m new file mode 100644 index 00000000000..5d07f40184d --- /dev/null +++ b/components/CollectionCells/examples/CollectionCellsLayoutExample.m @@ -0,0 +1,198 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionCellsLayoutExample.h" + +#import "MaterialSwitch.h" +#import "MaterialTypography.h" + +@interface SimpleModel : NSObject +@property(nonatomic, strong, nullable) NSString *text; +@property(nonatomic, strong, nullable) NSString *detailText; +@property(nonatomic) NSInteger textLines; +@property(nonatomic) NSInteger detailTextLines; ++ (instancetype)modelWithTextArray:(NSArray *)textArray textLineArray:(NSArray *)textLineArray; +@end + +@implementation SimpleModel +- (instancetype)initWithTextArray:(NSArray *)textArray textLineArray:(NSArray *)textLineArray { + self = [super init]; + if (self) { + _text = textArray[0]; + _detailText = textArray[1]; + _textLines = [textLineArray[0] integerValue]; + _detailTextLines = [textLineArray[1] integerValue]; + } + return self; +} + ++ (instancetype)modelWithTextArray:(NSArray *)textArray textLineArray:(NSArray *)textLineArray { + return [[self alloc] initWithTextArray:textArray textLineArray:textLineArray]; +} + +@end + +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; +static NSString *const kExampleText = + @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris tempus, enim non tincidunt " + "rhoncus, lacus sapien sagittis mi, id gravida risus turpis ut libero. "; +static NSString *const kExampleDetailText = + @"Pellentesque non quam ornare, porta urna sed, malesuada felis. Praesent at gravida felis, " + "non facilisis enim. Proin dapibus laoreet lorem, in viverra leo dapibus a."; + +#define RGBCOLOR(r, g, b) [UIColor colorWithRed:(r) / 255.0f green:(g) / 255.0f blue:(b) / 255.0f alpha:1] +#define HEXCOLOR(hex) RGBCOLOR((((hex) >> 16) & 0xFF), (((hex) >> 8) & 0xFF), ((hex)&0xFF)) + +@implementation CollectionCellsLayoutExample { + NSMutableArray *_content; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collection Cells", @"Cell Layout Example" ]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Register cell class. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Populate content. + _content = [NSMutableArray array]; + [_content addObject:[SimpleModel modelWithTextArray:@[ @"Show in Editing Mode", @"" ] + textLineArray:@[ @(1), @(0) ]]]; + [_content addObject:[SimpleModel modelWithTextArray:@[ @"Single line text", @"" ] + textLineArray:@[ @(1), @(0) ]]]; + [_content addObject:[SimpleModel modelWithTextArray:@[ kExampleText, @"" ] + textLineArray:@[ @(2), @(0) ]]]; + [_content addObject:[SimpleModel modelWithTextArray:@[ kExampleText, @"" ] + textLineArray:@[ @(3), @(0) ]]]; + [_content addObject:[SimpleModel modelWithTextArray:@[ @"", @"Detail text" ] + textLineArray:@[ @(0), @(1) ]]]; + [_content addObject:[SimpleModel modelWithTextArray:@[ kExampleText, kExampleDetailText ] + textLineArray:@[ @(1), @(1) ]]]; + [_content addObject:[SimpleModel modelWithTextArray:@[ kExampleText, kExampleDetailText ] + textLineArray:@[ @(1), @(2) ]]]; + [_content addObject:[SimpleModel modelWithTextArray:@[ kExampleText, @"Detail text" ] + textLineArray:@[ @(2), @(1) ]]]; + + // Customize collection view settings. + self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; +} + +#pragma mark - + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + SimpleModel *model = _content[indexPath.item]; + cell.textLabel.text = model.text; + cell.textLabel.numberOfLines = model.textLines; + cell.detailTextLabel.text = model.detailText; + cell.detailTextLabel.numberOfLines = model.detailTextLines; + + // Add accessory views. + if (indexPath.item == 0) { + // Add switch as accessory view. + MDCSwitch *editingSwitch = [[MDCSwitch alloc] initWithFrame:CGRectZero]; + [editingSwitch addTarget:self + action:@selector(didSwitch:) + forControlEvents:UIControlEventValueChanged]; + cell.accessoryView = editingSwitch; + } + + if (indexPath.item == 2) { + cell.accessoryType = MDCCollectionViewCellAccessoryCheckmark; + } else if (indexPath.item == 5 || indexPath.item == 6) { + cell.imageView.image = + [self imageWithSize:CGSizeMake(40, 40) + color:HEXCOLOR(0x80CBC4) + cornerRadius:20]; + } + + return cell; +} + +#pragma mark - + +- (CGFloat)collectionView:(UICollectionView *)collectionView + cellHeightAtIndexPath:(NSIndexPath *)indexPath { + SimpleModel *model = _content[indexPath.item]; + NSInteger numberOfLines = model.textLines + model.detailTextLines; + if (numberOfLines == 1) { + return MDCCellDefaultOneLineHeight; + } else if (numberOfLines == 2) { + return MDCCellDefaultTwoLineHeight; + } else if (numberOfLines == 3) { + return MDCCellDefaultThreeLineHeight; + } + return MDCCellDefaultOneLineHeight; +} + +#pragma mark - + +- (BOOL)collectionViewAllowsEditing:(UICollectionView *)collectionView { + return YES; +} + +- (BOOL)collectionViewAllowsReordering:(UICollectionView *)collectionView { + return YES; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + canEditItemAtIndexPath:(NSIndexPath *)indexPath { + return (indexPath.item != 0); +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + canMoveItemAtIndexPath:(NSIndexPath *)indexPath { + return (indexPath.item != 0); +} + +#pragma mark UIControlEvents + +- (void)didSwitch:(id)sender { + MDCSwitch *switchControl = sender; + [self.editingManager setEditing:switchControl.isOn animated:YES]; +} + +#pragma mark - Private helper methods + +- (UIImage *)imageWithSize:(CGSize)size color:(UIColor *)color cornerRadius:(CGFloat)cornerRadius { + // Create a colored image. + UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)]; + view.backgroundColor = color; + view.layer.cornerRadius = cornerRadius; + UIGraphicsBeginImageContextWithOptions(CGSizeMake(size.width, size.height), NO, 0); + [view.layer renderInContext:UIGraphicsGetCurrentContext()]; + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return image; +} + +@end diff --git a/components/CollectionCells/examples/CollectionCellsTextExample.m b/components/CollectionCells/examples/CollectionCellsTextExample.m new file mode 100644 index 00000000000..7e9fddd89ad --- /dev/null +++ b/components/CollectionCells/examples/CollectionCellsTextExample.m @@ -0,0 +1,102 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionCellsTextExample.h" + +#import "MaterialTypography.h" + +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; +static NSString *const kExampleDetailText = + @"Pellentesque non quam ornare, porta urna sed, malesuada felis. Praesent at gravida felis, " + "non facilisis enim. Proin dapibus laoreet lorem, in viverra leo dapibus a."; + +@implementation CollectionCellsTextExample { + NSMutableArray *_content; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collection Cells", @"Cell Text Example" ]; +} + ++ (BOOL)catalogIsPrimaryDemo { + return YES; +} + ++ (NSString *)catalogDescription { + return @"Material Collection Cells enables a native collection view cell to have Material " + "design layout and styling. It also provides editing and extensive customization " + "capabilities."; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Register cell class. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Populate content with array of text, details text, and number of lines. + _content = [NSMutableArray array]; + [_content addObject:@[ @"Single line text", + @"", + @(MDCCellDefaultOneLineHeight) ]]; + [_content addObject:@[ @"", + @"Single line detail text", + @(MDCCellDefaultOneLineHeight) ]]; + [_content addObject:@[ @"Two line text", + @"Here is the detail text", + @(MDCCellDefaultTwoLineHeight) ]]; + [_content addObject:@[ @"Two line text (truncated)", + kExampleDetailText, + @(MDCCellDefaultTwoLineHeight) ]]; + [_content addObject:@[ @"Three line text (wrapped)", + kExampleDetailText, + @(MDCCellDefaultThreeLineHeight) ]]; +} + +#pragma mark - + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _content[indexPath.item][0]; + cell.detailTextLabel.text = _content[indexPath.item][1]; + + if (indexPath.item == 4) { + cell.detailTextLabel.numberOfLines = 2; + } + return cell; +} + +#pragma mark - + +- (CGFloat)collectionView:(UICollectionView *)collectionView + cellHeightAtIndexPath:(NSIndexPath *)indexPath { + return [_content[indexPath.item][2] integerValue]; +} + +@end diff --git a/components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/Contents.json b/components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/Contents.json new file mode 100644 index 00000000000..8c45308acec --- /dev/null +++ b/components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images": [ + { + "filename": "ic_description.png", + "idiom": "universal", + "scale": "1x" + }, + { + "filename": "ic_description_2x.png", + "idiom": "universal", + "scale": "2x" + }, + { + "filename": "ic_description_3x.png", + "idiom": "universal", + "scale": "3x" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/ic_description.png b/components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/ic_description.png new file mode 100644 index 0000000000000000000000000000000000000000..a39e27f47b62605d1230f16cb90a2d9ae3d3406d GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1e@_?3kP61P=NC#gFmSj$ls?+Z zlk;SSTM4I+*tz$n8-J|$&TbO%B4F!njvqDYL1o;+>{UuUa|{@m9;op&)H6isH~eR3 jQa^Xuk->AV)N7_$FMdb1u}w_`8qMJ8>gTe~DWM4fcLOXk literal 0 HcmV?d00001 diff --git a/components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/ic_description_2x.png b/components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/ic_description_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8470bc03fe5f4358e2aa428c818b4d72107c74e2 GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0DiJmTwAr*{oFQ4T+R3PB;G5l=& zn@deC9p9!d`1m56(eliu<_U-Q*rwMB2$e-z6+Ao`ey207+0|$Ekx2jQBQa8D9pQ`z z6z{1z2%9b7l)liyn{kNEB$0X621Z|t18oy(UmckCp^;Bcp}~v!88hRRzpNY+&YS#q zY*=o(Uuk#LftEKX_S)JTFL1gerJt3WG@^^f BPE!B? literal 0 HcmV?d00001 diff --git a/components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/ic_description_3x.png b/components/CollectionCells/examples/resources/CollectionCellsExample.xcassets/ic_description.imageset/ic_description_3x.png new file mode 100644 index 0000000000000000000000000000000000000000..dd23b93ac24f290c0fd00a5776de8433fe05ccb9 GIT binary patch literal 279 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhaw?s>X6hEy=Vy?l`KP=G+oLrdEr zXNL{;Csjow<|ZgSOJba+!12nlm-We({(8=qpUWHHZ&)2#d``5~>h(g)9gBmqOj!k9 zE?MN=GQr>K2*b}M!J?0*x$g>Z{8qWdU(};=Nxo`_8l%E2mWB)=h9(aN4uy>h4M&<7 zgt(bJbReAO3L*ZeNr(PMTxi;&mf`rw@c@s}358}eX0Bt6Y0W&h>^kN;vPn+)|BX#D zCEy)UZb7-$^OXh3S{VU5>_o3<#w^a3N)HNj-K}zIN!V1=SEjF6=Y3k>=c@f|&Tr-j XldErCzMe4$`jo-b)z4*}Q$iB}?q6)( literal 0 HcmV?d00001 diff --git a/components/CollectionCells/examples/supplemental/CollectionCellsLayoutExample.h b/components/CollectionCells/examples/supplemental/CollectionCellsLayoutExample.h new file mode 100644 index 00000000000..7aa134b9f2a --- /dev/null +++ b/components/CollectionCells/examples/supplemental/CollectionCellsLayoutExample.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionCellsLayoutExample : MDCCollectionViewController +@end diff --git a/components/CollectionCells/examples/supplemental/CollectionCellsTextExample.h b/components/CollectionCells/examples/supplemental/CollectionCellsTextExample.h new file mode 100644 index 00000000000..74c321a87c1 --- /dev/null +++ b/components/CollectionCells/examples/supplemental/CollectionCellsTextExample.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionCellsTextExample : MDCCollectionViewController +@end diff --git a/components/CollectionCells/src/MDCCollectionViewCell+Ink.h b/components/CollectionCells/src/MDCCollectionViewCell+Ink.h new file mode 100644 index 00000000000..e7a206ff2d4 --- /dev/null +++ b/components/CollectionCells/src/MDCCollectionViewCell+Ink.h @@ -0,0 +1,30 @@ +/* + 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 "MDCCollectionViewCell.h" + +#import "MaterialInk.h" + +/** + This class adds an ink view as a cell subview. Must be used with an ink controller in order to + fully implement ink behavior. + */ +@interface MDCCollectionViewCell (Ink) + +/** View containing the ink effect. */ +@property(nonatomic, strong, nullable) MDCInkView *inkView; + +@end diff --git a/components/CollectionCells/src/MDCCollectionViewCell+Ink.m b/components/CollectionCells/src/MDCCollectionViewCell+Ink.m new file mode 100644 index 00000000000..69760ae6a65 --- /dev/null +++ b/components/CollectionCells/src/MDCCollectionViewCell+Ink.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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "MDCCollectionViewCell+Ink.h" + +#import + +@implementation MDCCollectionViewCell (Ink) + +@dynamic inkView; + +- (MDCInkView *)inkView { + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setInkView:(MDCInkView *)inkView { + if (!self.inkView) { + [self addSubview:inkView]; + } + objc_setAssociatedObject(self, @selector(inkView), inkView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end diff --git a/components/CollectionCells/src/MDCCollectionViewCell.h b/components/CollectionCells/src/MDCCollectionViewCell.h new file mode 100644 index 00000000000..6580b48b3c5 --- /dev/null +++ b/components/CollectionCells/src/MDCCollectionViewCell.h @@ -0,0 +1,90 @@ +/* + 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 + +/** The available cell accessory view types. Based on UITableViewCellAccessoryType. */ +typedef NS_ENUM(NSUInteger, MDCCollectionViewCellAccessoryType) { + /** Default value. No accessory view shown. */ + MDCCollectionViewCellAccessoryNone, + + /** A chevron accessory view. */ + MDCCollectionViewCellAccessoryDisclosureIndicator, + + /** A checkmark accessory view. */ + MDCCollectionViewCellAccessoryCheckmark, + + /** An info button accessory view. */ + MDCCollectionViewCellAccessoryDetailButton +}; + +/** + The MDCCollectionViewCell class provides an implementation of UICollectionViewCell that + supports Material design layout and styling. + */ +@interface MDCCollectionViewCell : UICollectionViewCell + +/** The accessory type for this cell. Default is MDCCollectionViewCellAccessoryNone. */ +@property(nonatomic) MDCCollectionViewCellAccessoryType accessoryType; + +/** If set, use custom view and ignore accessoryType. Defaults to nil. */ +@property(nonatomic, strong, nullable) UIView *accessoryView; + +/** + The accessory inset for this cell. Only left/right insets are valid as top/bottom insets will + be ignored. These insets are used for both accessories and editing mask controls. + Defaults to {0, 16.0f, 0, 16.0f}. + */ +@property(nonatomic) UIEdgeInsets accessoryInset; + +/** + Whether to hide the separator for this cell. If not set, the |shouldHideSeparators| property of + the collection view style manager will be used. Defaults to NO. + */ +@property(nonatomic) BOOL shouldHideSeparator; + +/** + The separator inset for this cell. Only left/right insets are valid as top/bottom insets will be + ignored. If this property is not changed, the |separatorInset| property of the collection view + style manager will be used instead. Defaults to UIEdgeInsetsZero. + */ +@property(nonatomic) UIEdgeInsets separatorInset; + +/** + A boolean value indicating whether a cell permits interactions with subviews of its content while + the cell is in editing mode. If NO, then tapping anywhere in the cell will select it instead of + permitting the tapped subview to receive the touch. Defaults to NO. + */ +@property(nonatomic) BOOL allowsCellInteractionsWhileEditing; + +/** + A boolean value indicating whether the a cell is being edited. Setting is not animated. + + When set, the cell will shows/hide editing controls with/without animation. + */ +@property(nonatomic, getter=isEditing) BOOL editing; + +/** + Set the editing state with optional animations. + + When set, the cell will shows/hide editing controls with/without animation. + + @param editing YES if editing; otherwise, NO. + @param animated YES the transition will be animated; otherwise, NO. + */ +- (void)setEditing:(BOOL)editing animated:(BOOL)animated; + +@end diff --git a/components/CollectionCells/src/MDCCollectionViewCell.m b/components/CollectionCells/src/MDCCollectionViewCell.m new file mode 100644 index 00000000000..d9018772da1 --- /dev/null +++ b/components/CollectionCells/src/MDCCollectionViewCell.m @@ -0,0 +1,340 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "MDCCollectionViewCell.h" + +#import "MaterialCollectionLayoutAttributes.h" +#import "private/MDCCollectionCellResources.h" + +static CGFloat kEditingControlAppearanceOffset = 16.0f; + +// Default accesory insets. +static const UIEdgeInsets kAccessoryInsetDefault = {0, 16.0f, 0, 16.0f}; + +@implementation MDCCollectionViewCell { + MDCCollectionViewLayoutAttributes *_attr; + BOOL _usesCellSeparatorHiddenOverride; + BOOL _usesCellSeparatorInsetOverride; + CAShapeLayer *_separatorLayer; + UIView *_separatorView; + UIImageView *_backgroundImageView; + UIImageView *_editingReorderImageView; + UIImageView *_editingSelectorImageView; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self commonMDCCollectionViewCellInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonMDCCollectionViewCellInit]; + } + return self; +} + +- (void)commonMDCCollectionViewCellInit { + // Separator defaults. + _separatorView = [[UIImageView alloc] initWithFrame:CGRectZero]; + [self addSubview:_separatorView]; + + // Accessory defaults. + _accessoryType = MDCCollectionViewCellAccessoryNone; + _accessoryInset = kAccessoryInsetDefault; +} + +#pragma mark - Layout + +- (void)prepareForReuse { + [super prepareForReuse]; + + // Reset properties. + _usesCellSeparatorHiddenOverride = NO; + _usesCellSeparatorInsetOverride = NO; + _separatorView.hidden = YES; + + [self drawSeparatorIfNeeded]; + [self updateInterfaceForEditing]; + + // Reset cells hidden during swipe deletion. + self.hidden = NO; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + self.contentView.frame = [self contentViewFrame]; + _accessoryView.frame = [self accessoryFrame]; + + // Animate editing controls. + [UIView animateWithDuration:0.3 + animations:^{ + _editingReorderImageView.alpha = + _attr.shouldShowReorderStateMask ? 1.0f : 0.0f; + _editingReorderImageView.transform = + _attr.shouldShowReorderStateMask + ? CGAffineTransformMakeTranslation(kEditingControlAppearanceOffset, 0) + : CGAffineTransformIdentity; + + _editingSelectorImageView.alpha = + _attr.shouldShowSelectorStateMask ? 1.0f : 0.0f; + _editingSelectorImageView.transform = + _attr.shouldShowSelectorStateMask + ? CGAffineTransformMakeTranslation(-kEditingControlAppearanceOffset, 0) + : CGAffineTransformIdentity; + }]; +} + +- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes { + [super applyLayoutAttributes:layoutAttributes]; + if ([layoutAttributes isKindOfClass:[MDCCollectionViewLayoutAttributes class]]) { + _attr = (MDCCollectionViewLayoutAttributes *)layoutAttributes; + + if (_attr.representedElementCategory == UICollectionElementCategoryCell) { + [self setEditing:_attr.editing]; + } + + // Create image view to hold cell background image with shadowing. + if (!_backgroundImageView) { + _backgroundImageView = [[UIImageView alloc] initWithFrame:self.bounds]; + self.backgroundView = _backgroundImageView; + } + _backgroundImageView.image = _attr.backgroundImage; + + // Draw separator if needed. + [self drawSeparatorIfNeeded]; + + self.contentView.frame = [self contentViewFrame]; + _accessoryView.frame = [self accessoryFrame]; + + // Animate cell on appearance settings. + [self updateAppearanceAnimation]; + } +} + +#pragma mark - Accessory Views + +- (void)setAccessoryType:(MDCCollectionViewCellAccessoryType)accessoryType { + _accessoryType = accessoryType; + + UIImageView *accessoryImageView = nil; + if (!_accessoryView && accessoryType != MDCCollectionViewCellAccessoryNone) { + // Add accessory view. + accessoryImageView = [[UIImageView alloc] initWithFrame:CGRectZero]; + _accessoryView = accessoryImageView; + _accessoryView.userInteractionEnabled = NO; + [self addSubview:_accessoryView]; + } + + switch (_accessoryType) { + case MDCCollectionViewCellAccessoryDisclosureIndicator: { + UIImage *image = MDCCollectionCellResources(imageForCellAccessoryChevronRight); + accessoryImageView.image = image; + break; + } + case MDCCollectionViewCellAccessoryCheckmark: { + UIImage *image = MDCCollectionCellResources(imageForCellAccessoryCheck); + accessoryImageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + break; + } + case MDCCollectionViewCellAccessoryDetailButton: { + UIImage *image = MDCCollectionCellResources(imageForCellAccessoryInfo); + accessoryImageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + break; + } + case MDCCollectionViewCellAccessoryNone: + accessoryImageView.image = nil; + break; + } + [_accessoryView sizeToFit]; +} + +- (void)setAccessoryView:(UIView *)accessoryView { + if (!_accessoryView) { + [self addSubview:accessoryView]; + } + _accessoryView = accessoryView; +} + +- (CGRect)accessoryFrame { + CGSize size = _accessoryView.frame.size; + return CGRectMake(CGRectGetWidth(self.bounds) - size.width - _accessoryInset.right, + (CGRectGetHeight(self.bounds) - size.height) / 2, + size.width, + size.height); +} + +#pragma mark - Separator + +- (void)setShouldHideSeparator:(BOOL)shouldHideSeparator { + _usesCellSeparatorHiddenOverride = YES; + _shouldHideSeparator = shouldHideSeparator; + [self drawSeparatorIfNeeded]; +} + +- (void)setSeparatorInset:(UIEdgeInsets)separatorInset { + _usesCellSeparatorInsetOverride = YES; + _separatorInset = separatorInset; + [self drawSeparatorIfNeeded]; +} + +- (void)drawSeparatorIfNeeded { + // Determine separator spec from attributes and cell overrides. Don't draw separator for bottom + // cell or in grid layout cells. Separators are added here as cell subviews instead of decoration + // views registered with the layout to overcome inability to animate decoration views in + // coordination with cell animations. + BOOL isHidden = + _usesCellSeparatorHiddenOverride ? _shouldHideSeparator : _attr.shouldHideSeparators; + UIEdgeInsets separatorInset = + _usesCellSeparatorInsetOverride ? _separatorInset : _attr.separatorInset; + BOOL isBottom = _attr.sectionOrdinalPosition & MDCCollectionViewOrdinalPositionVerticalBottom; + BOOL isGrid = _attr.isGridLayout; + + BOOL hideSeparator = isBottom || isHidden || isGrid; + if (hideSeparator != _separatorView.hidden) { + _separatorView.hidden = hideSeparator; + } + + if (!hideSeparator) { + CGFloat borderWidth = (1.0f / [[UIScreen mainScreen] scale]); + CGRect separatorFrame = + CGRectMake(borderWidth, + CGRectGetHeight(self.bounds) - _attr.separatorLineHeight, + CGRectGetWidth(self.bounds) - borderWidth, + _attr.separatorLineHeight); + _separatorView.frame = UIEdgeInsetsInsetRect(separatorFrame, separatorInset); + _separatorView.backgroundColor = _attr.separatorColor; + } +} + +#pragma mark - Editing + +- (void)setEditing:(BOOL)editing { + [self setEditing:editing animated:NO]; +} + +- (void)setEditing:(BOOL)editing animated:(BOOL)animated { + if (_editing == editing) { + return; + } + _editing = editing; + [self updateInterfaceForEditing]; +} + +- (void)updateInterfaceForEditing { + self.contentView.userInteractionEnabled = [self shouldEnableCellInteractions]; + + if (_editing) { + // Disable implicit animations when setting initial positioning of these subviews. + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + + // Create reorder editing controls. + if (_attr.shouldShowReorderStateMask && !_editingReorderImageView) { + UIImage *reorderImage = MDCCollectionCellResources(imageForCellEditingReorder); + _editingReorderImageView = [[UIImageView alloc] initWithImage:reorderImage]; + _editingReorderImageView.alpha = 0.0f; + _editingReorderImageView.frame = + CGRectMake(0, + (CGRectGetHeight(self.bounds) - reorderImage.size.height) / 2, + reorderImage.size.width, + reorderImage.size.height); + [self addSubview:_editingReorderImageView]; + } + + // Create selector editing controls. + if (_attr.shouldShowSelectorStateMask && !_editingSelectorImageView) { + UIImage *selectorImage = MDCCollectionCellResources(imageForCellEditingUnselected); + _editingSelectorImageView = [[UIImageView alloc] initWithImage:selectorImage]; + _editingSelectorImageView.alpha = 0.0f; + _editingSelectorImageView.frame = + CGRectMake(CGRectGetWidth(self.bounds) - selectorImage.size.width, + (CGRectGetHeight(self.bounds) - selectorImage.size.height) / 2, + selectorImage.size.width, + selectorImage.size.height); + _editingSelectorImageView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; + [self addSubview:_editingSelectorImageView]; + } + [CATransaction commit]; + } + + // Update accessory view. + _accessoryView.alpha = _attr.shouldShowSelectorStateMask ? 0.0f : 1.0f; + _accessoryInset.right = _attr.shouldShowSelectorStateMask + ? kAccessoryInsetDefault.right + kEditingControlAppearanceOffset + : kAccessoryInsetDefault.right; +} + +#pragma mark - Selecting + +- (void)setSelected:(BOOL)selected { + [super setSelected:selected]; + _editingSelectorImageView.image = selected + ? MDCCollectionCellResources(imageForCellEditingSelected) + : MDCCollectionCellResources(imageForCellEditingUnselected); +} + +#pragma mark - Cell Appearance Animation + +- (void)updateAppearanceAnimation { + if (_attr.animateCellsOnAppearanceDelay > 0) { + // Intially hide content view and separator. + self.contentView.alpha = 0; + _separatorView.alpha = 0; + + // Animate fade-in after delay. + if (!_attr.willAnimateCellsOnAppearance) { + [UIView animateWithDuration:_attr.animateCellsOnAppearanceDuration + delay:_attr.animateCellsOnAppearanceDelay + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + self.contentView.alpha = 1; + _separatorView.alpha = 1; + } + completion:nil]; + } + } +} + +#pragma mark - Private + +- (CGRect)contentViewFrame { + CGFloat leftPadding = + _attr.shouldShowReorderStateMask + ? CGRectGetWidth(_editingReorderImageView.bounds) + kEditingControlAppearanceOffset + : 0.f; + + CGFloat rightPadding = + _attr.shouldShowSelectorStateMask + ? CGRectGetWidth(_editingSelectorImageView.bounds) + kEditingControlAppearanceOffset + : 0.f; + return UIEdgeInsetsInsetRect(self.bounds, UIEdgeInsetsMake(0, leftPadding, 0, rightPadding)); +} + +- (BOOL)shouldEnableCellInteractions { + return !_editing || _allowsCellInteractionsWhileEditing; +} + +@end diff --git a/components/CollectionCells/src/MDCCollectionViewTextCell.h b/components/CollectionCells/src/MDCCollectionViewTextCell.h new file mode 100644 index 00000000000..8fbf1bfcb28 --- /dev/null +++ b/components/CollectionCells/src/MDCCollectionViewTextCell.h @@ -0,0 +1,75 @@ +/* + 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 "MDCCollectionViewCell.h" + +/** Default cell height for single line of text. Defaults to 48.0f. */ +extern const CGFloat MDCCellDefaultOneLineHeight; + +/** Default cell height for single line of text with avatar. Defaults to 56.0f. */ +extern const CGFloat MDCCellDefaultOneLineWithAvatarHeight; + +/** Default cell height for two lines of text. Defaults to 72.0f. */ +extern const CGFloat MDCCellDefaultTwoLineHeight; + +/** Default cell height for three lines of text. Defaults to 88.0f. */ +extern const CGFloat MDCCellDefaultThreeLineHeight; + +/** + The MDCCollectionViewTextCell class provides an implementation of UICollectionViewCell that + supports Material design layout and styling. It provides two labels for text as well as an + image view. The default layout specifications can be found at the following link. + + @see http://www.google.com/design/spec/components/lists.html#lists-specs + */ +@interface MDCCollectionViewTextCell : MDCCollectionViewCell + +/** + A text label. Typically this will be the first line of text in the cell. + + Default text label properties: + - text defaults to nil. + - font defaults to [MDCTypography subheadFont]. + - textColor defaults to [UIColor colorWithWhite:0 alpha:MDCTypography subheadFontOpacity]]. + - shadowColor defaults to nil. + - shadowOffset defaults to CGSizeZero. + - textAlignment defaults to NSTextAlignmentLeft. + - lineBreakMode defaults to NSLineBreakByTruncatingTail. + - numberOfLines defaults to 1. + */ +@property(nonatomic, readonly, strong, nullable) UILabel *textLabel; + +/** + A detail text label. Typically this will be the second line of text in the cell. + + Default detail text label properties: + - text defaults to nil. + - font defaults to [MDCTypography body1Font]. + - textColor defaults to [UIColor colorWithWhite:0 alpha:MDCTypography captionFontOpacity]]. + - shadowColor defaults to nil. + - shadowOffset defaults to CGSizeZero. + - textAlignment defaults to NSTextAlignmentLeft. + - lineBreakMode defaults to NSLineBreakByTruncatingTail. + - numberOfLines defaults to 1. + */ +@property(nonatomic, readonly, strong, nullable) UILabel *detailTextLabel; + +/** + An image view placed left side of cell. Default left padding is 16.0f. + */ +@property(nonatomic, readonly, strong, nullable) UIImageView *imageView; + +@end diff --git a/components/CollectionCells/src/MDCCollectionViewTextCell.m b/components/CollectionCells/src/MDCCollectionViewTextCell.m new file mode 100644 index 00000000000..d403e12c36d --- /dev/null +++ b/components/CollectionCells/src/MDCCollectionViewTextCell.m @@ -0,0 +1,197 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "MDCCollectionViewTextCell.h" + +#import "MaterialTypography.h" + +#import + +// Default cell heights. +const CGFloat MDCCellDefaultOneLineHeight = 48.0f; +const CGFloat MDCCellDefaultOneLineWithAvatarHeight = 56.0f; +const CGFloat MDCCellDefaultTwoLineHeight = 72.0f; +const CGFloat MDCCellDefaultThreeLineHeight = 88.0f; + +// Default cell fonts. +#define kCellDefaultTextFont [MDCTypography subheadFont] +#define kCellDefaultDetailTextFont [MDCTypography body1Font] + +// Default cell font opacity. +#define kCellDefaultTextOpacity [MDCTypography subheadFontOpacity] +#define kCellDefaultDetailTextFontOpacity [MDCTypography captionFontOpacity] + +// Cell padding top/bottom. +static const CGFloat kCellTwoLinePaddingTop = 20; +static const CGFloat kCellTwoLinePaddingBottom = 20; +static const CGFloat kCellThreeLinePaddingTop = 16; +static const CGFloat kCellThreeLinePaddingBottom = 20; +// Cell padding left/right. +static const CGFloat kCellTextNoImagePaddingLeft = 16; +static const CGFloat kCellTextNoImagePaddingRight = 16; +static const CGFloat kCellTextWithImagePaddingLeft = 72; +// Cell image view padding. +static const CGFloat kCellImagePaddingLeft = 16; + +@implementation MDCCollectionViewTextCell { + UIView *_contentWrapper; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self commonMDCCollectionViewTextCellInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonMDCCollectionViewTextCellInit]; + } + return self; +} + +- (void)commonMDCCollectionViewTextCellInit { + _contentWrapper = [[UIView alloc] initWithFrame:self.contentView.bounds]; + _contentWrapper.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _contentWrapper.clipsToBounds = YES; + [self.contentView addSubview:_contentWrapper]; + + // Text label. + _textLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + _textLabel.font = kCellDefaultTextFont; + _textLabel.textColor = [UIColor colorWithWhite:0 alpha:kCellDefaultTextOpacity]; + _textLabel.shadowColor = nil; + _textLabel.shadowOffset = CGSizeZero; + _textLabel.textAlignment = NSTextAlignmentLeft; + _textLabel.lineBreakMode = NSLineBreakByTruncatingTail; + [_contentWrapper addSubview:_textLabel]; + + // Detail text label. + _detailTextLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + _detailTextLabel.font = kCellDefaultDetailTextFont; + _detailTextLabel.textColor = [UIColor colorWithWhite:0 alpha:kCellDefaultDetailTextFontOpacity]; + _detailTextLabel.shadowColor = nil; + _detailTextLabel.shadowOffset = CGSizeZero; + _detailTextLabel.textAlignment = NSTextAlignmentLeft; + _detailTextLabel.lineBreakMode = NSLineBreakByTruncatingTail; + [_contentWrapper addSubview:_detailTextLabel]; + + // Image view. + _imageView = [[UIImageView alloc] initWithFrame:CGRectZero]; + [self.contentView addSubview:_imageView]; +} + +#pragma mark - Layout + +- (void)layoutSubviews { + [super layoutSubviews]; + [self applyMetrics]; +} + +- (CGRect)contentWrapperFrame { + CGFloat leftPadding = + _imageView.image ? kCellTextWithImagePaddingLeft : kCellTextNoImagePaddingLeft; + CGFloat rightPadding = kCellTextNoImagePaddingRight; + if (self.accessoryView && !self.isEditing) { + rightPadding += CGRectGetWidth(self.accessoryView.bounds) + kCellTextNoImagePaddingRight; + } + return UIEdgeInsetsInsetRect(self.contentView.bounds, + UIEdgeInsetsMake(0, leftPadding, 0, rightPadding)); +} + +- (void)applyMetrics { + // Set content wrapper frame. + _contentWrapper.frame = [self contentWrapperFrame]; + CGFloat boundsHeight = CGRectGetHeight(_contentWrapper.bounds); + + // Image layout. + [_imageView sizeToFit]; + CGRect imageFrame = _imageView.frame; + imageFrame.origin.x = kCellImagePaddingLeft; + imageFrame.origin.y = + (CGRectGetHeight(self.contentView.frame) / 2) - (imageFrame.size.height / 2); + + // Text layout. + CGRect textFrame = CGRectZero; + textFrame.size = [self frameSizeForLabel:_textLabel]; + CGRect detailFrame = CGRectZero; + detailFrame.size = [self frameSizeForLabel:_detailTextLabel]; + + if ([self numberOfAllVisibleTextLines] == 1) { + // Alignment for single line. + textFrame.origin.y = (boundsHeight / 2) - (textFrame.size.height / 2); + detailFrame.origin.y = (boundsHeight / 2) - (detailFrame.size.height / 2); + + } else if ([self numberOfAllVisibleTextLines] == 2) { + if (!CGRectIsEmpty(textFrame) && !CGRectIsEmpty(detailFrame)) { + // Alignment for two lines. + textFrame.origin.y = + kCellTwoLinePaddingTop + _textLabel.font.ascender - textFrame.size.height; + detailFrame.origin.y = + boundsHeight - kCellTwoLinePaddingBottom - + detailFrame.size.height - _detailTextLabel.font.descender; + } else { + // Since single wrapped label, just center. + textFrame.origin.y = (boundsHeight / 2) - (textFrame.size.height / 2); + detailFrame.origin.y = (boundsHeight / 2) - (detailFrame.size.height / 2); + } + + } else if ([self numberOfAllVisibleTextLines] == 3) { + if (!CGRectIsEmpty(textFrame) && !CGRectIsEmpty(detailFrame)) { + // Alignment for three lines. + textFrame.origin.y = + kCellThreeLinePaddingTop + _textLabel.font.ascender - _textLabel.font.lineHeight; + detailFrame.origin.y = + boundsHeight - kCellThreeLinePaddingBottom - + detailFrame.size.height - _detailTextLabel.font.descender; + imageFrame.origin.y = kCellThreeLinePaddingTop; + } else { + // Since single wrapped label, just center. + textFrame.origin.y = (boundsHeight / 2) - (textFrame.size.height / 2); + detailFrame.origin.y = (boundsHeight / 2) - (detailFrame.size.height / 2); + } + } + _textLabel.frame = textFrame; + _detailTextLabel.frame = detailFrame; + _imageView.frame = imageFrame; +} + +- (NSInteger)numberOfAllVisibleTextLines { + return [self numberOfLinesForLabel:_textLabel] + [self numberOfLinesForLabel:_detailTextLabel]; +} + +- (NSInteger)numberOfLinesForLabel:(UILabel *)label { + CGSize size = [self frameSizeForLabel:label]; + return (NSInteger)floor(size.height / label.font.lineHeight); +} + +- (CGSize)frameSizeForLabel:(UILabel *)label { + CGFloat width = MIN(CGRectGetWidth(_contentWrapper.bounds), + [label.text sizeWithAttributes:@{NSFontAttributeName : label.font}].width); + CGFloat height = [label textRectForBounds:_contentWrapper.bounds + limitedToNumberOfLines:label.numberOfLines] + .size.height; + return CGSizeMake(width, height); +} + +@end diff --git a/components/CollectionCells/src/MaterialCollectionCells.h b/components/CollectionCells/src/MaterialCollectionCells.h new file mode 100644 index 00000000000..bc72418e497 --- /dev/null +++ b/components/CollectionCells/src/MaterialCollectionCells.h @@ -0,0 +1,19 @@ +/* + 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 "MDCCollectionViewCell+Ink.h" +#import "MDCCollectionViewCell.h" +#import "MDCCollectionViewTextCell.h" diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/Contents.json b/components/CollectionCells/src/MaterialCollectionCells.xcassets/Contents.json new file mode 100644 index 00000000000..da4a164c918 --- /dev/null +++ b/components/CollectionCells/src/MaterialCollectionCells.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_check.imageset/Contents.json b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_check.imageset/Contents.json new file mode 100644 index 00000000000..370e9ec2629 --- /dev/null +++ b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_check.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images": [ + { + "filename": "mdc_cell_check.png", + "idiom": "universal", + "scale": "1x" + }, + { + "filename": "mdc_cell_check_2x.png", + "idiom": "universal", + "scale": "2x" + }, + { + "filename": "mdc_cell_check_3x.png", + "idiom": "universal", + "scale": "3x" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_check.imageset/mdc_cell_check.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_check.imageset/mdc_cell_check.png new file mode 100644 index 0000000000000000000000000000000000000000..1c14c9c44592e95983dec13ca705ab99a6c54f21 GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1cTX3`kP61+1H5jHdYPvdOa3!3 znXv4Sz4PG@!RHQbn4&mG+#ttr57iP(&yMOzaz berIh-Q-qr4!cP|^dng9R*07*qoM6N<$ Eg0vTEBme*a literal 0 HcmV?d00001 diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_chevron_right.imageset/Contents.json b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_chevron_right.imageset/Contents.json new file mode 100644 index 00000000000..787f9812ceb --- /dev/null +++ b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_chevron_right.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images": [ + { + "filename": "mdc_cell_chevron_right.png", + "idiom": "universal", + "scale": "1x" + }, + { + "filename": "mdc_cell_chevron_right_2x.png", + "idiom": "universal", + "scale": "2x" + }, + { + "filename": "mdc_cell_chevron_right_3x.png", + "idiom": "universal", + "scale": "3x" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_chevron_right.imageset/mdc_cell_chevron_right.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_chevron_right.imageset/mdc_cell_chevron_right.png new file mode 100644 index 0000000000000000000000000000000000000000..c11a2a5e2d3172870bcf7848bb8ffc90a7ad010d GIT binary patch literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*16Hgb%kP60R1>v41w>Gw*DX)$G zB>wQ9HI3uaR+mZZRJ%%aFAC^R$&jdcYSOcVO<@T~4->ouBorxH~AC;R6%DZ24h(Pqs3k;S8RxelF{r5}E*KA2A~U literal 0 HcmV?d00001 diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_chevron_right.imageset/mdc_cell_chevron_right@3x.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_chevron_right.imageset/mdc_cell_chevron_right@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f97c51b8ed7f4716341898f25e522f49336c7ec7 GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhaw`aNA7Ln;{G-Z;z2WFX-3aHRt4 zkI50QR)zi&f2%TS@^&4LLk;g&Kagdzb^)qBaK6RKtxrnI&U5+9$CFFuY1(Pg~FoNpT>L!PC{xWt~$(69CpdO(*~W literal 0 HcmV?d00001 diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/Contents.json b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/Contents.json new file mode 100644 index 00000000000..720abaa989a --- /dev/null +++ b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "mdc_cell_delete.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "mdc_cell_delete@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "mdc_cell_delete@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/mdc_cell_delete.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/mdc_cell_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..0dc34999bf12534f48ee414421084686dbd39c8c GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XLOfj@Ln>}1B}lNcFeodszJC(R({QCuLCj(DFNPz1PWmdKI;Vst02I(T^#A|> literal 0 HcmV?d00001 diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/mdc_cell_delete@2x.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/mdc_cell_delete@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..330b1cc0e903e020a2662e8aa3bfe4d6b36a2c23 GIT binary patch literal 244 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1G(>z@qLn>~)x$4N5ccn>J z*)ubA{BQVkDr9f3SH3)-AFVdQ&MBb@0L)lV=l}o! literal 0 HcmV?d00001 diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/mdc_cell_delete@3x.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_delete.imageset/mdc_cell_delete@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f0e4f05c5637884bf223842dc57ddb7e23a41f5c GIT binary patch literal 321 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X9(%ethE&{obNe7~lYvA-U;)!%rZ^b~w*(XOLKd?ER`v%9 z+%xhP*giNbJmb^i_uf)@4)Gqrd~3BlC#k%2EbP0l+XkKSax#; literal 0 HcmV?d00001 diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_info.imageset/Contents.json b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_info.imageset/Contents.json new file mode 100644 index 00000000000..e2bf98ee712 --- /dev/null +++ b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_info.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images": [ + { + "filename": "mdc_cell_info.png", + "idiom": "universal", + "scale": "1x" + }, + { + "filename": "mdc_cell_info_2x.png", + "idiom": "universal", + "scale": "2x" + }, + { + "filename": "mdc_cell_info_3x.png", + "idiom": "universal", + "scale": "3x" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_info.imageset/mdc_cell_info.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_info.imageset/mdc_cell_info.png new file mode 100644 index 0000000000000000000000000000000000000000..5ef3dc0809e2030ac573605e4d50a1b15be283f3 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_+irJgR1Ar*{!&pBo_7znsMbQe4M zmu2aqie+Lg_Z%-OYdThQ-1WK3yF`&wq~^cgljetCWtg?+{hpb=@c6_R&GITYJe^Gr zn+wS+w{7@Qu3Fze@l2LUOog?>hoS^g9uF~&xlN*)H`W{wZ;U+{TXN|u|AX!W%Mvu1 zCO7Tl*uo`~E>@u<#XBj|^}>ul|6C>19PJEUR$ER;nAhbpt#9Vs*Ef&3bU%Gs$9S4s WW8+=f3r;`>GkCiCxvXk7>D5($61se024W16Y9PdLry5vdn5qWG7(8}rf#0;i^i3`Bz)3@LmB1SV3Ir;F zXA1R55y$YDX|Txx7F9T)MSz5C z%me$p#<(@Ec}8Mx!WA`)S?3W+u@6bG_Q+CN{iWZ6iFpgn8im7?Un+GeL)QF6AA4ghWDNXmU%KBG@?RmKM)K1 zMojBSqWw=2twt=5)`ISdVGXesBvgrXZ8hkU2o;{K22Jn?b%0n667~qyLM-o&5Jdv( zL4TP?X$x_%4U`&Af{svXIte;OspBN5jZz=6{@DvXl!i`%1}Ftig2q!9G`&wY{XgoC zlb|+AO(#JoC^eh}9ip`9BT! zt3hvwp=B+|Cze%9K`*Qj(>Y>cXT-L|$au~Wj|A$*gSJWNv(cQ7Bv#Y(JK#w*j9P*Yqa=F};`Uf%+Z6V(b RSUCUy002ovPDHLkV1jt6`k4R# literal 0 HcmV?d00001 diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/Contents.json b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/Contents.json new file mode 100644 index 00000000000..e106cefc973 --- /dev/null +++ b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "mdc_cell_reorder.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "mdc_cell_reorder@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "mdc_cell_reorder@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/mdc_cell_reorder.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/mdc_cell_reorder.png new file mode 100644 index 0000000000000000000000000000000000000000..9556cbb1eae02ea0be0bddfdc6875aebf334ee4a GIT binary patch literal 85 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*18BZ6-5R22v2@*ROe6dgTnAsL+ i-}5W+#g9Y-Mur(LE;{-gUAP~pj=|H_&t;ucLK6UOlo^-+ literal 0 HcmV?d00001 diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/mdc_cell_reorder@2x.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/mdc_cell_reorder@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3ffe68f1c70b1ffd92c9ad17da087ad586cb2d05 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}tT~8Oskch)?FB=LnFmNy%`Ye{W zDbOkKWc_o6b%7 literal 0 HcmV?d00001 diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/mdc_cell_reorder@3x.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_reorder.imageset/mdc_cell_reorder@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..146b67cee2390384a3ca0753c5e0212dd8906b89 GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xf3?%cF6tJzX3_Dj1U`mMr*Z&+$ijuB&>r`&-#L3=FpSW;^z`D%JuOF?hQAxvX;sC}4)WiXB z1K0#6KqpZrkWF9%9l%6gz_@|_BZZWD%H3&VXwqDAz-#-jSCksM1)m!3j!lmw<#RJ5?a8$o%*>_l>I3$yZTh@qeR)&MtL3B15 zRcR#)v79bi(FC+?;r5~hUrbP|$!eOW2hVv_YY+IcuT7IT{ck&y{`|xF^)GYy_7q?M XD|Ljjl#MIE00000NkvXXu0mjf&1IiN literal 0 HcmV?d00001 diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_selected.imageset/mdc_cell_selected@2x.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_selected.imageset/mdc_cell_selected@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..007abb509e276be50d1899b74d2603203706c058 GIT binary patch literal 613 zcmV-r0-F7aP)Y2RI&3Yp=KlNhE2#j0Gw2<(wdE-)Xh#rfq6#4R9=OS%M!*B}A_K8LQBtZ!ez?hy(i7jFUppCkM{QgXSVMCUAkSNYz-plITsEFS zK2Ufrn~gw)AhF@K1!Qr;Tulune88)p1DJti@LSB)oqoMlN%Wa@iYdk(zXA*Zw%;Yw@dW!-00000NkvXXu0mjfG8F_2 literal 0 HcmV?d00001 diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_selected.imageset/mdc_cell_selected@3x.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_selected.imageset/mdc_cell_selected@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..81375ad1d0869c216e67196d3f4593d87972063f GIT binary patch literal 908 zcmV;719SX|P)h%95(0>mU64<$3_!2-*#_K7KZ753PjutUrJL^ZSq-Ab>J~?iQNvFNlvj zLfb-n1Xx?KfB=dHv4A#1PiaHivmT(W0M=0qAb_)k?kj`0_;}SXfV7Mn5P;F~}ja1{Rb4#yIQ2f<+Upf=D3k!5I|?WW*OWiUXPvUeTEnkKPn zOeqbDgyFD2DiVi7hEz0wqhrpKAUJO!4Si!$lPtCo2uIZVpS}fP(0{1VK`1U+5k-w zhhw#|XIYS5Bo5~ktkG6)4;q~T9ILG+KsRRs$7rhwQ2$Ke7;WX8VryInNrPjw^%Ij3 z)%r&dm~&y<1st~3ycG_X_gtP798N7DT%dZxVT2R~94-&~Py|TPz^Mh~aS-4R2h0U2 zDmYvo#04ojIGj_A3MooBHbDE1AjJS@-vm(EfhWb{Ic1YV@tH8))JN&Rf`rASifCHZ z+FDq)JD#`-NFN1=Im5d5_Jw7juZp%ONI7{YF%fosqdG(0pU60u`o#0LGse~i}@--TY!W>B41`9?MdSg>!5dt z5YB0A)Jwo@HxBUx`yvuJI+cIpcu>BQ*(IQNYqxx<*8(YD3-^GP|5(7YG|;h1#9vcn iWo2b$Wy#e)0R{j7eWF&R4rAN^0000V%%Vlz{_ZgY1@EBfQKWbPC8$@hwwpK0+BRe}%p|M# z^1bAJe`3r(FCrEku#}Wo_RNWV;S=ttv2Tps!5uaTaU`ZoNJx)_3?WiFZNUcd;IqL7 z#N>#yZALtJbG|(!M~v#J-cj@Rm-7U1C9H@A;^-+xK!%uA!~qd|%e_SGDrga1ub_{( zRIo%Z4&4nE#V-UF(enxhh;s$^h{P*cBeoTsBQgR{Sx<=RtHYbGu&fZamL+3E&d?eD z&J*#`q*FvrTsKC*iYMaRHQp&gWGv|u5iwxR2_e25<86#FjJUQwkZqZ%NkV4qxKL1V fX3Nyy(7#wezq-NowbLgm00000NkvXXu0mjf4Lp!R literal 0 HcmV?d00001 diff --git a/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_unselected.imageset/mdc_cell_unselected@2x.png b/components/CollectionCells/src/MaterialCollectionCells.xcassets/mdc_cell_unselected.imageset/mdc_cell_unselected@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..44f17d3960656f19650fc3358eb55ae7b4244ee1 GIT binary patch literal 668 zcmV;N0%QG&P)I3m1fjJ^=Xvd(J&)WKM5V-#0Un<6Pc^3V9q1A~e zsa++?yR?e>jypr#w*wvbbTIL&-o3TFM5MAYCkiLraDvkUesgatD}ymEL}khet_ZXpX!`Do z%CZwG3zQ3FkQbGb6RHZ731pBJm5LK;2$T$DkQSA?6H+}83uF*iz3>+dREkogO3}!R zJo0N=5NIcm!JeornGe^rdb65F#dl+(33DpooNt49Q8;!PcA*e21U@9{OFqUYQCM{+ zOz2*~mw~Eci@PRDQGc#H8e0A_Es5SRlTy0fYNUsfALmFS&$vB^IHNy54(u%$=e>@5 z;zcfMYK=+Dr%RrcIp+C6>szj>P)*)8c-uPaIlN-QcBuY$bjuUIqdhvFX)5mJ;|LQqL56;VlE1PW_O zA|hT_gb0yhx=45J8lJACch2rPJLh9!gPpm3-sgSJnR!2ECK4%+@+glI9mHs(j}hir zCPR)K8J3x2gg#oS`Ik}-(9H}vESH((16SB@Ud$OL`0Xl1&Mn^3Y);HsK59#mbB5F5 zi#f=1Ut)IVD0N}Vxx~*vax5%1=?F(mHNy@rRv0BkEA`Y8B}y&zw9&~Js}2?eM1#uN zN4fy$AzgR=eVG}2dfD=l17AH1$tvpfr`QOd6S zM>8X!cZ~I(gTl+S`})clIA%=HapC2c%R0~|+b@Ghg$mY$rxs^mpX`E-o?c{!-s=n- z5wMg|L0!VrBWKv0fVCPG)Gj>DI>VL)tlp@g2H|PR8I}>STBCvzGT%AFWY9;A3aXX` z{67*@a!Xcnn_7l_TPcM7i0Rg&U}AizS31Uw3VIP5a770FK9@3NxMU@2qso>@>lvL^zTtC-mks;)Dwwx9rE55< zNO~30bxalG3h!fm(>gX4)8SAAJy19^o^E65uwB3n2fWg&vdOWy5ahUPVe#76W$w=2 z8Mm?B_D&LI(nj)yBR(VRtBvU$yZl|iE!c>1-1L?ziPNtHx`oBO?{rHtPm;w7-Fh{S z7}p9s!^{^g7DfKjRDmay4F)+*g-gG4f+3}Z{#~q~#fT^a4v$T1Jg19x8c0x0H3=GM zr;8U#`z7ZsUwfxu64(s + +/** + Shorthand for returning a resource of any type from MDCCollectionCellResources's singleton. + */ +#define MDCCollectionCellResources(sel) [[MDCCollectionCellResources sharedInstance] sel] + +/** Resources that are used for collection views. */ +@interface MDCCollectionCellResources : NSObject + +/** Returns the shared resources singleton instance. */ ++ (nonnull instancetype)sharedInstance; + +/** Returns cell accessory check image. */ +- (nonnull UIImage *)imageForCellAccessoryCheck; + +/** Returns cell accessory chevron right image. */ +- (nonnull UIImage *)imageForCellAccessoryChevronRight; + +/** Returns cell accessory info image. */ +- (nonnull UIImage *)imageForCellAccessoryInfo; + +/** Returns cell editing delete image. */ +- (nonnull UIImage *)imageForCellEditingDelete; + +/** Returns cell editing reorder image. */ +- (nonnull UIImage *)imageForCellEditingReorder; + +/** Returns cell editing selected image. */ +- (nonnull UIImage *)imageForCellEditingSelected; + +/** Returns cell editing unselected image. */ +- (nonnull UIImage *)imageForCellEditingUnselected; + +@end diff --git a/components/CollectionCells/src/private/MDCCollectionCellResources.m b/components/CollectionCells/src/private/MDCCollectionCellResources.m new file mode 100644 index 00000000000..f222264b929 --- /dev/null +++ b/components/CollectionCells/src/private/MDCCollectionCellResources.m @@ -0,0 +1,102 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "MDCCollectionCellResources.h" + +// The Bundle for string and icon resources. +static NSString *const kBundleName = @"MaterialCollectionCells.bundle"; + +static NSString *const kCellCheckImageName = @"mdc_cell_check"; +static NSString *const kCellChevronRightImageName = @"mdc_cell_chevron_right"; +static NSString *const kCellInfoImageName = @"mdc_cell_info"; +static NSString *const kCellDeleteImageName = @"mdc_cell_delete"; +static NSString *const kCellReorderImageName = @"mdc_cell_reorder"; +static NSString *const kCellSelectedImageName = @"mdc_cell_selected"; +static NSString *const kCellUnselectedImageName = @"mdc_cell_unselected"; + +@implementation MDCCollectionCellResources + ++ (instancetype)sharedInstance { + static MDCCollectionCellResources *sharedInstance; + @synchronized(self) { + if (sharedInstance == nil) { + sharedInstance = [[MDCCollectionCellResources alloc] init]; + } + } + return sharedInstance; +} + +#pragma mark - Resource bundle + ++ (NSBundle *)bundle { + static NSBundle *bundle = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + bundle = [NSBundle bundleWithPath:[self bundlePathWithName:kBundleName]]; + }); + + return bundle; +} + ++ (NSString *)bundlePathWithName:(NSString *)bundleName { + // In iOS 8+, we could be included by way of a dynamic framework, and our resource bundles may + // not be in the main .app bundle, but rather in a nested framework, so figure out where we live + // and use that as the search location. + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *resourcePath = [(nil == bundle ? [NSBundle mainBundle] : bundle)resourcePath]; + return [resourcePath stringByAppendingPathComponent:bundleName]; +} + +- (UIImage *)loadImageWithName:(NSString *)imageName { + NSBundle *bundle = [[self class] bundle]; + return [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil]; +} + +#pragma mark - Images + +- (UIImage *)imageForCellAccessoryCheck { + return [self loadImageWithName:kCellCheckImageName]; +} + +- (UIImage *)imageForCellAccessoryChevronRight { + return [self loadImageWithName:kCellChevronRightImageName]; +} + +- (UIImage *)imageForCellAccessoryInfo { + return [self loadImageWithName:kCellInfoImageName]; +} + +- (UIImage *)imageForCellEditingDelete { + return [self loadImageWithName:kCellDeleteImageName]; +} + +- (UIImage *)imageForCellEditingReorder { + return [self loadImageWithName:kCellReorderImageName]; +} + +- (UIImage *)imageForCellEditingSelected { + return [self loadImageWithName:kCellSelectedImageName]; +} + +- (UIImage *)imageForCellEditingUnselected { + return [self loadImageWithName:kCellUnselectedImageName]; +} + +@end diff --git a/components/CollectionLayoutAttributes/.jazzy.yaml b/components/CollectionLayoutAttributes/.jazzy.yaml new file mode 100644 index 00000000000..207bdfcb143 --- /dev/null +++ b/components/CollectionLayoutAttributes/.jazzy.yaml @@ -0,0 +1,5 @@ +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: CollectionLayoutAttributes +umbrella_header: src/MaterialCollectionLayoutAttributes.h +objc: true +sdk: iphonesimulator diff --git a/components/CollectionLayoutAttributes/README.md b/components/CollectionLayoutAttributes/README.md new file mode 100644 index 00000000000..92dca5bea02 --- /dev/null +++ b/components/CollectionLayoutAttributes/README.md @@ -0,0 +1,39 @@ +--- +title: "Collection Layout Attributes" +layout: detail +section: components +excerpt: "" +--- +# Collection Layout Attributes + +## Installation + +### Requirements + +- Xcode 7.0 or higher. +- iOS SDK version 7.0 or higher. + +### Installation with CocoaPods + +To add this component to your Xcode project using CocoaPods, add the following to your `Podfile`: + +~~~ +pod 'MaterialComponents/CollectionViewLayouts' +~~~ + +Then, run the following command: + +~~~ bash +$ pod install +~~~ + +- - - + + +## Usage + +```objectivec + +// Example usage + +``` diff --git a/components/CollectionLayoutAttributes/src/MDCCollectionViewLayoutAttributes.h b/components/CollectionLayoutAttributes/src/MDCCollectionViewLayoutAttributes.h new file mode 100644 index 00000000000..156bb07b93d --- /dev/null +++ b/components/CollectionLayoutAttributes/src/MDCCollectionViewLayoutAttributes.h @@ -0,0 +1,115 @@ +/* + 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 + +/** Types of cell ordinal positions available within a collectionView. */ +typedef NS_OPTIONS(NSUInteger, MDCCollectionViewOrdinalPosition) { + /** Cell visually has top edge within section. */ + MDCCollectionViewOrdinalPositionVerticalTop = 1 << 0, + + /** Cell visually has no top/bottom edges within section. */ + MDCCollectionViewOrdinalPositionVerticalCenter = 1 << 1, + + /** Cell visually has bottom edge within section. */ + MDCCollectionViewOrdinalPositionVerticalBottom = 1 << 2, + + /** + Cell visually has both bottom/top edges within section. Typically for a single or inlaid cell. + */ + MDCCollectionViewOrdinalPositionVerticalTopBottom = + (MDCCollectionViewOrdinalPositionVerticalTop | + MDCCollectionViewOrdinalPositionVerticalBottom), + + /** Cell visually has left edge within section. */ + MDCCollectionViewOrdinalPositionHorizontalLeft = 1 << 10, + + /** Cell visually has no left/right edges within section. */ + MDCCollectionViewOrdinalPositionHorizontalCenter = 1 << 11, + + /** Cell visually has right edge within section. */ + MDCCollectionViewOrdinalPositionHorizontalRight = 1 << 12 +}; + +/** + The MDCCollectionViewLayoutAttributes class allows passing layout attributes to the cells and + supplementary views. + */ +@interface MDCCollectionViewLayoutAttributes : UICollectionViewLayoutAttributes + +#pragma mark - Cell Styling + +/** A boolean value indicating whether the collectionView is being edited. Defaults to NO. */ +@property(nonatomic, getter=isEditing) BOOL editing; + +/** + A boolean value indicating whether the collectionView cell should be displayed with reorder + state mask. Defaults to NO. + */ +@property(nonatomic, assign) BOOL shouldShowReorderStateMask; + +/** + A boolean value indicating whether the collectionView cell should be displayed with selector + state mask. Defaults to NO. + */ +@property(nonatomic, assign) BOOL shouldShowSelectorStateMask; + +/** + A Boolean value indicating whether the collection view cell should allow the grid background + decoration view to be drawn at the specified index. + */ +@property(nonatomic, assign) BOOL shouldShowGridBackground; + +/** The image for use as the cells background image. */ +@property(nonatomic, strong, nullable) UIImage *backgroundImage; + +/** + A boolean value indicating whether the collectionView cell style is set to + MDCCollectionViewCellLayoutTypeGrid. + */ +@property(nonatomic, assign) BOOL isGridLayout; + +/** The ordinal position within the collectionView section. */ +@property(nonatomic, assign) MDCCollectionViewOrdinalPosition sectionOrdinalPosition; + +#pragma mark - Cell Separator + +/** Separator color. Defaults to #E0E0E0. */ +@property(nonatomic, strong, nullable) UIColor *separatorColor; + +/** Separator inset. Defaults to UIEdgeInsetsZero. */ +@property(nonatomic) UIEdgeInsets separatorInset; + +/** Separator line height. Defaults to 1.0f */ +@property(nonatomic) CGFloat separatorLineHeight; + +/** Whether to hide the cell separators. Defaults to NO. */ +@property(nonatomic) BOOL shouldHideSeparators; + +#pragma mark - Cell Appearance Animation + +/** Whether cells will animation on appearance. */ +@property(nonatomic, assign) BOOL willAnimateCellsOnAppearance; + +/** + The cell appearance animation duration. Defaults to MDCCollectionViewAnimatedAppearanceDuration. + */ +@property(nonatomic, assign) NSTimeInterval animateCellsOnAppearanceDuration; + +/** The cell delay used to stagger fade-in during appearance animation. Defaults to 0. */ +@property(nonatomic, assign) NSTimeInterval animateCellsOnAppearanceDelay; + +@end diff --git a/components/CollectionLayoutAttributes/src/MDCCollectionViewLayoutAttributes.m b/components/CollectionLayoutAttributes/src/MDCCollectionViewLayoutAttributes.m new file mode 100644 index 00000000000..8b14287b05e --- /dev/null +++ b/components/CollectionLayoutAttributes/src/MDCCollectionViewLayoutAttributes.m @@ -0,0 +1,86 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "MDCCollectionViewLayoutAttributes.h" + +@implementation MDCCollectionViewLayoutAttributes + +- (id)copyWithZone:(NSZone *)zone { + MDCCollectionViewLayoutAttributes *attributes = + (MDCCollectionViewLayoutAttributes *)[super copyWithZone:zone]; + attributes->_editing = _editing; + attributes->_shouldShowReorderStateMask = _shouldShowReorderStateMask; + attributes->_shouldShowSelectorStateMask = _shouldShowSelectorStateMask; + attributes->_shouldShowGridBackground = _shouldShowGridBackground; + attributes->_sectionOrdinalPosition = _sectionOrdinalPosition; + attributes->_backgroundImage = _backgroundImage; + attributes->_isGridLayout = _isGridLayout; + attributes->_separatorColor = _separatorColor; + attributes->_separatorInset = _separatorInset; + attributes->_separatorLineHeight = _separatorLineHeight; + attributes->_shouldHideSeparators = _shouldHideSeparators; + attributes->_willAnimateCellsOnAppearance = _willAnimateCellsOnAppearance; + attributes->_animateCellsOnAppearanceDuration = _animateCellsOnAppearanceDuration; + attributes->_animateCellsOnAppearanceDelay = _animateCellsOnAppearanceDelay; + return attributes; +} + +- (BOOL)isEqual:(id)object { + if (object == self) { + return YES; + } + if (!object || ![[object class] isEqual:[self class]]) { + return NO; + } + + // Compare custom properties that affect layout. + MDCCollectionViewLayoutAttributes *otherAttrs = (MDCCollectionViewLayoutAttributes *)object; + if ((otherAttrs.editing != self.editing) || + (otherAttrs.shouldShowReorderStateMask != self.shouldShowReorderStateMask) || + (otherAttrs.shouldShowSelectorStateMask != self.shouldShowSelectorStateMask) || + (otherAttrs.shouldShowGridBackground != self.shouldShowGridBackground) || + (otherAttrs.sectionOrdinalPosition != self.sectionOrdinalPosition) || + (otherAttrs.backgroundImage != self.backgroundImage) || + (otherAttrs.isGridLayout != self.isGridLayout) || + (otherAttrs.separatorColor != self.separatorColor) || + (!UIEdgeInsetsEqualToEdgeInsets(otherAttrs.separatorInset, self.separatorInset)) || + (otherAttrs.separatorLineHeight != self.separatorLineHeight) || + (otherAttrs.shouldHideSeparators != self.shouldHideSeparators) || + (!CGRectEqualToRect(otherAttrs.frame, self.frame))) { + return NO; + } + + return [super isEqual:object]; +} + +- (NSUInteger)hash { + return (NSUInteger)self.editing ^ + (NSUInteger)self.shouldShowReorderStateMask ^ + (NSUInteger)self.shouldShowSelectorStateMask ^ + (NSUInteger)self.shouldShowGridBackground ^ + (NSUInteger)self.sectionOrdinalPosition ^ + (NSUInteger)self.backgroundImage ^ + (NSUInteger)self.isGridLayout ^ + (NSUInteger)self.separatorColor ^ + (NSUInteger)self.separatorLineHeight ^ + (NSUInteger)self.shouldHideSeparators; +} + +@end diff --git a/components/CollectionLayoutAttributes/src/MaterialCollectionLayoutAttributes.h b/components/CollectionLayoutAttributes/src/MaterialCollectionLayoutAttributes.h new file mode 100644 index 00000000000..78edd8d9a75 --- /dev/null +++ b/components/CollectionLayoutAttributes/src/MaterialCollectionLayoutAttributes.h @@ -0,0 +1,17 @@ +/* + 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 "MDCCollectionViewLayoutAttributes.h" diff --git a/components/Collections/.jazzy.yaml b/components/Collections/.jazzy.yaml new file mode 100644 index 00000000000..9759e4bc93c --- /dev/null +++ b/components/Collections/.jazzy.yaml @@ -0,0 +1,5 @@ +# Auto-generated by scripts/generate_jazzy_yamls.sh. Used primarily by scripts/external/arc-jazzy-linter. +module: Collections +umbrella_header: src/MaterialCollections.h +objc: true +sdk: iphonesimulator diff --git a/components/Collections/README.md b/components/Collections/README.md new file mode 100644 index 00000000000..9deda017239 --- /dev/null +++ b/components/Collections/README.md @@ -0,0 +1,261 @@ +--- +title: "Collections" +layout: detail +section: components +excerpt: "Collection view classes that adhere to Material design layout and animation styling." +--- +# Collections + +![Collections](docs/assets/appbar_screenshot.png) + + +Collection view classes that adhere to Material design layout and animation styling. + + +### Material Design Specifications + + + +### API Documentation + + + +- - - + +## Installation + +### Requirements + +- Xcode 7.0 or higher. +- iOS SDK version 7.0 or higher. + +### Installation with CocoaPods + +To add this component to your Xcode project using CocoaPods, add the following to your `Podfile`: + +~~~ +pod 'MaterialComponents/Collections' +~~~ + +Then, run the following command: + +~~~ bash +$ pod install +~~~ + +- - - + +## Usage + +### Importing + +Before using Collections, you'll need to import it: + + +#### Objective-C + +~~~ objc +#import "MaterialCollections.h" +~~~ + +#### Swift +~~~ swift +import MaterialCollections +~~~ + + +### Use `MDCCollectionViewController` as a view controller + +The following four steps will allow you to get a basic example to get a MDCCollectionViewController +up and running. + +Step 1: **Subclass `MDCCollectionViewController` in your view controller interface**. + + +#### Objective-C + +~~~ objc +#import "MaterialCollections.h" + +@interface MyCollectionsExample : MDCCollectionViewController +@end +~~~ + +#### Swift +~~~ swift +~~~ + + +Step 2: **Setup your data**. + + +#### Objective-C + +~~~ objc +_colors = @[ @"red", @"blue", @"green", @"black", @"yellow", @"purple" ]; +~~~ + +#### Swift +~~~ swift +~~~ + + +Step 3: **Register a cell class**. + + +#### Objective-C + +~~~ objc +[self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; +~~~ + +#### Swift +~~~ swift +~~~ + + +Step 4: **Override `UICollectionViewDataSource` protocol required methods**. + + +#### Objective-C + +~~~ objc +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return _colors.count; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _colors[indexPath.item]; + return cell; +} +~~~ + +#### Swift +~~~ swift +~~~ + + +- - - + +### Styling the collection view + +The `MDCCollectionViewStyleManager` class provides methods and properties for styling the +collection view. Styling can be set for the entire collection view, or by using the +`MDCCollectionViewStyleManagerDelegate` protocol methods to define styles at specific +sections and rows. + +### Cell Styles + +The style manager allows setting the cell style as Default, Grouped, or Card Style. Choose to +either set the style manager `cellStyle` property directly, or use the protocol method +`collectionView:cellStyleForSection:` to style per section. + + +#### Objective-C + +~~~ objc +// Set for entire collection view. +self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + +// Or set for specific sections. +- (MDCCollectionViewCellStyle)collectionView:(UICollectionView *)collectionView + cellStyleForSection:(NSInteger)section { + if (section == 2) { + return MDCCollectionViewCellStyleCard; + } + return MDCCollectionViewCellStyleGrouped; +} +~~~ + +#### Swift +~~~ swift +~~~ + + +### Cell Height + +The style manager delegate protocol can be used to override the default cell height of 48.0f. + + +#### Objective-C + +~~~ objc +- (CGFloat)collectionView:(UICollectionView *)collectionView + cellHeightAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.item == 0) { + return 80.0f; + } + return 48.0f; +} +~~~ + +#### Swift +~~~ swift +~~~ + + +### Cell Layout + +The style manager allows setting the cell layout as List, Grid, or Custom. + + +#### Objective-C + +~~~ objc +// Set as list layout. +self.styleManager.cellLayoutType = MDCCollectionViewCellLayoutTypeList; + +// Or set as grid layout. +self.styleManager.cellLayoutType = MDCCollectionViewCellLayoutTypeGrid; +self.styleManager.gridPadding = 8; +self.styleManager.gridColumnCount = 2; +~~~ + +#### Swift +~~~ swift +~~~ + + +### Cell Separators + +The style manager allows customizing cell separators for the entire collection view. Individual +cell customization can is available by using a cell subclassed from MDCCollectionViewCell. Learn +more by reading the section on [Cell Separators](../CollectionCells/#cell-separators) in the +[CollectionCells](../CollectionCells) component. + + +#### Objective-C + +~~~ objc +// Set separator color. +self.styleManager.separatorColor = [UIColor redColor]; + +// Set separator insets. +self.styleManager.separatorInset = UIEdgeInsetsMake(0, 16, 0, 16); + +// Set separator line height. +self.styleManager.separatorLineHeight = 1.0f; + +// Whether to hide separators. +self.styleManager.shouldHideSeparators = NO; +~~~ + +#### Swift +~~~ swift +~~~ + diff --git a/components/Collections/examples/CollectionsAppearanceAnimationExample.m b/components/Collections/examples/CollectionsAppearanceAnimationExample.m new file mode 100644 index 00000000000..7eacdb0c451 --- /dev/null +++ b/components/Collections/examples/CollectionsAppearanceAnimationExample.m @@ -0,0 +1,80 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionsAppearanceAnimationExample.h" + +#import "MaterialTypography.h" + +static const NSInteger kSectionCount = 10; +static const NSInteger kSectionItemCount = 5; +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; + +@implementation CollectionsAppearanceAnimationExample { + NSMutableArray *_content; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collections", @"Appearance Animation Example" ]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Register cell class. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Populate content. + _content = [NSMutableArray array]; + for (NSInteger i = 0; i < kSectionCount; i++) { + NSMutableArray *items = [NSMutableArray array]; + for (NSInteger j = 0; j < kSectionItemCount; j++) { + NSString *itemString = [NSString stringWithFormat:@"Section-%zd Item-%zd", i, j]; + [items addObject:itemString]; + } + [_content addObject:items]; + } + + // Customize collection view settings. + self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + self.styleManager.shouldAnimateCellsOnAppearance = YES; +} + +#pragma mark - + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return [_content count]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content[section] count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _content[indexPath.section][indexPath.item]; + return cell; +} + +@end diff --git a/components/Collections/examples/CollectionsCellAccessoryExample.m b/components/Collections/examples/CollectionsCellAccessoryExample.m new file mode 100644 index 00000000000..001ee015dfe --- /dev/null +++ b/components/Collections/examples/CollectionsCellAccessoryExample.m @@ -0,0 +1,123 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionsCellAccessoryExample.h" + +#import "MaterialSwitch.h" + +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; + +@implementation CollectionsCellAccessoryExample { + NSArray *_accessoryTypes; + NSMutableArray *_content; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collections", @"Cell Accessory Example" ]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Register cell class. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Array of available accessory types. + _accessoryTypes = @[ @(MDCCollectionViewCellAccessoryDisclosureIndicator), + @(MDCCollectionViewCellAccessoryCheckmark), + @(MDCCollectionViewCellAccessoryDetailButton), + @(MDCCollectionViewCellAccessoryNone), + @(MDCCollectionViewCellAccessoryNone) ]; + + // Populate content. + _content = [NSMutableArray array]; + [_content addObject:@[ @"Enable Editing" ]]; + [_content addObject:@[ @"Disclosure Indicator", + @"Checkmark", + @"Detail Button", + @"Custom Accessory View", + @"No Accessory View" ]]; + + // Customize collection view settings. + self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; +} + +#pragma mark - + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return [_content count]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content[section] count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _content[indexPath.section][indexPath.item]; + + // Add accessory views. + if (indexPath.section == 0) { + // Add switch as accessory view. + MDCSwitch *editingSwitch = [[MDCSwitch alloc] initWithFrame:CGRectZero]; + [editingSwitch addTarget:self + action:@selector(didSwitch:) + forControlEvents:UIControlEventValueChanged]; + cell.accessoryView = editingSwitch; + } else { + cell.accessoryType = [_accessoryTypes[indexPath.item] unsignedIntegerValue]; + } + + return cell; +} + +#pragma mark - + +- (BOOL)collectionViewAllowsEditing:(UICollectionView *)collectionView { + return NO; +} + +- (BOOL)collectionViewAllowsReordering:(UICollectionView *)collectionView { + return NO; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + canEditItemAtIndexPath:(NSIndexPath *)indexPath { + return (indexPath.section != 0); +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + canMoveItemAtIndexPath:(NSIndexPath *)indexPath { + return (indexPath.section != 0); +} + +#pragma mark - UIControlEvents + +- (void)didSwitch:(id)sender { + MDCSwitch *switchControl = sender; + [self.editingManager setEditing:switchControl.isOn animated:YES]; +} + +@end diff --git a/components/Collections/examples/CollectionsCellColorExample.m b/components/Collections/examples/CollectionsCellColorExample.m new file mode 100644 index 00000000000..dcb3ca60762 --- /dev/null +++ b/components/Collections/examples/CollectionsCellColorExample.m @@ -0,0 +1,86 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionsCellColorExample.h" + +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; + +@implementation CollectionsCellColorExample { + NSMutableArray *_content; + NSArray *_cellBackgroundColors; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collections", @"Cell Color Example" ]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Register cell class. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Array of cell background colors. + _cellBackgroundColors = @[ [UIColor colorWithWhite:0 alpha:0.2], + [UIColor colorWithRed:(CGFloat)0x39 / (CGFloat)255 + green:(CGFloat)0xA4 / (CGFloat)255 + blue:(CGFloat)0xDD / (CGFloat)255 + alpha:1], + [UIColor whiteColor] ]; + + // Populate content. + _content = [NSMutableArray array]; + [_content addObject:@[ @"[UIColor colorWithWhite:0 alpha:0.2]", + @"Custom Blue Color", + @"Default White Color" ]]; + + // Customize collection view settings. + self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; +} + +#pragma mark - + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return [_content count]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content[section] count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _content[indexPath.section][indexPath.item]; + return cell; +} + +#pragma mark - + +- (UIColor *)collectionView:(UICollectionView *)collectionView + cellBackgroundColorAtIndexPath:(NSIndexPath *)indexPath { + return _cellBackgroundColors[indexPath.item]; +} + +@end diff --git a/components/Collections/examples/CollectionsCellSeparatorExample.m b/components/Collections/examples/CollectionsCellSeparatorExample.m new file mode 100644 index 00000000000..df5437fe447 --- /dev/null +++ b/components/Collections/examples/CollectionsCellSeparatorExample.m @@ -0,0 +1,87 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionsCellSeparatorExample.h" + +static const NSInteger kSectionCount = 10; +static const NSInteger kSectionItemCount = 3; +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; + +@implementation CollectionsCellSeparatorExample { + NSMutableArray *_content; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collections", @"Cell Separator Example" ]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Register cell class. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Populate content. + _content = [NSMutableArray array]; + for (NSInteger i = 0; i < kSectionCount; i++) { + NSMutableArray *items = [NSMutableArray array]; + for (NSInteger j = 0; j < kSectionItemCount; j++) { + NSString *itemString = [NSString stringWithFormat:@"Section-%zd Item-%zd", i, j]; + [items addObject:itemString]; + } + [_content addObject:items]; + } + + // Customize collection view settings. + self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; +} + +#pragma mark - + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return [_content count]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content[section] count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _content[indexPath.section][indexPath.item]; + + // Customize separators. + if (indexPath.section == 0) { + cell.separatorInset = UIEdgeInsetsMake(0, 72, 0, 0); + } else if (indexPath.section == 1) { + cell.separatorInset = UIEdgeInsetsMake(0, 0, 0, 72); + } else if (indexPath.section == 2) { + cell.shouldHideSeparator = YES; + } + + return cell; +} + +@end diff --git a/components/Collections/examples/CollectionsContainerExample.m b/components/Collections/examples/CollectionsContainerExample.m new file mode 100644 index 00000000000..fa9c80f98dc --- /dev/null +++ b/components/Collections/examples/CollectionsContainerExample.m @@ -0,0 +1,99 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionsContainerExample.h" + +#import "MaterialCollections.h" + +static const NSInteger kSectionCount = 2; +static const NSInteger kSectionItemCount = 2; +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; + +@implementation CollectionsContainerExample { + MDCCollectionViewController *_collectionsController; + NSMutableArray *_content; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collections", @"Collections in a Container" ]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor whiteColor]; + + // Create gray view to contain collection view. + UIView *container = + [[UIView alloc] initWithFrame:CGRectMake(30, + 200, + self.view.bounds.size.width - 60, + self.view.bounds.size.height - 200 - 30)]; + container.backgroundColor = [UIColor lightGrayColor]; + container.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self.view addSubview:container]; + + // Create collection view controller. + _collectionsController = [[MDCCollectionViewController alloc] init]; + _collectionsController.collectionView.dataSource = self; + [container addSubview:_collectionsController.view]; + [_collectionsController.view setFrame:container.bounds]; + + // Register cell class. + [_collectionsController.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Populate content. + _content = [NSMutableArray array]; + for (NSInteger i = 0; i < kSectionCount; i++) { + NSMutableArray *items = [NSMutableArray array]; + for (NSInteger j = 0; j < kSectionItemCount; j++) { + NSString *itemString = [NSString stringWithFormat:@"Section-%zd Item-%zd", i, j]; + [items addObject:itemString]; + } + [_content addObject:items]; + } + + // Customize collection view settings. + MDCCollectionViewStyleManager *styleManager = _collectionsController.styleManager; + styleManager.cellStyle = MDCCollectionViewCellStyleCard; +} + +#pragma mark - + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return [_content count]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content[section] count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _content[indexPath.section][indexPath.item]; + return cell; +} + +@end diff --git a/components/Collections/examples/CollectionsEditingExample.m b/components/Collections/examples/CollectionsEditingExample.m new file mode 100644 index 00000000000..e1af20304ff --- /dev/null +++ b/components/Collections/examples/CollectionsEditingExample.m @@ -0,0 +1,132 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionsEditingExample.h" + +static const NSInteger kSectionCount = 10; +static const NSInteger kSectionItemCount = 5; +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; + +@implementation CollectionsEditingExample { + NSMutableArray *_content; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collections", @"Cell Editing Example" ]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Add button to toggle edit mode. + [self updatedRightBarButtonItem:NO]; + + // Register cell class. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Populate content. + _content = [NSMutableArray array]; + for (NSInteger i = 0; i < kSectionCount; i++) { + NSMutableArray *items = [NSMutableArray array]; + for (NSInteger j = 0; j < kSectionItemCount; j++) { + NSString *itemString = [NSString stringWithFormat:@"Section-%zd Item-%zd", i, j]; + [items addObject:itemString]; + } + [_content addObject:items]; + } + + // Customize collection view settings. + self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; +} + +- (void)updatedRightBarButtonItem:(BOOL)isEditing { + self.navigationItem.rightBarButtonItem = + [[UIBarButtonItem alloc] initWithTitle:isEditing ? @"Cancel" : @"Edit" + style:UIBarButtonItemStyleDone + target:self + action:@selector(toggleEditMode:)]; +} + +- (void)toggleEditMode:(id)sender { + BOOL isEditing = self.editingManager.isEditing; + [self updatedRightBarButtonItem:!isEditing]; + [self.editingManager setEditing:!isEditing animated:YES]; +} + +#pragma mark - + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return [_content count]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content[section] count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _content[indexPath.section][indexPath.item]; + return cell; +} + +#pragma mark - + +- (BOOL)collectionViewAllowsEditing:(UICollectionView *)collectionView { + return YES; +} + +- (BOOL)collectionViewAllowsReordering:(UICollectionView *)collectionView { + return YES; +} + +- (BOOL)collectionViewAllowsSwipeToDismissItem:(UICollectionView *)collectionView { + return self.editingManager.isEditing; +} + +- (void)collectionView:(UICollectionView *)collectionView + willDeleteItemsAtIndexPaths:(NSArray *)indexPaths { + // Remove these index paths from our data. + for (NSIndexPath *indexPath in indexPaths) { + [_content[indexPath.section] removeObjectAtIndex:indexPath.item]; + } +} + +- (void)collectionView:(UICollectionView *)collectionView + willMoveItemAtIndexPath:(NSIndexPath *)indexPath + toIndexPath:(NSIndexPath *)newIndexPath { + if (indexPath.section == newIndexPath.section) { + // Exchange data within same section. + [_content[indexPath.section] exchangeObjectAtIndex:indexPath.item + withObjectAtIndex:newIndexPath.item]; + } else { + // Since moving to different section, first remove data from index path and insert + // at new index path. + id movedObject = [_content[indexPath.section] objectAtIndex:indexPath.item]; + [_content[indexPath.section] removeObjectAtIndex:indexPath.item]; + [_content[newIndexPath.section] insertObject:movedObject atIndex:newIndexPath.item]; + } +} + +@end diff --git a/components/Collections/examples/CollectionsGridExample.m b/components/Collections/examples/CollectionsGridExample.m new file mode 100644 index 00000000000..b62132a38c8 --- /dev/null +++ b/components/Collections/examples/CollectionsGridExample.m @@ -0,0 +1,142 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionsGridExample.h" + +static const NSInteger kSectionCount = 10; +static const NSInteger kSectionItemCount = 4; +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; + +@implementation CollectionsGridExample { + NSMutableArray *_content; + UIAlertController *_actionController; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collections", @"Grid Example" ]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Add button to update styles. + self.navigationItem.rightBarButtonItem = + [[UIBarButtonItem alloc] initWithTitle:@"Update Styles" + style:UIBarButtonItemStylePlain + target:self + action:@selector(presentActionController:)]; + + // Register cell class. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Populate content. + _content = [NSMutableArray array]; + for (NSInteger i = 0; i < kSectionCount; i++) { + NSMutableArray *items = [NSMutableArray array]; + for (NSInteger j = 0; j < kSectionItemCount; j++) { + NSString *itemString = [NSString stringWithFormat:@"Section-%zd Item-%zd", i, j]; + [items addObject:itemString]; + } + [_content addObject:items]; + } + + // Customize collection view settings. + self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + self.styleManager.cellLayoutType = MDCCollectionViewCellLayoutTypeGrid; + self.styleManager.gridPadding = 8; + self.styleManager.gridColumnCount = 2; +} + +#pragma mark - + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return [_content count]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content[section] count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _content[indexPath.section][indexPath.item]; + return cell; +} + +#pragma mark - Update Styles + +- (void)toggleCellLayoutType { + // Toggles between list and grid layout. + BOOL isListLayout = (self.styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeList); + self.styleManager.cellLayoutType = + isListLayout ? MDCCollectionViewCellLayoutTypeGrid : MDCCollectionViewCellLayoutTypeList; + [self.collectionView performBatchUpdates:nil completion:nil]; +} + +- (void)toggleCellStyle { + // Toggles between card and grouped styles. + BOOL isCardStyle = (self.styleManager.cellStyle == MDCCollectionViewCellStyleCard); + self.styleManager.cellStyle = + isCardStyle ? MDCCollectionViewCellStyleGrouped : MDCCollectionViewCellStyleCard; + [self.collectionView performBatchUpdates:nil completion:nil]; +} + +#pragma mark - Action Controller + +- (void)presentActionController:(id)sender { + _actionController = + [UIAlertController alertControllerWithTitle:@"Update Styles" + message:nil + preferredStyle:UIAlertControllerStyleActionSheet]; + + [_actionController addAction: + [UIAlertAction actionWithTitle:@"Toggle List/Grid Layout" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + [self toggleCellLayoutType]; + }]]; + + [_actionController addAction: + [UIAlertAction actionWithTitle:@"Toggle Card/Grouped Style" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + [self toggleCellStyle]; + }]]; + + [_actionController addAction: + [UIAlertAction actionWithTitle:@"Cancel" + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) { + [self dismissActionController]; + }]]; + + [self presentViewController:_actionController animated:YES completion:nil]; +} + +- (void)dismissActionController { + [self dismissViewControllerAnimated:YES completion:nil]; +} + +@end diff --git a/components/Collections/examples/CollectionsHeaderFooterExample.m b/components/Collections/examples/CollectionsHeaderFooterExample.m new file mode 100644 index 00000000000..a82f67d2720 --- /dev/null +++ b/components/Collections/examples/CollectionsHeaderFooterExample.m @@ -0,0 +1,133 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionsHeaderFooterExample.h" + +#import "MaterialTypography.h" + +static const NSInteger kSectionCount = 3; +static const NSInteger kSectionItemCount = 2; +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; + +@implementation CollectionsHeaderFooterExample { + NSMutableArray *_content; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collections", @"Header / Footer Demo" ]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Register cell. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Register header. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forSupplementaryViewOfKind:UICollectionElementKindSectionHeader + withReuseIdentifier:UICollectionElementKindSectionHeader]; + + // Register footer. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forSupplementaryViewOfKind:UICollectionElementKindSectionFooter + withReuseIdentifier:UICollectionElementKindSectionFooter]; + + // Populate content. + _content = [NSMutableArray array]; + for (NSInteger i = 0; i < kSectionCount; i++) { + NSMutableArray *items = [NSMutableArray array]; + for (NSInteger j = 0; j < kSectionItemCount; j++) { + NSString *itemString = [NSString stringWithFormat:@"Section-%zd Item-%zd", i, j]; + [items addObject:itemString]; + } + [_content addObject:items]; + } + + // Customize collection view settings. + self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; +} + +#pragma mark - + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return [_content count]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content[section] count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _content[indexPath.section][indexPath.item]; + return cell; +} + +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView + viewForSupplementaryElementOfKind:(NSString *)kind + atIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *supplementaryView = + [collectionView dequeueReusableSupplementaryViewOfKind:kind + withReuseIdentifier:kind + forIndexPath:indexPath]; + + if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { + if (indexPath.section == 0) { + supplementaryView.textLabel.text = @"Section with only header"; + } + supplementaryView.textLabel.text = @"Section header"; + + } else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) { + if (indexPath.section == 1) { + supplementaryView.textLabel.text = @"Section with only footer"; + } + supplementaryView.textLabel.text = @"Section footer"; + } + + return supplementaryView; +} + +#pragma mark - + +- (CGSize)collectionView:(UICollectionView *)collectionView + layout:(UICollectionViewLayout *)collectionViewLayout + referenceSizeForHeaderInSection:(NSInteger)section { + if (section == 0 || section == 2) { + return CGSizeMake(collectionView.bounds.size.width, MDCCellDefaultOneLineHeight); + } + return CGSizeZero; +} + +- (CGSize)collectionView:(UICollectionView *)collectionView + layout:(UICollectionViewLayout *)collectionViewLayout + referenceSizeForFooterInSection:(NSInteger)section { + if (section > 0) { + return CGSizeMake(collectionView.bounds.size.width, MDCCellDefaultOneLineHeight); + } + return CGSizeZero; +} + +@end diff --git a/components/Collections/examples/CollectionsInlayExample.m b/components/Collections/examples/CollectionsInlayExample.m new file mode 100644 index 00000000000..5fd00dce895 --- /dev/null +++ b/components/Collections/examples/CollectionsInlayExample.m @@ -0,0 +1,77 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionsInlayExample.h" + +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; + +@implementation CollectionsInlayExample { + NSArray *_colors; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collections", @"Cell Inlay Example" ]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + _colors = @[ @"red", @"blue", @"green", @"black", @"yellow", @"purple" ]; + + // Register cell class. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Customize collection view settings. + self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + self.styleManager.allowsItemInlay = YES; + self.styleManager.allowsMultipleItemInlays = YES; +} + +#pragma mark - + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return _colors.count; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _colors[indexPath.item]; + return cell; +} + +#pragma mark - + +- (void)collectionView:(UICollectionView *)collectionView + didSelectItemAtIndexPath:(NSIndexPath *)indexPath { + [super collectionView:collectionView didSelectItemAtIndexPath:indexPath]; + BOOL isInlaid = [self.styleManager isItemInlaidAtIndexPath:indexPath]; + if (isInlaid) { + [self.styleManager removeInlayFromItemAtIndexPath:indexPath animated:YES]; + } else { + [self.styleManager applyInlayToItemAtIndexPath:indexPath animated:YES]; + } +} + +@end diff --git a/components/Collections/examples/CollectionsSimpleDemo.m b/components/Collections/examples/CollectionsSimpleDemo.m new file mode 100644 index 00000000000..662f7daefb0 --- /dev/null +++ b/components/Collections/examples/CollectionsSimpleDemo.m @@ -0,0 +1,90 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionsSimpleDemo.h" + +#import "MaterialTypography.h" + +static const NSInteger kSectionCount = 10; +static const NSInteger kSectionItemCount = 5; +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; + +@implementation CollectionsSimpleDemo { + NSMutableArray *_content; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collections", @"Simple Demo" ]; +} + ++ (BOOL)catalogIsPrimaryDemo { + return YES; +} + ++ (NSString *)catalogDescription { + return @"Material Collections enables a native collection view controller to have Material " + "design layout and styling. It also provides editing and extensive customization " + "capabilities."; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + self.title = @"Simple Demo"; + + // Register cell class. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Populate content. + _content = [NSMutableArray array]; + for (NSInteger i = 0; i < kSectionCount; i++) { + NSMutableArray *items = [NSMutableArray array]; + for (NSInteger j = 0; j < kSectionItemCount; j++) { + NSString *itemString = [NSString stringWithFormat:@"Section-%zd Item-%zd", i, j]; + [items addObject:itemString]; + } + [_content addObject:items]; + } + + // Customize collection view settings. + self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; +} + +#pragma mark - + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return [_content count]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content[section] count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _content[indexPath.section][indexPath.item]; + return cell; +} + +@end diff --git a/components/Collections/examples/CollectionsSwipeToDismissRowExample.m b/components/Collections/examples/CollectionsSwipeToDismissRowExample.m new file mode 100644 index 00000000000..4389001d009 --- /dev/null +++ b/components/Collections/examples/CollectionsSwipeToDismissRowExample.m @@ -0,0 +1,104 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionsSwipeToDismissRowExample.h" + +static const NSInteger kSectionCount = 10; +static const NSInteger kSectionItemCount = 5; +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; + +@implementation CollectionsSwipeToDismissRowExample { + NSMutableArray *_content; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collections", @"Swipe-To-Dismiss-Row Example" ]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Register cell class. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Populate content. + _content = [NSMutableArray array]; + for (NSInteger i = 0; i < kSectionCount; i++) { + NSMutableArray *items = [NSMutableArray array]; + for (NSInteger j = 0; j < kSectionItemCount; j++) { + NSString *itemString = [NSString stringWithFormat:@"Section-%zd Item-%zd", i, j]; + [items addObject:itemString]; + } + [_content addObject:items]; + } + + // Insert cell that cannot be removed. + [_content insertObject:@[ @"This cell cannot be deleted." ] atIndex:0]; + + // Customize collection view settings. + self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; +} + +#pragma mark - + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return [_content count]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content[section] count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _content[indexPath.section][indexPath.item]; + return cell; +} + +#pragma mark - + +- (BOOL)collectionViewAllowsSwipeToDismissItem:(UICollectionView *)collectionView { + return YES; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + canSwipeToDismissItemAtIndexPath:(NSIndexPath *)indexPath { + // In this example we are allowing all items to be dismissed + // except this first section. + if (indexPath.section == 0) { + return NO; + } + return YES; +} + +- (void)collectionView:(UICollectionView *)collectionView + willDeleteItemsAtIndexPaths:(NSArray *)indexPaths { + // Remove these swiped index paths from our data. + for (NSIndexPath *indexPath in indexPaths) { + [_content[indexPath.section] removeObjectAtIndex:indexPath.item]; + } +} + +@end diff --git a/components/Collections/examples/CollectionsSwipeToDismissSectionExample.m b/components/Collections/examples/CollectionsSwipeToDismissSectionExample.m new file mode 100644 index 00000000000..10efcaa4854 --- /dev/null +++ b/components/Collections/examples/CollectionsSwipeToDismissSectionExample.m @@ -0,0 +1,104 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "CollectionsSwipeToDismissSectionExample.h" + +static const NSInteger kSectionCount = 10; +static const NSInteger kSectionItemCount = 5; +static NSString *const kReusableIdentifierItem = @"itemCellIdentifier"; + +@implementation CollectionsSwipeToDismissSectionExample { + NSMutableArray *_content; +} + ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Collections", @"Swipe-To-Dismiss-Section Example" ]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Register cell class. + [self.collectionView registerClass:[MDCCollectionViewTextCell class] + forCellWithReuseIdentifier:kReusableIdentifierItem]; + + // Populate content. + _content = [NSMutableArray array]; + for (NSInteger i = 0; i < kSectionCount; i++) { + NSMutableArray *items = [NSMutableArray array]; + for (NSInteger j = 0; j < kSectionItemCount; j++) { + NSString *itemString = [NSString stringWithFormat:@"Section-%zd Item-%zd", i, j]; + [items addObject:itemString]; + } + [_content addObject:items]; + } + + // Insert section of cells that cannot be removed. + [_content insertObject:@[ @"This cell cannot be deleted.", + @"This cell cannot be deleted." ] + atIndex:0]; + + // Customize collection view settings. + self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; +} + +#pragma mark - + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return [_content count]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [_content[section] count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewTextCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem + forIndexPath:indexPath]; + cell.textLabel.text = _content[indexPath.section][indexPath.item]; + return cell; +} + +#pragma mark - + +- (BOOL)collectionViewAllowsSwipeToDismissSection:(UICollectionView *)collectionView { + return YES; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + canSwipeToDismissSection:(NSInteger)section { + // In this example we are allowing all sections to be dismissed + // except this first section. + if (section == 0) { + return NO; + } + return YES; +} + +- (void)collectionView:(UICollectionView *)collectionView + willDeleteSections:(NSIndexSet *)sections { + // Remove these swiped sections from our data. + [_content removeObjectsAtIndexes:sections]; +} + +@end diff --git a/components/Collections/examples/supplemental/CollectionsAppearanceAnimationExample.h b/components/Collections/examples/supplemental/CollectionsAppearanceAnimationExample.h new file mode 100644 index 00000000000..e880f2a17b3 --- /dev/null +++ b/components/Collections/examples/supplemental/CollectionsAppearanceAnimationExample.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionsAppearanceAnimationExample : MDCCollectionViewController +@end diff --git a/components/Collections/examples/supplemental/CollectionsCellAccessoryExample.h b/components/Collections/examples/supplemental/CollectionsCellAccessoryExample.h new file mode 100644 index 00000000000..1d3506177c7 --- /dev/null +++ b/components/Collections/examples/supplemental/CollectionsCellAccessoryExample.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionsCellAccessoryExample : MDCCollectionViewController +@end diff --git a/components/Collections/examples/supplemental/CollectionsCellColorExample.h b/components/Collections/examples/supplemental/CollectionsCellColorExample.h new file mode 100644 index 00000000000..f00b6ec0cfb --- /dev/null +++ b/components/Collections/examples/supplemental/CollectionsCellColorExample.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionsCellColorExample : MDCCollectionViewController +@end diff --git a/components/Collections/examples/supplemental/CollectionsCellSeparatorExample.h b/components/Collections/examples/supplemental/CollectionsCellSeparatorExample.h new file mode 100644 index 00000000000..80e93b11ec9 --- /dev/null +++ b/components/Collections/examples/supplemental/CollectionsCellSeparatorExample.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionsCellSeparatorExample : MDCCollectionViewController +@end diff --git a/components/Collections/examples/supplemental/CollectionsContainerExample.h b/components/Collections/examples/supplemental/CollectionsContainerExample.h new file mode 100644 index 00000000000..4740f5bda2f --- /dev/null +++ b/components/Collections/examples/supplemental/CollectionsContainerExample.h @@ -0,0 +1,20 @@ +/* + 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 CollectionsContainerExample : UIViewController +@end diff --git a/components/Collections/examples/supplemental/CollectionsEditingExample.h b/components/Collections/examples/supplemental/CollectionsEditingExample.h new file mode 100644 index 00000000000..02231a72680 --- /dev/null +++ b/components/Collections/examples/supplemental/CollectionsEditingExample.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionsEditingExample : MDCCollectionViewController +@end diff --git a/components/Collections/examples/supplemental/CollectionsGridExample.h b/components/Collections/examples/supplemental/CollectionsGridExample.h new file mode 100644 index 00000000000..ce128440aba --- /dev/null +++ b/components/Collections/examples/supplemental/CollectionsGridExample.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionsGridExample : MDCCollectionViewController +@end diff --git a/components/Collections/examples/supplemental/CollectionsHeaderFooterExample.h b/components/Collections/examples/supplemental/CollectionsHeaderFooterExample.h new file mode 100644 index 00000000000..c5b507807da --- /dev/null +++ b/components/Collections/examples/supplemental/CollectionsHeaderFooterExample.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionsHeaderFooterExample : MDCCollectionViewController +@end diff --git a/components/Collections/examples/supplemental/CollectionsInlayExample.h b/components/Collections/examples/supplemental/CollectionsInlayExample.h new file mode 100644 index 00000000000..7296bcb3528 --- /dev/null +++ b/components/Collections/examples/supplemental/CollectionsInlayExample.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionsInlayExample : MDCCollectionViewController +@end diff --git a/components/Collections/examples/supplemental/CollectionsSimpleDemo.h b/components/Collections/examples/supplemental/CollectionsSimpleDemo.h new file mode 100644 index 00000000000..581c00618ed --- /dev/null +++ b/components/Collections/examples/supplemental/CollectionsSimpleDemo.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionsSimpleDemo : MDCCollectionViewController +@end diff --git a/components/Collections/examples/supplemental/CollectionsSwipeToDismissRowExample.h b/components/Collections/examples/supplemental/CollectionsSwipeToDismissRowExample.h new file mode 100644 index 00000000000..d50dbea26e6 --- /dev/null +++ b/components/Collections/examples/supplemental/CollectionsSwipeToDismissRowExample.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionsSwipeToDismissRowExample : MDCCollectionViewController +@end diff --git a/components/Collections/examples/supplemental/CollectionsSwipeToDismissSectionExample.h b/components/Collections/examples/supplemental/CollectionsSwipeToDismissSectionExample.h new file mode 100644 index 00000000000..382f7a797ef --- /dev/null +++ b/components/Collections/examples/supplemental/CollectionsSwipeToDismissSectionExample.h @@ -0,0 +1,22 @@ +/* + 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 "MaterialCollections.h" + +@interface CollectionsSwipeToDismissSectionExample : MDCCollectionViewController +@end diff --git a/components/Collections/src/MDCCollectionViewController.h b/components/Collections/src/MDCCollectionViewController.h new file mode 100644 index 00000000000..7a2c739b8e9 --- /dev/null +++ b/components/Collections/src/MDCCollectionViewController.h @@ -0,0 +1,68 @@ +/* + 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 "MDCCollectionViewEditingManager.h" +#import "MDCCollectionViewEditingManagerDelegate.h" +#import "MDCCollectionViewStyleManager.h" +#import "MDCCollectionViewStyleManagerDelegate.h" + +/** + Controller that implements a collection view that adheres to Material design layout + and animation styling. + */ +@interface MDCCollectionViewController : UICollectionViewController < + /** Allows for editing notifications/permissions. */ + MDCCollectionViewEditingManagerDelegate, + + /** Allows for styling updates. */ + MDCCollectionViewStyleManagerDelegate, + + /** Adheres to flow layout. */ + UICollectionViewDelegateFlowLayout> + +/** The collection view style manager. */ +@property(nonatomic, strong, readonly, nonnull) MDCCollectionViewStyleManager *styleManager; + +/** The collection view editing manager. */ +@property(nonatomic, strong, readonly, nonnull) + MDCCollectionViewEditingManager *editingManager; + +#pragma mark - Subclassing + +/** + The following methods require a call to super in their overriding implementations to allow + this collection view controller to properly configure the collection view when in editing mode. + */ + +- (BOOL)collectionView:(nonnull UICollectionView *)collectionView + shouldSelectItemAtIndexPath:(nonnull NSIndexPath *)indexPath NS_REQUIRES_SUPER; + +- (BOOL)collectionView:(nonnull UICollectionView *)collectionView + shouldDeselectItemAtIndexPath:(nonnull NSIndexPath *)indexPath NS_REQUIRES_SUPER; + +- (void)collectionView:(nonnull UICollectionView *)collectionView + didSelectItemAtIndexPath:(nonnull NSIndexPath *)indexPath NS_REQUIRES_SUPER; + +- (void)collectionView:(nonnull UICollectionView *)collectionView + didDeselectItemAtIndexPath:(nonnull NSIndexPath *)indexPath NS_REQUIRES_SUPER; + +- (void)collectionViewWillBeginEditing:(nonnull UICollectionView *)collectionView NS_REQUIRES_SUPER; + +- (void)collectionViewWillEndEditing:(nonnull UICollectionView *)collectionView NS_REQUIRES_SUPER; + +@end diff --git a/components/Collections/src/MDCCollectionViewController.m b/components/Collections/src/MDCCollectionViewController.m new file mode 100644 index 00000000000..a8b92888b26 --- /dev/null +++ b/components/Collections/src/MDCCollectionViewController.m @@ -0,0 +1,563 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "MDCCollectionViewController.h" + +#import "MDCCollectionViewFlowLayout.h" +#import "MaterialCollectionCells.h" +#import "MaterialInk.h" +#import "private/MDCCollectionInfoBarView.h" +#import "private/MDCCollectionStringResources.h" + +#import + +@interface MDCCollectionViewController () + +@end + +@implementation MDCCollectionViewController { + MDCInkTouchController *_inkTouchController; + MDCCollectionInfoBarView *_headerInfoBar; + MDCCollectionInfoBarView *_footerInfoBar; + BOOL _headerInfoBarDismissed; +} + +@synthesize collectionViewLayout = _collectionViewLayout; + +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout { + self = [super initWithCollectionViewLayout:layout]; + if (self) { + [self commonMDCCollectionViewControllerInit:self.collectionViewLayout]; + } + return self; +} + +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + self = [super initWithCollectionViewLayout:self.collectionViewLayout]; + if (self) { + [self commonMDCCollectionViewControllerInit:self.collectionViewLayout]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCollectionViewLayout:self.collectionViewLayout]; + if (self) { + [self commonMDCCollectionViewControllerInit:self.collectionViewLayout]; + } + return self; +} + +- (void)commonMDCCollectionViewControllerInit:(UICollectionViewLayout *)layout { + _collectionViewLayout = layout; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.collectionView.backgroundColor = [UIColor whiteColor]; + self.collectionView.alwaysBounceVertical = YES; + + // Style manager. + _styleManager = + [[MDCCollectionViewStyleManager alloc] initWithCollectionView:self.collectionView]; + _styleManager.delegate = self; + + // Editing manager. + _editingManager = + [[MDCCollectionViewEditingManager alloc] initWithCollectionView:self.collectionView]; + _editingManager.delegate = self; + + // Set up ink touch controller. + _inkTouchController = [[MDCInkTouchController alloc] initWithView:self.collectionView]; + _inkTouchController.delegate = self; + _inkTouchController.delaysInkSpread = YES; +} + +- (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + _styleManager.shouldInvalidateLayout = NO; +} + +- (UICollectionViewLayout *)collectionViewLayout { + if (!_collectionViewLayout) { + _collectionViewLayout = [[MDCCollectionViewFlowLayout alloc] init]; + } + return _collectionViewLayout; +} + +#pragma mark - + +- (void)updateControllerWithInfoBar:(MDCCollectionInfoBarView *)infoBar { + // Updates info bar styling for header/footer. + if ([infoBar.kind isEqualToString:MDCCollectionInfoBarKindHeader]) { + _headerInfoBar = infoBar; + _headerInfoBar.message = MDCCollectionStringResources(infoBarGestureHintString); + _headerInfoBar.style = MDCCollectionInfoBarViewStyleHUD; + [self updateHeaderInfoBarIfNecessary]; + } else if ([infoBar.kind isEqualToString:MDCCollectionInfoBarKindFooter]) { + _footerInfoBar = infoBar; + _footerInfoBar.message = MDCCollectionStringResources(deleteButtonString); + _footerInfoBar.style = MDCCollectionInfoBarViewStyleActionable; + [self updateFooterInfoBarIfNecessary]; + } +} + +- (void)didTapInfoBar:(MDCCollectionInfoBarView *)infoBar { + if ([infoBar isEqual:_footerInfoBar]) { + [self deleteIndexPaths:self.collectionView.indexPathsForSelectedItems]; + } +} + +- (void)infoBar:(MDCCollectionInfoBarView *)infoBar + willShowAnimated:(BOOL)animated + willAutoDismiss:(BOOL)willAutoDismiss { + if ([infoBar.kind isEqualToString:MDCCollectionInfoBarKindFooter]) { + [self updateContentWithBottomInset:MDCCollectionInfoBarFooterHeight]; + } +} + +- (void)infoBar:(MDCCollectionInfoBarView *)infoBar + willDismissAnimated:(BOOL)animated + willAutoDismiss:(BOOL)willAutoDismiss { + if ([infoBar.kind isEqualToString:MDCCollectionInfoBarKindHeader]) { + _headerInfoBarDismissed = willAutoDismiss; + } else { + [self updateContentWithBottomInset:-MDCCollectionInfoBarFooterHeight]; + } +} + +#pragma mark - + +- (MDCCollectionViewCellStyle)collectionView:(UICollectionView *)collectionView + cellStyleForSection:(NSInteger)section { + return _styleManager.cellStyle; +} + +#pragma mark - + +- (CGSize)collectionView:(UICollectionView *)collectionView + layout:(UICollectionViewLayout *)collectionViewLayout + sizeForItemAtIndexPath:(NSIndexPath *)indexPath { + UICollectionViewLayoutAttributes *attr = + [collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath]; + CGSize size = [self sizeWithAttribute:attr]; + size = [self inlaidSizeAtIndexPath:indexPath withSize:size]; + return size; +} + +- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView + layout:(UICollectionViewLayout *)collectionViewLayout + insetForSectionAtIndex:(NSInteger)section { + return [self insetsAtSectionIndex:section]; +} + +- (CGFloat)collectionView:(UICollectionView *)collectionView + layout:(UICollectionViewLayout *)collectionViewLayout + minimumLineSpacingForSectionAtIndex:(NSInteger)section { + if ([collectionViewLayout isKindOfClass:[UICollectionViewFlowLayout class]]) { + if (_styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { + return _styleManager.gridPadding; + } + return [(UICollectionViewFlowLayout *)collectionViewLayout minimumLineSpacing]; + } + return 0; +} + +- (CGSize)sizeWithAttribute:(UICollectionViewLayoutAttributes *)attr { + CGFloat height = MDCCellDefaultOneLineHeight; + if ([_styleManager.delegate respondsToSelector: + @selector(collectionView:cellHeightAtIndexPath:)]) { + height = [_styleManager.delegate collectionView:self.collectionView + cellHeightAtIndexPath:attr.indexPath]; + } + + CGFloat width = [self cellWidthAtSectionIndex:attr.indexPath.section]; + return CGSizeMake(width, height); +} + +- (CGFloat)cellWidthAtSectionIndex:(NSInteger)section { + CGFloat bounds = CGRectGetWidth(UIEdgeInsetsInsetRect(self.collectionView.bounds, + self.collectionView.contentInset)); + UIEdgeInsets sectionInsets = [self insetsAtSectionIndex:section]; + CGFloat insets = sectionInsets.left + sectionInsets.right; + if (_styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { + CGFloat cellWidth = + bounds - insets - (_styleManager.gridPadding * (_styleManager.gridColumnCount - 1)); + return cellWidth / _styleManager.gridColumnCount; + } + return bounds - insets; +} + +- (UIEdgeInsets)insetsAtSectionIndex:(NSInteger)section { + // Determine insets based on cell style. + CGFloat inset = (CGFloat)floor(MDCCollectionViewCellStyleCardSectionInset); + UIEdgeInsets insets = UIEdgeInsetsZero; + NSInteger numberOfSections = self.collectionView.numberOfSections; + BOOL isTop = (section == 0); + BOOL isBottom = (section == numberOfSections - 1); + MDCCollectionViewCellStyle cellStyle = [_styleManager cellStyleAtSectionIndex:section]; + BOOL isCardStyle = cellStyle == MDCCollectionViewCellStyleCard; + BOOL isGroupedStyle = cellStyle == MDCCollectionViewCellStyleGrouped; + // Set left/right insets. + if (isCardStyle) { + insets.left = inset; + insets.right = inset; + } + // Set top/bottom insets. + if (isCardStyle || isGroupedStyle) { + insets.top = (CGFloat)floor((isTop) ? inset : inset / 2.0f); + insets.bottom = (CGFloat)floor((isBottom) ? inset : inset / 2.0f); + } + return insets; +} + +- (CGSize)inlaidSizeAtIndexPath:(NSIndexPath *)indexPath + withSize:(CGSize)size { + // If object is inlaid, return its adjusted size. + UICollectionView *collectionView = self.collectionView; + if ([_styleManager isItemInlaidAtIndexPath:indexPath]) { + CGFloat inset = MDCCollectionViewCellStyleCardSectionInset; + UIEdgeInsets inlayInsets = UIEdgeInsetsZero; + BOOL prevCellIsInlaid = NO; + BOOL nextCellIsInlaid = NO; + + BOOL hasSectionHeader = NO; + if ([self respondsToSelector: + @selector(collectionView:layout:referenceSizeForHeaderInSection:)]) { + CGSize headerSize = [self collectionView:collectionView + layout:_collectionViewLayout + referenceSizeForHeaderInSection:indexPath.section]; + hasSectionHeader = !CGSizeEqualToSize(headerSize, CGSizeZero); + } + + BOOL hasSectionFooter = NO; + if ([self respondsToSelector: + @selector(collectionView:layout:referenceSizeForFooterInSection:)]) { + CGSize footerSize = [self collectionView:collectionView + layout:_collectionViewLayout + referenceSizeForFooterInSection:indexPath.section]; + hasSectionFooter = !CGSizeEqualToSize(footerSize, CGSizeZero); + } + + // Check if previous cell is inlaid. + if (indexPath.item > 0 || hasSectionHeader) { + NSIndexPath *prevIndexPath = + [NSIndexPath indexPathForItem:(indexPath.item - 1) + inSection:indexPath.section]; + prevCellIsInlaid = [_styleManager isItemInlaidAtIndexPath:prevIndexPath]; + inlayInsets.top = prevCellIsInlaid ? inset / 2 : inset; + } + + // Check if next cell is inlaid. + if (indexPath.item < [collectionView numberOfItemsInSection:indexPath.section] - 1 || + hasSectionFooter) { + NSIndexPath *nextIndexPath = + [NSIndexPath indexPathForItem:(indexPath.item + 1) + inSection:indexPath.section]; + nextCellIsInlaid = [_styleManager isItemInlaidAtIndexPath:nextIndexPath]; + inlayInsets.bottom = nextCellIsInlaid ? inset / 2 : inset; + } + + // Apply top/bottom height adjustments to inlaid object. + size.height += inlayInsets.top + inlayInsets.bottom; + } + return size; +} + +#pragma mark - + +- (BOOL)inkTouchControllerShouldProcessInkTouches:(MDCInkTouchController *)inkTouchController + atTouchLocation:(CGPoint)location { + NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:location]; + if (indexPath) { + if ([_styleManager.delegate respondsToSelector: + @selector(collectionView:hidesInkViewAtIndexPath:)]) { + return [_styleManager.delegate collectionView:self.collectionView + hidesInkViewAtIndexPath:indexPath]; + } + } + return YES; +} + +- (MDCInkView *)inkTouchController:(MDCInkTouchController *)inkTouchController + inkViewAtTouchLocation:(CGPoint)location { + NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:location]; + UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath]; + + if ([cell isKindOfClass:[MDCCollectionViewCell class]]) { + MDCCollectionViewCell *inkCell = (MDCCollectionViewCell *)cell; + if ([inkCell respondsToSelector:@selector(inkView)]) { + // Set cell ink. + MDCInkView *ink = [cell performSelector:@selector(inkView)]; + if (!ink) { + ink = [[MDCInkView alloc] initWithFrame:inkCell.bounds]; + inkCell.inkView = ink; + } + return ink; + } + } + return nil; +} + +#pragma mark - + +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView + viewForSupplementaryElementOfKind:(NSString *)kind + atIndexPath:(NSIndexPath *)indexPath { + // Editing info bar. + if ([kind isEqualToString:MDCCollectionInfoBarKindHeader] || + [kind isEqualToString:MDCCollectionInfoBarKindFooter]) { + NSString *identifier = NSStringFromClass([MDCCollectionInfoBarView class]); + identifier = [identifier stringByAppendingFormat:@".%@", kind]; + [collectionView registerClass:[MDCCollectionInfoBarView class] + forSupplementaryViewOfKind:kind + withReuseIdentifier:identifier]; + + UICollectionReusableView *supplementaryView = + [collectionView dequeueReusableSupplementaryViewOfKind:kind + withReuseIdentifier:identifier + forIndexPath:indexPath]; + + // Update info bar. + if ([supplementaryView isKindOfClass:[MDCCollectionInfoBarView class]]) { + MDCCollectionInfoBarView *infoBar = (MDCCollectionInfoBarView *)supplementaryView; + infoBar.delegate = self; + infoBar.kind = kind; + [self updateControllerWithInfoBar:infoBar]; + } + return supplementaryView; + } + return nil; +} + +#pragma mark - + +- (BOOL)collectionView:(UICollectionView *)collectionView + shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath { + if (_editingManager.isEditing) { + if ([self collectionView:collectionView canEditItemAtIndexPath:indexPath]) { + return [self collectionView:collectionView canSelectItemDuringEditingAtIndexPath:indexPath]; + } + return NO; + } + return YES; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath { + return collectionView.allowsMultipleSelection; +} + +- (void)collectionView:(UICollectionView *)collectionView + didSelectItemAtIndexPath:(NSIndexPath *)indexPath { + [self updateFooterInfoBarIfNecessary]; +} + +- (void)collectionView:(UICollectionView *)collectionView + didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { + [self updateFooterInfoBarIfNecessary]; +} + +#pragma mark - + +- (BOOL)collectionViewAllowsEditing:(UICollectionView *)collectionView { + return NO; +} + +- (void)collectionViewWillBeginEditing:(UICollectionView *)collectionView { + // Inlay all items. + _styleManager.allowsItemInlay = YES; + _styleManager.allowsMultipleItemInlays = YES; + [_styleManager applyInlayToAllItemsAnimated:YES]; + [self updateHeaderInfoBarIfNecessary]; +} + +- (void)collectionViewWillEndEditing:(UICollectionView *)collectionView { + // Remove inlay of all items. + [_styleManager removeInlayFromAllItemsAnimated:YES]; + [self updateFooterInfoBarIfNecessary]; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + canEditItemAtIndexPath:(NSIndexPath *)indexPath { + return [self collectionViewAllowsEditing:collectionView]; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + canSelectItemDuringEditingAtIndexPath:(NSIndexPath *)indexPath { + if ([self collectionViewAllowsEditing:collectionView]) { + return [self collectionView:collectionView canEditItemAtIndexPath:indexPath]; + } + return NO; +} + +#pragma mark - Item Moving + +- (BOOL)collectionViewAllowsReordering:(UICollectionView *)collectionView { + return NO; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + canMoveItemAtIndexPath:(NSIndexPath *)indexPath { + return ([self collectionViewAllowsEditing:collectionView] && + [self collectionViewAllowsReordering:collectionView]); +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + canMoveItemAtIndexPath:(NSIndexPath *)indexPath + toIndexPath:(NSIndexPath *)newIndexPath { + // First ensure both source and target items can be moved. + return ([self collectionView:collectionView canMoveItemAtIndexPath:indexPath] && + [self collectionView:collectionView + canMoveItemAtIndexPath:newIndexPath]); +} + +- (void)collectionView:(UICollectionView *)collectionView + didMoveItemAtIndexPath:(NSIndexPath *)indexPath + toIndexPath:(NSIndexPath *)newIndexPath { + [collectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath]; +} + +#pragma mark - Swipe-To-Dismiss-Items + +- (BOOL)collectionViewAllowsSwipeToDismissItem:(UICollectionView *)collectionView { + return NO; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + canSwipeToDismissItemAtIndexPath:(NSIndexPath *)indexPath { + return [self collectionViewAllowsSwipeToDismissItem:collectionView]; +} + +- (void)collectionView:(UICollectionView *)collectionView + didEndSwipeToDismissItemAtIndexPath:(NSIndexPath *)indexPath { + [self deleteIndexPaths:@[ indexPath ]]; +} + +#pragma mark - Swipe-To-Dismiss-Sections + +- (BOOL)collectionViewAllowsSwipeToDismissSection:(UICollectionView *)collectionView { + return NO; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView + canSwipeToDismissSection:(NSInteger)section { + return [self collectionViewAllowsSwipeToDismissSection:collectionView]; +} + +- (void)collectionView:(UICollectionView *)collectionView + didEndSwipeToDismissSection:(NSInteger)section { + [self deleteSections:[NSIndexSet indexSetWithIndex:section]]; +} + +#pragma mark - Private + +- (void)deleteIndexPaths:(NSArray *)indexPaths { + if ([self respondsToSelector:@selector(collectionView:willDeleteItemsAtIndexPaths:)]) { + void (^batchUpdates)() = ^{ + // Notify delegate to delete data. + [self collectionView:self.collectionView willDeleteItemsAtIndexPaths:indexPaths]; + + // Delete index paths. + [self.collectionView deleteItemsAtIndexPaths:indexPaths]; + }; + + void (^completionBlock)(BOOL finished) = ^(BOOL finished) { + [self updateFooterInfoBarIfNecessary]; + // Notify delegate of deletion. + if ([self respondsToSelector:@selector(collectionView:didDeleteItemsAtIndexPaths:)]) { + [self collectionView:self.collectionView didDeleteItemsAtIndexPaths:indexPaths]; + } + }; + + // Animate deletion. + [self.collectionView performBatchUpdates:batchUpdates completion:completionBlock]; + } +} + +- (void)deleteSections:(NSIndexSet *)sections { + if ([self respondsToSelector:@selector(collectionView:willDeleteSections:)]) { + void (^batchUpdates)() = ^{ + // Notify delegate to delete data. + [self collectionView:self.collectionView willDeleteSections:sections]; + + // Delete sections. + [self.collectionView deleteSections:sections]; + }; + + void (^completionBlock)(BOOL finished) = ^(BOOL finished) { + [self updateFooterInfoBarIfNecessary]; + // Notify delegate of deletion. + if ([self respondsToSelector:@selector(collectionView:didDeleteSections:)]) { + [self collectionView:self.collectionView didDeleteSections:sections]; + } + }; + + // Animate deletion. + [self.collectionView performBatchUpdates:batchUpdates completion:completionBlock]; + } +} + +- (void)updateHeaderInfoBarIfNecessary { + if (_editingManager.isEditing) { + // Show HUD only once before autodissmissing. + BOOL allowsSwipeToDismissItem = NO; + if ([self respondsToSelector:@selector(collectionViewAllowsSwipeToDismissItem:)]) { + allowsSwipeToDismissItem = [self collectionViewAllowsSwipeToDismissItem:self.collectionView]; + } + + if (!_headerInfoBar.isVisible && !_headerInfoBarDismissed && allowsSwipeToDismissItem) { + [_headerInfoBar showAnimated:YES]; + } else { + [_headerInfoBar dismissAnimated:YES]; + } + } +} + +- (void)updateFooterInfoBarIfNecessary { + NSInteger selectedItemCount = [self.collectionView.indexPathsForSelectedItems count]; + if (_editingManager.isEditing) { + // Invalidate layout to add info bar if necessary. + [self.collectionView.collectionViewLayout invalidateLayout]; + if (_footerInfoBar) { + if (selectedItemCount > 0 && !_footerInfoBar.isVisible) { + [_footerInfoBar showAnimated:YES]; + } else if (selectedItemCount == 0 && _footerInfoBar.isVisible) { + [_footerInfoBar dismissAnimated:YES]; + } + } + } else if (selectedItemCount == 0 && _footerInfoBar.isVisible) { + [_footerInfoBar dismissAnimated:YES]; + } +} + +- (void)updateContentWithBottomInset:(CGFloat)inset { + // Update bottom inset to account for footer info bar. + UIEdgeInsets contentInset = self.collectionView.contentInset; + contentInset.bottom += inset; + [UIView animateWithDuration:MDCCollectionInfoBarAnimationDuration + animations:^{ + self.collectionView.contentInset = contentInset; + }]; +} + +@end diff --git a/components/Collections/src/MDCCollectionViewEditingManager.h b/components/Collections/src/MDCCollectionViewEditingManager.h new file mode 100644 index 00000000000..c47af6f2b1d --- /dev/null +++ b/components/Collections/src/MDCCollectionViewEditingManager.h @@ -0,0 +1,81 @@ +/* + 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 + +@protocol MDCCollectionViewEditingManagerDelegate; + +/** + The MDCCollectionViewEditingManager class provides an implementation for a UICollectionView to + set its editing properties. + */ +@interface MDCCollectionViewEditingManager : NSObject + +/** Unavailable superclass initializers. */ +- (nonnull instancetype)init NS_UNAVAILABLE; +- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder NS_UNAVAILABLE; + +/** + Initialize the controller with a collection view. + + Designated initializer. + + @param collectionView The controller's collection view. + */ +- (nonnull instancetype)initWithCollectionView:(nonnull UICollectionView *)collectionView + NS_DESIGNATED_INITIALIZER; + +/** The controller's collection view. */ +@property(nonatomic, readonly, weak, nullable) UICollectionView *collectionView; + +/** + A delegate through which the MDCCollectionViewEditingManager may inform of changes in status. + */ +@property(nonatomic, weak, nullable) id delegate; + +/** + The index path of a cell that is currently being moved/reordered within a collection View. + */ +@property(nonatomic, readonly, strong, nullable) NSIndexPath *reorderingCellIndexPath; + +/** + The index path of a cell that is currently being dragged for dismissal within a collection View. + */ +@property(nonatomic, readonly, strong, nullable) NSIndexPath *dismissingCellIndexPath; + +/** The section being dragged for dismissal within a collection View. */ +@property(nonatomic, readonly, assign) NSInteger dismissingSection; + +/** + A boolean value indicating whether the a visible cell within the collectionView is being + edited. When set, all rows show or hide editing controls without animation. To animate the + state change see @c setEditing:animated:. Setting the editing state of this class does not + propagate to the parent view controller's editing state. + */ +@property(nonatomic, getter=isEditing) BOOL editing; + +/** + Set the editing state with optional animations. + + When set, row shows or hides editing controls with/without animation. Setting the editing + state of this class does not propagate to the parent view controller's editing state. + + @param editing YES if editing; otherwise, NO. + @param animated YES the transition will be animated; otherwise, NO. + */ +- (void)setEditing:(BOOL)editing animated:(BOOL)animated; + +@end diff --git a/components/Collections/src/MDCCollectionViewEditingManager.m b/components/Collections/src/MDCCollectionViewEditingManager.m new file mode 100644 index 00000000000..2f65989001c --- /dev/null +++ b/components/Collections/src/MDCCollectionViewEditingManager.m @@ -0,0 +1,734 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "MDCCollectionViewEditingManager.h" + +#import "MDCCollectionViewEditingManagerDelegate.h" +#import "MaterialShadowLayer.h" + +#import + +// Distance from center before we start fading the item. +static const CGFloat kMDCDismissalDistanceBeforeFading = 50.0f; + +// Minimum alpha for an item being dismissed. +static const CGFloat kMDCDismissalMinimumAlpha = 0.5f; + +// Final angle of the arc the item travels through during dismissal. +static const CGFloat kMDCDismissalArcAngle = (CGFloat)(M_PI / 6.0f); + +// The centroid for the item dismissal arc. +static const CGFloat kMDCDismissalArcYOffset = 400.0f; + +// Simple linear friction applied to swipe velocity. +static const CGFloat kMDCDismissalSwipeFriction = 0.05f; + +// Animation duration for dismissal / restore. +static const NSTimeInterval kMDCDismissalAnimationDuration = 0.3; +static const NSTimeInterval kMDCRestoreAnimationDuration = 0.2; + +/** A view that uses an MDCShadowLayer as its sublayer. */ +@interface ShadowedSnapshotView : UIView +@end + +@implementation ShadowedSnapshotView ++ (Class)layerClass { + return [MDCShadowLayer class]; +} +@end + +@interface MDCCollectionViewEditingManager () +@end + +@implementation MDCCollectionViewEditingManager { + UILongPressGestureRecognizer *_longPressGestureRecognizer; + UIPanGestureRecognizer *_panGestureRecognizer; + CGPoint _selectedCellLocation; + CGPoint _initialCellLocation; + ShadowedSnapshotView *_cellSnapshot; +} + +#pragma mark - Public + +- (instancetype)init { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (instancetype)initWithCollectionView:(UICollectionView *)collectionView { + self = [super init]; + if (self) { + _collectionView = collectionView; + _dismissingSection = NSNotFound; + + // Setup gestures to handle collectionView editing. + + SEL longPressSelector = @selector(handleLongPressGesture:); + _longPressGestureRecognizer = + [[UILongPressGestureRecognizer alloc] initWithTarget:self + action:longPressSelector]; + _longPressGestureRecognizer.delegate = self; + [_collectionView addGestureRecognizer:_longPressGestureRecognizer]; + + SEL panSelector = @selector(handlePanGesture:); + _panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:panSelector]; + _panGestureRecognizer.delegate = self; + [_collectionView addGestureRecognizer:_panGestureRecognizer]; + } + return self; +} + +- (void)dealloc { + _longPressGestureRecognizer.delegate = nil; + _panGestureRecognizer.delegate = nil; + + // Remove gesture recognizers to prevent duplicates when re-initializing this controller. + [_collectionView removeGestureRecognizer:_longPressGestureRecognizer]; + [_collectionView removeGestureRecognizer:_panGestureRecognizer]; +} + +- (void)setEditing:(BOOL)editing { + [self setEditing:editing animated:NO]; +} + +- (void)setEditing:(BOOL)editing animated:(BOOL)animated { + _editing = editing; + _collectionView.allowsMultipleSelection = editing; + + // Clear any selected indexPaths. + for (NSIndexPath *indexPath in [_collectionView indexPathsForSelectedItems]) { + [_collectionView deselectItemAtIndexPath:indexPath animated:NO]; + } + + [CATransaction begin]; + if (editing) { + // Notify delegate will begin editing. + if ([_delegate respondsToSelector:@selector(collectionViewWillBeginEditing:)]) { + [_delegate collectionViewWillBeginEditing:_collectionView]; + } + + [CATransaction setCompletionBlock:^{ + // Notify delegate did begin editing. + if ([_delegate respondsToSelector:@selector(collectionViewDidBeginEditing:)]) { + [_delegate collectionViewDidBeginEditing:_collectionView]; + } + }]; + + } else { + // Notify delegate will end editing. + if ([_delegate respondsToSelector:@selector(collectionViewWillEndEditing:)]) { + [_delegate collectionViewWillEndEditing:_collectionView]; + } + + [CATransaction setCompletionBlock:^{ + // Notify delegate did end editing. + if ([_delegate respondsToSelector:@selector(collectionViewDidEndEditing:)]) { + [_delegate collectionViewDidEndEditing:_collectionView]; + } + }]; + } + [CATransaction commit]; +} + +#pragma mark - Private + +- (NSArray *)attributesAtSection:(NSInteger)section { + UICollectionViewLayout *layout = _collectionView.collectionViewLayout; + NSIndexPath *indexPath; + NSMutableArray *sectionAttributes = [NSMutableArray array]; + + // Get all item attributes at section. + NSInteger numberOfItemsInSection = [_collectionView numberOfItemsInSection:section]; + + for (NSInteger i = 0; i < numberOfItemsInSection; ++i) { + indexPath = [NSIndexPath indexPathForItem:i inSection:section]; + UICollectionViewLayoutAttributes *attribute = + [layout layoutAttributesForItemAtIndexPath:indexPath]; + [sectionAttributes addObject:attribute]; + } + + // Headers/footers require section but ignore item of index path, so set to zero. + indexPath = [NSIndexPath indexPathForItem:0 inSection:section]; + + // Get header attributes at section. + UICollectionViewLayoutAttributes *headerAttribute = + [layout layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader + atIndexPath:indexPath]; + [sectionAttributes addObject:headerAttribute]; + + // Get footer attributes at section. + UICollectionViewLayoutAttributes *footerAttribute = + [layout layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter + atIndexPath:indexPath]; + [sectionAttributes addObject:footerAttribute]; + + return sectionAttributes; +} + +- (NSInteger)swipedSectionAtLocation:(CGPoint)location { + // Returns section index being swiped for dismissal, or NSNotFound for an invalid swipes + // where location does not return an item or section header/footer. + CGRect contentFrame = _collectionView.frame; + contentFrame.size = _collectionView.contentSize; + NSArray *visibleAttributes = + [_collectionView.collectionViewLayout layoutAttributesForElementsInRect:contentFrame]; + for (UICollectionViewLayoutAttributes *attribute in visibleAttributes) { + if (!CGRectIsNull(attribute.frame) && CGRectContainsPoint(attribute.frame, location)) { + return attribute.indexPath.section; + } + } + return NSNotFound; +} + +#pragma mark - Snapshotting + +- (UIView *)snapshotWithIndexPath:(NSIndexPath *)indexPath { + // Here we will take a snapshot of the collectionView item. + if (_cellSnapshot) { + [_cellSnapshot removeFromSuperview]; + _cellSnapshot = nil; + } + + // Create snapshot. + UICollectionViewLayoutAttributes *attributes = + [_collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath]; + _cellSnapshot = [[ShadowedSnapshotView alloc] initWithFrame:attributes.frame]; + UICollectionViewCell *cell = [_collectionView cellForItemAtIndexPath:indexPath]; + [_cellSnapshot addSubview:[cell snapshotViewAfterScreenUpdates:NO]]; + + // Invalidate layout here to force attributes to now be hidden. + [_collectionView.collectionViewLayout invalidateLayout]; + return _cellSnapshot; +} + +- (UIView *)snapshotWithSection:(NSInteger)section { + // Here we will take a snapshot of the collectionView section items, header, and footer. + if (_cellSnapshot) { + [_cellSnapshot removeFromSuperview]; + _cellSnapshot = nil; + } + + // The snapshot frame encompasses all of the section items, header, and footer attribute frames. + NSArray *sectionAttributes = [self attributesAtSection:section]; + CGRect snapshotFrame = CGRectNull; + for (UICollectionViewLayoutAttributes *attribute in sectionAttributes) { + if (!CGRectIsNull(attribute.frame)) { + snapshotFrame = CGRectUnion(snapshotFrame, attribute.frame); + } + } + + // Create snapshot. + _cellSnapshot = [[ShadowedSnapshotView alloc] initWithFrame:snapshotFrame]; + UIImageView *snapshotView = + [[UIImageView alloc] initWithImage:[self snapshotWithRect:snapshotFrame]]; + [_cellSnapshot addSubview:snapshotView]; + + // Invalidate layout here to force attributes to now be hidden. + [_collectionView.collectionViewLayout invalidateLayout]; + return _cellSnapshot; +} + +- (UIImage *)snapshotWithRect:(CGRect)rect { + // Here we will take a snapshot of a rect within the collectionView. + UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0); + CGContextRef cx = UIGraphicsGetCurrentContext(); + CGContextTranslateCTM(cx, -rect.origin.x, -rect.origin.y); + + // Save original collection view properties. + id savedDelegate = _collectionView.delegate; + _collectionView.delegate = nil; + CGPoint savedContentOffset = _collectionView.contentOffset; + BOOL savedClipsToBounds = _collectionView.clipsToBounds; + _collectionView.clipsToBounds = NO; + + // Hide any scroll indicators. + BOOL showsHorizontalScrollIndicator = _collectionView.showsHorizontalScrollIndicator; + BOOL showsVerticalScrollIndicator = _collectionView.showsVerticalScrollIndicator; + _collectionView.showsHorizontalScrollIndicator = NO; + _collectionView.showsVerticalScrollIndicator = NO; + + // Render snapshot. + [_collectionView.layer renderInContext:cx]; + _collectionView.layer.rasterizationScale = [UIScreen mainScreen].scale; + _collectionView.layer.shouldRasterize = YES; + UIImage *screenshotImage = UIGraphicsGetImageFromCurrentImageContext(); + + // Reset collection view. + _collectionView.contentOffset = savedContentOffset; + _collectionView.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator; + _collectionView.showsVerticalScrollIndicator = showsVerticalScrollIndicator; + _collectionView.delegate = savedDelegate; + _collectionView.clipsToBounds = savedClipsToBounds; + _collectionView.layer.shouldRasterize = NO; + + UIGraphicsEndImageContext(); + return screenshotImage; +} + +- (void)applyLayerShadowing:(CALayer *)layer { + MDCShadowLayer *shadowLayer = (MDCShadowLayer *)_cellSnapshot.layer; + shadowLayer.shadowMaskEnabled = NO; + shadowLayer.elevation = 3; +} + +#pragma mark - UIGestureRecognizerDelegate + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { + if ([gestureRecognizer isEqual:_longPressGestureRecognizer]) { + // Only allow longpress if collectionView is editing. + return self.isEditing; + + } else if ([gestureRecognizer isEqual:_panGestureRecognizer]) { + // Only allow panning if collectionView is editing or dismissing item/section. + BOOL allowsSwipeToDismissItem = NO; + if ([_delegate respondsToSelector:@selector(collectionViewAllowsSwipeToDismissItem:)]) { + allowsSwipeToDismissItem = [_delegate collectionViewAllowsSwipeToDismissItem:_collectionView]; + } + + BOOL allowsSwipeToDismissSection = NO; + if ([_delegate respondsToSelector:@selector(collectionViewAllowsSwipeToDismissSection:)]) { + allowsSwipeToDismissSection = + [_delegate collectionViewAllowsSwipeToDismissSection:_collectionView]; + } + return (self.isEditing || allowsSwipeToDismissItem || allowsSwipeToDismissSection); + } + return YES; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer + shouldRecognizeSimultaneouslyWithGestureRecognizer: + (UIGestureRecognizer *)otherGestureRecognizer { + return YES; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer + shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { + // Prevent panning to dismiss when scrolling. + return ([gestureRecognizer isEqual:_panGestureRecognizer] && + [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]); +} + +#pragma mark - LongPress Gesture Handling + +- (void)handleLongPressGesture:(UILongPressGestureRecognizer *)recognizer { + switch (recognizer.state) { + case UIGestureRecognizerStateBegan: { + _initialCellLocation = [recognizer locationInView:_collectionView]; + _selectedCellLocation = [recognizer locationInView:_collectionView]; + _reorderingCellIndexPath = [_collectionView indexPathForItemAtPoint:_selectedCellLocation]; + + if ([_delegate respondsToSelector:@selector(collectionView:canMoveItemAtIndexPath:)] && + ![_delegate collectionView:_collectionView + canMoveItemAtIndexPath:_reorderingCellIndexPath]) { + _reorderingCellIndexPath = nil; + return; + } + + // Notify delegate dragging has began. + if ([_delegate respondsToSelector: + @selector(collectionView:willBeginDraggingItemAtIndexPath:)]) { + [_delegate collectionView:_collectionView + willBeginDraggingItemAtIndexPath:_reorderingCellIndexPath]; + } + + // Create cell snapshot with shadowing. + [_collectionView addSubview:[self snapshotWithIndexPath:_reorderingCellIndexPath]]; + [self applyLayerShadowing:_cellSnapshot.layer]; + + // Disable scrolling. + [_collectionView setScrollEnabled:NO]; + break; + } + case UIGestureRecognizerStateCancelled: + case UIGestureRecognizerStateEnded: { + NSIndexPath *currentIndexPath = _reorderingCellIndexPath; + if (currentIndexPath) { + UICollectionViewLayoutAttributes *attributes = + [_collectionView.collectionViewLayout + layoutAttributesForItemAtIndexPath:currentIndexPath]; + + void (^completionBlock)(BOOL finished) = ^(BOOL finished) { + // Notify delegate dragging has finished. + if ([_delegate respondsToSelector: + @selector(collectionView:didEndDraggingItemAtIndexPath:)]) { + [_delegate collectionView:_collectionView + didEndDraggingItemAtIndexPath:_reorderingCellIndexPath]; + } + [self restoreEditingItem]; + + // Re-enable scrolling. + [_collectionView setScrollEnabled:YES]; + }; + + [UIView animateWithDuration:kMDCDismissalAnimationDuration + delay:0 + options:UIViewAnimationOptionBeginFromCurrentState + animations:^{ + _cellSnapshot.frame = attributes.frame; + } + completion:completionBlock]; + } + break; + } + default: + break; + } +} + +#pragma mark - Pan Gesture Handling + +- (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer { + if (_reorderingCellIndexPath) { + [self panToReorderWithRecognizer:recognizer]; + } else { + [self panToDismissWithRecognizer:recognizer]; + } +} + +- (void)panToReorderWithRecognizer:(UIPanGestureRecognizer *)recognizer { + if (recognizer.state == UIGestureRecognizerStateChanged) { + // Transform snapshot position when panning. + _selectedCellLocation = [recognizer locationInView:_collectionView]; + CGFloat change = _selectedCellLocation.y - _initialCellLocation.y; + CGAffineTransform transform = CGAffineTransformMakeTranslation(0, change); + _cellSnapshot.layer.transform = CATransform3DMakeAffineTransform(transform); + + // Determine moved index paths. + NSIndexPath *newIndexPath = [_collectionView indexPathForItemAtPoint:_selectedCellLocation]; + NSIndexPath *previousIndexPath = _reorderingCellIndexPath; + if ((newIndexPath == nil) || [newIndexPath isEqual:previousIndexPath]) { + return; + } + + // Check delegate for permission to move item. + if ([_delegate respondsToSelector: + @selector(collectionView:canMoveItemAtIndexPath:toIndexPath:)]) { + if ([_delegate collectionView:_collectionView + canMoveItemAtIndexPath:previousIndexPath + toIndexPath:newIndexPath]) { + _reorderingCellIndexPath = newIndexPath; + + // Notify delegate that item will move. + if ([_delegate respondsToSelector: + @selector(collectionView:willMoveItemAtIndexPath:toIndexPath:)]) { + [_delegate collectionView:_collectionView + willMoveItemAtIndexPath:previousIndexPath + toIndexPath:newIndexPath]; + + // Notify delegate item did move. + if ([_delegate respondsToSelector: + @selector(collectionView:didMoveItemAtIndexPath:toIndexPath:)]) { + [_delegate collectionView:_collectionView + didMoveItemAtIndexPath:previousIndexPath + toIndexPath:newIndexPath]; + } + } + } else { + // Exit if delegate will not allow this indexPath to move. + return; + } + } + } +} + +- (void)panToDismissWithRecognizer:(UIPanGestureRecognizer *)recognizer { + CGPoint translation = [recognizer translationInView:_collectionView]; + CGPoint velocity = [recognizer velocityInView:_collectionView]; + CGPoint location = [recognizer locationInView:_collectionView]; + + switch (recognizer.state) { + case UIGestureRecognizerStateBegan: { + if (fabs(velocity.y) > fabs(velocity.x)) { + // Exit if panning vertically. + return [self exitPanToDismissWithRecognizer:recognizer]; + } + + BOOL allowsSwipeToDismissSection = NO; + if ([_delegate respondsToSelector:@selector(collectionViewAllowsSwipeToDismissSection:)]) { + allowsSwipeToDismissSection = + [_delegate collectionViewAllowsSwipeToDismissSection:_collectionView]; + } + + BOOL allowsSwipeToDismissItem = NO; + if ([_delegate respondsToSelector:@selector(collectionViewAllowsSwipeToDismissItem:)]) { + allowsSwipeToDismissItem = + [_delegate collectionViewAllowsSwipeToDismissItem:_collectionView]; + } + + if (allowsSwipeToDismissSection && !self.isEditing) { + // Determine panned section to dismiss. + _dismissingSection = [self swipedSectionAtLocation:location]; + if (_dismissingSection == NSNotFound) { + return [self exitPanToDismissWithRecognizer:recognizer]; + } + + // Check delegate for permission to swipe section. + if ([_delegate respondsToSelector:@selector(collectionView:canSwipeToDismissSection:)]) { + if ([_delegate collectionView:_collectionView + canSwipeToDismissSection:_dismissingSection]) { + // Notify delegate. + if ([_delegate respondsToSelector: + @selector(collectionView:willBeginSwipeToDismissSection:)]) { + [_delegate collectionView:_collectionView + willBeginSwipeToDismissSection:_dismissingSection]; + } + } else { + // Cannot swipe so exit. + return [self exitPanToDismissWithRecognizer:recognizer]; + } + } + + // Create section snapshot. + [_collectionView insertSubview:[self snapshotWithSection:_dismissingSection] atIndex:0]; + break; + + } else if (allowsSwipeToDismissItem) { + // Determine panned index path to dismiss. + _dismissingCellIndexPath = [_collectionView indexPathForItemAtPoint:location]; + if (!_dismissingCellIndexPath) { + return [self exitPanToDismissWithRecognizer:recognizer]; + } + + // Check delegate for permission to swipe item. + if ([_delegate respondsToSelector: + @selector(collectionView:canSwipeToDismissItemAtIndexPath:)]) { + if ([_delegate collectionView:_collectionView + canSwipeToDismissItemAtIndexPath:_dismissingCellIndexPath]) { + // Notify delegate. + if ([_delegate respondsToSelector: + @selector(collectionView:willBeginSwipeToDismissItemAtIndexPath:)]) { + [_delegate collectionView:_collectionView + willBeginSwipeToDismissItemAtIndexPath:_dismissingCellIndexPath]; + } + } else { + // Cannot swipe so exit. + return [self exitPanToDismissWithRecognizer:recognizer]; + } + } + + // Create item snapshot. + [_collectionView insertSubview:[self snapshotWithIndexPath:_dismissingCellIndexPath] + atIndex:0]; + break; + } + } + + case UIGestureRecognizerStateChanged: { + // Update the tracked item's position and alpha. + CGAffineTransform transform; + CGFloat alpha; + // The item is fully opaque until it pans at least |kMDCDismissalDistanceBeforeFading| points. + CGFloat panDistance = (CGFloat)fabs(translation.x) - kMDCDismissalDistanceBeforeFading; + if (panDistance > 0) { + transform = [self transformItemDismissalToTranslationX:translation.x]; + alpha = [self dismissalAlphaForTranslationX:translation.x]; + } else { + // Pan the item. + transform = CGAffineTransformMakeTranslation(translation.x, 0); + alpha = 1; + } + _cellSnapshot.layer.transform = CATransform3DMakeAffineTransform(transform); + _cellSnapshot.alpha = alpha; + break; + } + + case UIGestureRecognizerStateEnded: { + // Check the final translation, including the final velocity, to determine + // if the item should be dismissed. + CGFloat momentumX = velocity.x * kMDCDismissalSwipeFriction; + CGFloat translationX = translation.x + momentumX; + + if (fabs(translationX) > [self distanceThresholdForDismissal]) { + // |translationX| is only guaranteed to be over the dismissal threshold; + // make sure the view animates all the way off the screen. + translationX = (CGFloat)copysign(MAX(fabs(translationX), + CGRectGetWidth(_collectionView.bounds)), + translationX); + [self animateFinalItemDismissalToTranslationX:translationX]; + } else { + [self restorePanningItemIfNecessaryWithMomentumX:momentumX]; + } + break; + } + default: { + [self restorePanningItemIfNecessaryWithMomentumX:0]; + break; + } + } +} + +- (void)exitPanToDismissWithRecognizer:(UIPanGestureRecognizer *)recognizer { + // To exit, disable the recognizer immediately which forces it to drop out of the current + // loop and prevent any state updates. Then re-enable to allow future panning. + recognizer.enabled = NO; + recognizer.enabled = YES; +} + +#pragma mark - Dismissal animation. + +- (CGAffineTransform)transformItemDismissalToTranslationX:(CGFloat)translationX { + // Returns a transform that can be applied to the snapshot during dismissal. The + // translation will pan along an arc facing downwards. + CGFloat panDistance = (CGFloat)fabs(translationX) - kMDCDismissalDistanceBeforeFading; + CGFloat panRatio = panDistance / CGRectGetWidth(_collectionView.bounds); + CGFloat arcAngle = (translationX > 0 ? kMDCDismissalArcAngle : -kMDCDismissalArcAngle); + + CGAffineTransform initialTranslation = + CGAffineTransformMakeTranslation(0, -kMDCDismissalArcYOffset); + CGAffineTransform rotation = CGAffineTransformMakeRotation(panRatio * arcAngle); + + // Modify the X translation to take account of the rotation angle. + // This makes the item continue to track the finger as it follows the arc. + CGFloat correctedXTranslation = (CGFloat)(translationX * (1 - cos(kMDCDismissalArcAngle) / 2)); + if (translationX > 0) { + correctedXTranslation = MAX(kMDCDismissalDistanceBeforeFading, correctedXTranslation); + } else { + correctedXTranslation = MIN(-kMDCDismissalDistanceBeforeFading, correctedXTranslation); + } + + // Reverse |initialTranslation| and add |translation.x| to ensure that + // we pan along with the arc. + CGAffineTransform reverseTranslation = + CGAffineTransformMakeTranslation(correctedXTranslation, kMDCDismissalArcYOffset); + + CGAffineTransform arcTransform = CGAffineTransformConcat(initialTranslation, rotation); + return CGAffineTransformConcat(arcTransform, reverseTranslation); +} + +- (void)animateFinalItemDismissalToTranslationX:(CGFloat)translationX { + // Called at the end of a pan gesture that results in the item being dismissed. + // Animation that moves the dismissed item to the final location and fades it out. + CGAffineTransform transform = [self transformItemDismissalToTranslationX:translationX]; + + // Notify delegate of dismissed section. + if (_dismissingSection != NSNotFound) { + if ([_delegate respondsToSelector:@selector(collectionView:didEndSwipeToDismissSection:)]) { + [_delegate collectionView:_collectionView didEndSwipeToDismissSection:_dismissingSection]; + } + } + + // Notify delegate of dismissed item. + if (_dismissingCellIndexPath) { + if ([_delegate respondsToSelector: + @selector(collectionView:didEndSwipeToDismissItemAtIndexPath:)]) { + [_delegate collectionView:_collectionView + didEndSwipeToDismissItemAtIndexPath:_dismissingCellIndexPath]; + } + } + + [UIView animateWithDuration:kMDCDismissalAnimationDuration + delay:0 + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + _cellSnapshot.layer.transform = CATransform3DMakeAffineTransform(transform); + _cellSnapshot.alpha = 0; + } + completion:^(BOOL finished) { + [self restoreEditingItem]; + }]; +} + +- (void)restorePanningItemIfNecessaryWithMomentumX:(CGFloat)momentumX { + // If we never had a snapshot, or the snapshot never moved, then skip straight to cleanup. + if (_cellSnapshot == nil || CGAffineTransformIsIdentity(_cellSnapshot.transform)) { + [self restoreEditingItem]; + return; + } + + CAAnimationGroup *allAnimations = [CAAnimationGroup animation]; + allAnimations.duration = kMDCRestoreAnimationDuration; + + CATransform3D startTransform = CATransform3DMakeAffineTransform(_cellSnapshot.transform); + CATransform3D midTransform = CATransform3DTranslate(startTransform, momentumX, 0, 0); + CATransform3D endTransform = CATransform3DIdentity; + + CAKeyframeAnimation *transformAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"]; + transformAnimation.values = @[ + [NSValue valueWithCATransform3D:startTransform], + [NSValue valueWithCATransform3D:midTransform], + [NSValue valueWithCATransform3D:endTransform] + ]; + transformAnimation.calculationMode = kCAAnimationCubicPaced; + + CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + opacityAnimation.fromValue = @(_cellSnapshot.alpha); + opacityAnimation.toValue = @1; + + allAnimations.animations = @[ transformAnimation, opacityAnimation ]; + allAnimations.delegate = self; + allAnimations.fillMode = kCAFillModeBackwards; + _cellSnapshot.layer.transform = CATransform3DIdentity; + _cellSnapshot.alpha = 1; + [_cellSnapshot.layer addAnimation:allAnimations forKey:nil]; +} + +- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)didFinish { + [self cancelPanningItem]; +} + +- (void)cancelPanningItem { + // Notify delegate of panned section cancellation. + if (_dismissingSection != NSNotFound) { + if ([_delegate respondsToSelector:@selector(collectionView:didCancelSwipeToDismissSection:)]) { + [_delegate collectionView:_collectionView didCancelSwipeToDismissSection:_dismissingSection]; + } + } + + // Notify delegate of panned index path cancellation. + if (_dismissingCellIndexPath) { + if ([_delegate respondsToSelector: + @selector(collectionView:didCancelSwipeToDismissItemAtIndexPath:)]) { + [_delegate collectionView:_collectionView + didCancelSwipeToDismissItemAtIndexPath:_dismissingCellIndexPath]; + } + } + + [self restoreEditingItem]; +} + +- (void)restoreEditingItem { + // Remove snapshot and reset item. + [_cellSnapshot removeFromSuperview]; + _cellSnapshot = nil; + _dismissingSection = NSNotFound; + _dismissingCellIndexPath = nil; + _reorderingCellIndexPath = nil; + [_collectionView.collectionViewLayout invalidateLayout]; +} + +// The distance an item must be panned before it is dismissed. Currently half of the bounds width. +- (CGFloat)distanceThresholdForDismissal { + return _collectionView.bounds.size.width / 2; +} + +- (CGFloat)dismissalAlphaForTranslationX:(CGFloat)translationX { + translationX = (CGFloat)fabs(translationX) - kMDCDismissalDistanceBeforeFading; + CGFloat adjustedThreshold = + [self distanceThresholdForDismissal] - kMDCDismissalDistanceBeforeFading; + CGFloat dismissalPercentage = (CGFloat)MIN(1, fabs(translationX) / adjustedThreshold); + return kMDCDismissalMinimumAlpha + (1 - kMDCDismissalMinimumAlpha) * (1 - dismissalPercentage); +} + +@end diff --git a/components/Collections/src/MDCCollectionViewEditingManagerDelegate.h b/components/Collections/src/MDCCollectionViewEditingManagerDelegate.h new file mode 100644 index 00000000000..a35a9088cbe --- /dev/null +++ b/components/Collections/src/MDCCollectionViewEditingManagerDelegate.h @@ -0,0 +1,329 @@ +/* + 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 + +/** + A delegate protocol that provides editing notifications for three types of collection view + gestural interations: + + - Cells that are dragging/moved vertically for reordering. + - Individual cells being swiped horizontally for dismissal. + - Entire cell sections being swiped horizontally for dismissal. + */ +@protocol MDCCollectionViewEditingManagerDelegate + +@optional + +#pragma mark - CollectionView Item Editing + +/** + If YES, the collectionView will allow editing of its items. Permissions for individual + index paths are set by implementing the delegate -collectionView:canEditItemAtIndexPath method. + If not implemented, will default to NO. + */ +- (BOOL)collectionViewAllowsEditing:(nonnull UICollectionView *)collectionView; + +/** + Sent to the receiver when the collection view will begin editing. Typically this method will be + called from a collection view editing manager that has had its editing property set to YES. + This will be called before any animations to editing state will begin. + + @param collectionView The collection view. + */ +- (void)collectionViewWillBeginEditing:(nonnull UICollectionView *)collectionView; + +/** + Sent to the receiver when the collection view did begin editing. Typically this method will be + called from a collection view editing manager that has had its editing property set to YES. + This is called after animations to editing state have completed. + + @param collectionView The collection view. + */ +- (void)collectionViewDidBeginEditing:(nonnull UICollectionView *)collectionView; + +/** + Sent to the receiver when the collection view will end editing. Typically this method will be + called from a collection view editing manager that has had its editing property set to NO. + This will be called before any animations from editing state begin. + + @param collectionView The collection view. + */ +- (void)collectionViewWillEndEditing:(nonnull UICollectionView *)collectionView; + +/** + Sent to the receiver when the collection view did end editing. Typically this method will be + called from a collection view editing manager that has had its editing property set to NO. + This is called after animations from editing state have completed. + + @param collectionView The collection view. + */ +- (void)collectionViewDidEndEditing:(nonnull UICollectionView *)collectionView; + +/** + Sent to the receiver for permission to edit an item at this collection view index path. + Returning NO here will prevent editing the designated index path. If not implemented, will + default to NO. + + @param collectionView The collection view. + @param indexPath The index path of the collection view. + @return if the collection view index path can be edited. + */ +- (BOOL)collectionView:(nonnull UICollectionView *)collectionView + canEditItemAtIndexPath:(nonnull NSIndexPath *)indexPath; + +/** + Sent to the receiver for permission to select an item at this collection view index path + during collection view editing. Returning NO here will prevent selecting the designated + index path. If not implemented, will default to NO. + + + @param collectionView The collection view. + @param indexPath The index path of the collection view. + @return if the collection view index path can be selected. + */ +- (BOOL)collectionView:(nonnull UICollectionView *)collectionView + canSelectItemDuringEditingAtIndexPath:(nonnull NSIndexPath *)indexPath; + +#pragma mark - CollectionView Item Moving + +/** + If YES, the collectionView will allow reordering of its items. Permissions for individual + index paths are be set by the -collectionView:canMoveItemAtIndexPath and/or the + -collectionView:canMoveItemAtIndexPath:toIndexPath methods. If not implemented, will default + to NO. + + */ +- (BOOL)collectionViewAllowsReordering:(nonnull UICollectionView *)collectionView; + +/** + Sent to the receiver for permission to move an item at this collection view index path. + Returning NO here will prevent moving the designated index path. If not implemented, will + default to NO. + + @param collectionView The collection view. + @param indexPath The index path of the collection view. + @return if the collection view index path can be moved. + */ +- (BOOL)collectionView:(nonnull UICollectionView *)collectionView + canMoveItemAtIndexPath:(nonnull NSIndexPath *)indexPath; + +/** + Sent to the receiver for permission to move an item at this collection view index path to + a new index path. Returning NO here will prevent moving the designated index path. If not + implemented, will default to NO. + + @param collectionView The collection view. + @param indexPath The current index path of the collection view. + @param newIndexPath The propsed new index path of the collection view. + @return if the collection view index path can be moved. + */ +- (BOOL)collectionView:(nonnull UICollectionView *)collectionView + canMoveItemAtIndexPath:(nonnull NSIndexPath *)indexPath + toIndexPath:(nonnull NSIndexPath *)newIndexPath; + +/** + Sent to the receiver when the collection view will move an item from its previous index path + to the new index path. + + @param collectionView The collection view. + @param indexPath The previous index path of the collection view. + @param newIndexPath The new index path of the collection view. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + willMoveItemAtIndexPath:(nonnull NSIndexPath *)indexPath + toIndexPath:(nonnull NSIndexPath *)newIndexPath; + +/** + Sent to the receiver when the collection view did move an item from its previous index path + to the new index path. + + @param collectionView The collection view. + @param indexPath The previous index path of the collection view. + @param newIndexPath The new index path of the collection view. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + didMoveItemAtIndexPath:(nonnull NSIndexPath *)indexPath + toIndexPath:(nonnull NSIndexPath *)newIndexPath; + +/** + Sent to the receiver when a collection view item at specified index path will begin dragging. + + @param collectionView The collection view. + @param indexPath The index path of the collection view. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + willBeginDraggingItemAtIndexPath:(nonnull NSIndexPath *)indexPath; + +/** + Sent to the receiver when a collection view item at specified index path has finished dragging. + + @param collectionView The collection view. + @param indexPath The index path of the collection view. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + didEndDraggingItemAtIndexPath:(nonnull NSIndexPath *)indexPath; + +#pragma mark - CollectionView Item Deletions + +/** + Sent to the receiver when an array of index paths will be deleted from the collection view. + + @param collectionView The collection view. + @param indexPaths An array of index paths to be deleted from the collection view. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + willDeleteItemsAtIndexPaths:(nonnull NSArray *)indexPaths; + +/** + Sent to the receiver after an array of index paths did get deleted from the collection view. + + @param collectionView The collection view. + @param indexPaths An array of index paths deleted from the collection view. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + didDeleteItemsAtIndexPaths:(nonnull NSArray *)indexPaths; + +#pragma mark - CollectionView Section Deletions + +/** + Sent to the receiver when an array of index paths will be deleted from the collection view. + + @param collectionView The collection view. + @param sections An index set of sections to deleted from the collection view. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + willDeleteSections:(nonnull NSIndexSet *)sections; + +/** + Sent to the receiver after an array of index paths did get deleted from the collection view. + + @param collectionView The collection view. + @param sections An index set of sections deleted from the collection view. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + didDeleteSections:(nonnull NSIndexSet *)sections; + +#pragma mark - CollectionView Swipe To Dismiss Items + +/** + If YES, the collectionView will allow swiping to dismiss an item. If allowed, swiping is enabled + for all items (excluding headers, and footers). Permissions for individual items can be set by + implementing the protocol -collectionView:canSwipeToDismissItemAtIndexPath method. If not + implemented, will default to NO. + + @param collectionView The collection view being swiped for dismissal. + @return if the collection view can swipe to dismiss an item. + */ +- (BOOL)collectionViewAllowsSwipeToDismissItem:(nonnull UICollectionView *)collectionView; + +/** + Sent to the receiver when a collection view index path begins to swipe for dismissal. The + delegate method -collectionViewAllowsSwipeToDismissItem must return true in order for this + subsequent delegate method to be called. The collection view is NOT required to be in + edit mode to allow swipe-to-dismiss items. Returning NO here will prevent swiping the + designated item at index path. If not implemented, will default to NO. + + + @param collectionView The collection view being swiped for dismissal. + @param indexPath The index path of the collection view being swiped for dismissal. + @return if the collection view index path can be swiped for dismissal. + */ +- (BOOL)collectionView:(nonnull UICollectionView *)collectionView + canSwipeToDismissItemAtIndexPath:(nonnull NSIndexPath *)indexPath; + +/** + Sent to the receiver when the collection view index path begins to swipe for dismissal. + + @param collectionView The collection view being swiped for dismissal. + @param indexPath The index path of the collection view being swiped for dismissal. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + willBeginSwipeToDismissItemAtIndexPath:(nonnull NSIndexPath *)indexPath; + +/** + Sent to the receiver after the collection view item has been dismissed. + + @param collectionView The collection view being swiped for dismissal. + @param indexPath The index path of the collection view being swiped for dismissal. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + didEndSwipeToDismissItemAtIndexPath:(nonnull NSIndexPath *)indexPath; + +/** + Sent to the receiver when the collection view index path has reset without being dismissed. + + @param collectionView The collection view being swiped for dismissal. + @param indexPath The index path of the collection view being swiped for dismissal. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + didCancelSwipeToDismissItemAtIndexPath:(nonnull NSIndexPath *)indexPath; + +#pragma mark - CollectionView Swipe To Dismiss Sections + +/** + If YES, the collectionView will allow swiping to dismiss a section. If allowed, swiping is enabled + for all section items, headers, and footers. Permissions for individual sections can be + set by implementing the protocol -collectionView:canSwipeToDismissSection method. If not + implemented, will default to NO. + + @param collectionView The collection view being swiped for dismissal. + @return if the collection view can swipe to dismiss a section. + */ +- (BOOL)collectionViewAllowsSwipeToDismissSection:(nonnull UICollectionView *)collectionView; + +/** + Sent to the receiver when a collection view section begins to swipe for dismissal. The + collection view property |allowsSwipeToDismissSection| must be true in order for this + subsequent delegate method to be called. The collection view is NOT required to be in + edit mode to allow swipe-to-dismiss sections. Returning NO here will prevent swiping the + designated section. If not implemented, will default to NO. + + @param collectionView The collection view being swiped for dismissal. + @param section The section of the collection view being swiped for dismissal. + @return if the collection view section can be swiped for dismissal. + */ +- (BOOL)collectionView:(nonnull UICollectionView *)collectionView + canSwipeToDismissSection:(NSInteger)section; + +/** + Sent to the receiver when the collection view section begins to swipe for dismissal. + + @param collectionView The collection view being swiped for dismissal. + @param section The section of the collection view being swiped for dismissal. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + willBeginSwipeToDismissSection:(NSInteger)section; + +/** + Sent to the receiver after the collection view section has been dismissed. + + @param collectionView The collection view being swiped for dismissal. + @param section The section of the collection view being swiped for dismissal. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + didEndSwipeToDismissSection:(NSInteger)section; + +/** + Sent to the receiver when the collection view section has reset without being dismissed. + + @param collectionView The collection view being swiped for dismissal. + @param section The section of the collection view being swiped for dismissal. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + didCancelSwipeToDismissSection:(NSInteger)section; + +@end diff --git a/components/Collections/src/MDCCollectionViewFlowLayout.h b/components/Collections/src/MDCCollectionViewFlowLayout.h new file mode 100644 index 00000000000..3181e3e0e9b --- /dev/null +++ b/components/Collections/src/MDCCollectionViewFlowLayout.h @@ -0,0 +1,28 @@ +/* + 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 + +/** + The MDCCollectionViewFlowLayout class provides a Material design layout implementation of a + UICollectionViewFlowLayout layout. + + Learn more at the + [Material spec](https://www.google.com/design/spec/components/lists.html#lists-specs) + */ +@interface MDCCollectionViewFlowLayout : UICollectionViewFlowLayout + +@end diff --git a/components/Collections/src/MDCCollectionViewFlowLayout.m b/components/Collections/src/MDCCollectionViewFlowLayout.m new file mode 100644 index 00000000000..db4a499ba3b --- /dev/null +++ b/components/Collections/src/MDCCollectionViewFlowLayout.m @@ -0,0 +1,751 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "MDCCollectionViewFlowLayout.h" + +#import "MDCCollectionViewController.h" +#import "MDCCollectionViewEditingManager.h" +#import "MDCCollectionViewEditingManagerDelegate.h" +#import "MDCCollectionViewStyleManager.h" +#import "MaterialCollectionLayoutAttributes.h" +#import "private/MDCCollectionGridBackgroundView.h" +#import "private/MDCCollectionInfoBarView.h" + +#import + +/** The grid background decoration view kind. */ +NSString *const kMDCCollectionDecorationView = @"MDCCollectionDecorationView"; + +static const NSInteger kMDCSupplementaryViewZIndex = 99; + +@implementation MDCCollectionViewFlowLayout { + NSMutableArray *_deletedIndexPaths; + NSMutableArray *_insertedIndexPaths; + NSMutableIndexSet *_deletedSections; + NSMutableIndexSet *_insertedSections; + NSMutableIndexSet *_headerSections; + NSMutableIndexSet *_footerSections; +} + +- (instancetype)init { + self = [super init]; + if (self) { + // Defaults. + self.minimumLineSpacing = 0; + self.minimumInteritemSpacing = 0; + self.scrollDirection = UICollectionViewScrollDirectionVertical; + self.sectionInset = UIEdgeInsetsZero; + + // Register decoration view for grid background. + [self registerClass:[MDCCollectionGridBackgroundView class] + forDecorationViewOfKind:kMDCCollectionDecorationView]; + } + return self; +} + +- (MDCCollectionViewEditingManager *)editingManager { + if ([self.collectionView.delegate isKindOfClass:[MDCCollectionViewController class]]) { + MDCCollectionViewController *controller = + (MDCCollectionViewController *)self.collectionView.delegate; + return controller.editingManager; + } + return nil; +} + +- (MDCCollectionViewStyleManager *)styleManager { + if ([self.collectionView.delegate isKindOfClass:[MDCCollectionViewController class]]) { + MDCCollectionViewController *controller = + (MDCCollectionViewController *)self.collectionView.delegate; + return controller.styleManager; + } + return nil; +} + +#pragma mark - UICollectionViewLayout (SubclassingHooks) + +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { + // If performing appearance animation, increase bounds height in order to retrieve additional + // offscreen attributes needed during animation. + rect = [self boundsForAppearanceAnimationWithInitialBounds:rect]; + NSMutableArray *attributes = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; + + // Store index path sections of any headers/footers within these attributes. + [self storeSupplementaryViewsWithAttributes:attributes]; + + // Set layout attributes. + for (MDCCollectionViewLayoutAttributes *attr in attributes) { + [self updateAttribute:attr]; + } + + // Add info bar header/footer supplementary view if necessary. + [self addInfoBarAttributesIfNecessary:attributes]; + + // Begin cell appearance animation if necessary. + [self beginCellAppearanceAnimationIfNecessary:attributes]; + + // Add a grid background decoration view for each section if necessary. + [self addDecorationViewIfNecessary:attributes]; + + return attributes; +} + +- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { + if (!CGSizeEqualToSize(self.collectionView.bounds.size, newBounds.size) || + self.editingManager.isEditing) { + [self invalidateLayout]; + return YES; + } + return [super shouldInvalidateLayoutForBoundsChange:newBounds]; +} + +#pragma mark - UICollectionViewLayout (UISubclassingHooks) + ++ (Class)layoutAttributesClass { + return [MDCCollectionViewLayoutAttributes class]; +} + +- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewLayoutAttributes *attr = + (MDCCollectionViewLayoutAttributes *)[super layoutAttributesForItemAtIndexPath:indexPath]; + [self updateAttribute:attr]; + return attr; +} + +- (UICollectionViewLayoutAttributes *) + layoutAttributesForSupplementaryViewOfKind:(NSString *)kind + atIndexPath:(NSIndexPath *)indexPath { + UICollectionViewLayoutAttributes *attr; + + if ([kind isEqualToString:UICollectionElementKindSectionHeader] || + [kind isEqualToString:UICollectionElementKindSectionFooter]) { + // Update section headers/Footers attributes. + attr = [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath]; + if (!attr) { + attr = + [MDCCollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:kind + withIndexPath:indexPath]; + } + [self updateAttribute:(MDCCollectionViewLayoutAttributes *)attr]; + + } else { + // Update editing info bar attributes. + attr = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:kind + withIndexPath:indexPath]; + + // Force the info bar supplementary views to stay fixed to their respective positions + // at top/bottom of the collectionView bounds. + CGFloat offsetY = 0; + CGRect currentBounds = self.collectionView.bounds; + attr.zIndex = kMDCSupplementaryViewZIndex; + + if ([kind isEqualToString:MDCCollectionInfoBarKindHeader]) { + attr.size = CGSizeMake(CGRectGetWidth(currentBounds), MDCCollectionInfoBarHeaderHeight); + // Allow header to move upwards with scroll, but prevent from moving downwards with scroll. + CGFloat insetTop = self.collectionView.contentInset.top; + CGFloat boundsY = currentBounds.origin.y; + CGFloat maxOffsetY = MAX(boundsY + insetTop, 0); + offsetY = boundsY + (attr.size.height / 2) + insetTop - maxOffsetY; + } else if ([kind isEqualToString:MDCCollectionInfoBarKindFooter]) { + attr.size = CGSizeMake(CGRectGetWidth(currentBounds), MDCCollectionInfoBarFooterHeight); + offsetY = currentBounds.origin.y + currentBounds.size.height - (attr.size.height / 2); + } + attr.center = CGPointMake(CGRectGetMidX(currentBounds), offsetY); + } + return attr; +} + +- (UICollectionViewLayoutAttributes *) + layoutAttributesForDecorationViewOfKind:(NSString *)elementKind + atIndexPath:(NSIndexPath *)indexPath { + MDCCollectionViewLayoutAttributes *decorationAttr = + [MDCCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:elementKind + withIndexPath:indexPath]; + // Determine section frame by summing all of its item frames. + CGRect sectionFrame = CGRectNull; + for (NSInteger i = 0; i < [self numberOfItemsInSection:indexPath.section]; ++i) { + indexPath = [NSIndexPath indexPathForItem:i inSection:indexPath.section]; + UICollectionViewLayoutAttributes *attribute = + [self layoutAttributesForItemAtIndexPath:indexPath]; + if (!CGRectIsNull(attribute.frame)) { + sectionFrame = CGRectUnion(sectionFrame, attribute.frame); + } + } + decorationAttr.frame = sectionFrame; + decorationAttr.zIndex = -1; + return decorationAttr; +} + +- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset { + // Return current contentOffset to prevent any layout animations from jumping to new offset. + return [super targetContentOffsetForProposedContentOffset:self.collectionView.contentOffset]; +} + +#pragma mark - UICollectionViewLayout (UIUpdateSupportHooks) + +- (void)prepareForCollectionViewUpdates:(NSArray *)updateItems { + [super prepareForCollectionViewUpdates:updateItems]; + _deletedIndexPaths = [NSMutableArray array]; + _insertedIndexPaths = [NSMutableArray array]; + _deletedSections = [NSMutableIndexSet indexSet]; + _insertedSections = [NSMutableIndexSet indexSet]; + + for (UICollectionViewUpdateItem *item in updateItems) { + if (item.updateAction == UICollectionUpdateActionDelete) { + // Store deleted sections or indexPaths. + if (item.indexPathBeforeUpdate.item == NSNotFound) { + [_deletedSections addIndex:item.indexPathBeforeUpdate.section]; + } else { + [_deletedIndexPaths addObject:item.indexPathBeforeUpdate]; + } + + } else if (item.updateAction == UICollectionUpdateActionInsert) { + // Store inserted sections or indexPaths. + if (item.indexPathAfterUpdate.item == NSNotFound) { + [_insertedSections addIndex:item.indexPathAfterUpdate.section]; + } else { + [_insertedIndexPaths addObject:item.indexPathAfterUpdate]; + } + } + } +} + +- (void)finalizeCollectionViewUpdates { + [super finalizeCollectionViewUpdates]; + _deletedIndexPaths = nil; + _insertedIndexPaths = nil; + _deletedSections = nil; + _insertedSections = nil; +} + +- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath: + (NSIndexPath *)itemIndexPath { + UICollectionViewLayoutAttributes *attr = + [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath]; + // Adding new section or item. + if ([_insertedSections containsIndex:itemIndexPath.section] || + [_insertedIndexPaths containsObject:itemIndexPath]) { + attr.transform = CGAffineTransformMakeTranslation(0, -CGRectGetHeight(attr.bounds) / 2); + } else { + attr.alpha = 1; + } + return attr; +} + +- (UICollectionViewLayoutAttributes *) + initialLayoutAttributesForAppearingSupplementaryElementOfKind:(NSString *)elementKind + atIndexPath:(NSIndexPath *)elementIndexPath { + UICollectionViewLayoutAttributes *attr = + [super initialLayoutAttributesForAppearingSupplementaryElementOfKind:elementKind + atIndexPath:elementIndexPath]; + if ([elementKind isEqualToString:UICollectionElementKindSectionHeader] || + [elementKind isEqualToString:UICollectionElementKindSectionFooter]) { + // Adding new section header or footer. + if ([_insertedSections containsIndex:elementIndexPath.section]) { + attr.transform = CGAffineTransformMakeTranslation(0, -CGRectGetHeight(attr.bounds) / 2); + } else { + attr.alpha = 1; + } + } + return attr; +} + +- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath: + (NSIndexPath *)itemIndexPath { + UICollectionViewLayoutAttributes *attr = + [super finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath]; + // Deleting section or item. + if ([_deletedSections containsIndex:itemIndexPath.section] || + [_deletedIndexPaths containsObject:itemIndexPath]) { + attr.transform = CGAffineTransformMakeTranslation(0, -CGRectGetHeight(attr.bounds) / 2); + } else { + attr.alpha = 1; + } + return attr; +} + +- (UICollectionViewLayoutAttributes *) + finalLayoutAttributesForDisappearingSupplementaryElementOfKind:(NSString *)elementKind + atIndexPath:(NSIndexPath *)elementIndexPath { + UICollectionViewLayoutAttributes *attr = + [super finalLayoutAttributesForDisappearingSupplementaryElementOfKind:elementKind + atIndexPath:elementIndexPath]; + if ([elementKind isEqualToString:UICollectionElementKindSectionHeader] || + [elementKind isEqualToString:UICollectionElementKindSectionFooter]) { + // Deleting section header or footer. + if ([_deletedSections containsIndex:elementIndexPath.section]) { + attr.transform = CGAffineTransformMakeTranslation(0, -CGRectGetHeight(attr.bounds) / 2); + } else { + attr.alpha = 1; + } + } + return attr; +} + +#pragma mark - Header/Footer Caching + +- (void)storeSupplementaryViewsWithAttributes:(NSArray *)attributes { + _headerSections = [NSMutableIndexSet indexSet]; + _footerSections = [NSMutableIndexSet indexSet]; + + // Store index path sections for headers/footers. + for (MDCCollectionViewLayoutAttributes *attr in attributes) { + if ([attr.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { + [_headerSections addIndex:attr.indexPath.section]; + } else if ([attr.representedElementKind isEqualToString:UICollectionElementKindSectionFooter]) { + [_footerSections addIndex:attr.indexPath.section]; + } + } +} + +#pragma mark - Private + +- (MDCCollectionViewLayoutAttributes *)updateAttribute:(MDCCollectionViewLayoutAttributes *)attr { + if (attr.representedElementKind == UICollectionElementCategoryCell) { + attr.editing = self.editingManager.isEditing; + } + attr.isGridLayout = NO; + if (self.styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeList) { + attr.sectionOrdinalPosition = [self ordinalPositionForListElementWithAttribute:attr]; + } else if (self.styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { + attr.sectionOrdinalPosition = [self ordinalPositionForGridElementWithAttribute:attr]; + attr.isGridLayout = YES; + } + + [self updateCellStateMaskWithAttribute:attr]; + + if (attr.representedElementCategory == UICollectionElementCategorySupplementaryView) { + attr = [self updateSupplementaryViewAttribute:attr]; + } + + // Set cell background. + attr.backgroundImage = [self.styleManager backgroundImageForCellLayoutAttributes:attr]; + + // Set separator styling. + attr.separatorColor = self.styleManager.separatorColor; + attr.separatorInset = self.styleManager.separatorInset; + attr.separatorLineHeight = self.styleManager.separatorLineHeight; + attr.shouldHideSeparators = self.styleManager.shouldHideSeparators; + + // Set inlay and hidden state if necessary. + [self inlayAttributeIfNecessary:attr]; + [self hideAttributeIfNecessary:attr]; + + return attr; +} + +- (MDCCollectionViewLayoutAttributes *)updateSupplementaryViewAttribute: + (MDCCollectionViewLayoutAttributes *)attr { + // In vertical scrolling, supplementary views only respect their height and ignore their width + // value. The opposite is true for horizontal scrolling. Therefore we must manually set insets + // on both the backgroundView and contentView in order to match the insets of the collection + // view rows. + CGRect insetFrame = attr.frame; + UIEdgeInsets insets = [self insetsAtSectionIndex:attr.indexPath.section]; + if (self.scrollDirection == UICollectionViewScrollDirectionVertical) { + insetFrame = CGRectInset(insetFrame, insets.left / 2 + insets.right / 2, 0); + if (attr.representedElementKind == UICollectionElementKindSectionHeader) { + insetFrame.origin.y += insets.top; + } else if (attr.representedElementKind == UICollectionElementKindSectionFooter) { + insetFrame.origin.y -= insets.bottom; + } + attr.frame = insetFrame; + } + return attr; +} + +- (UIEdgeInsets)insetsAtSectionIndex:(NSInteger)section { + // Determine insets based on cell style. + CGFloat inset = (CGFloat)floor(MDCCollectionViewCellStyleCardSectionInset); + UIEdgeInsets insets = UIEdgeInsetsZero; + NSInteger numberOfSections = self.collectionView.numberOfSections; + BOOL isTop = (section == 0); + BOOL isBottom = (section == numberOfSections - 1); + MDCCollectionViewCellStyle cellStyle = [self.styleManager cellStyleAtSectionIndex:section]; + BOOL isCardStyle = cellStyle == MDCCollectionViewCellStyleCard; + BOOL isGroupedStyle = cellStyle == MDCCollectionViewCellStyleGrouped; + // Set left/right insets. + if (isCardStyle) { + insets.left = inset; + insets.right = inset; + } + // Set top/bottom insets. + if (isCardStyle || isGroupedStyle) { + insets.top = (CGFloat)floor((isTop) ? inset : inset / 2.0f); + insets.bottom = (CGFloat)floor((isBottom) ? inset : inset / 2.0f); + } + return insets; +} + +- (MDCCollectionViewOrdinalPosition)ordinalPositionForListElementWithAttribute: + (MDCCollectionViewLayoutAttributes *)attr { + // Returns the ordinal position of cells and supplementary views within a list layout. This is + // used to determine the layout attributes applied to their styling. + MDCCollectionViewOrdinalPosition position = 0; + NSIndexPath *indexPath = attr.indexPath; + NSInteger numberOfItemsInSection = [self numberOfItemsInSection:indexPath.section]; + BOOL isTop = NO; + BOOL isBottom = NO; + BOOL hasSectionHeader = [_headerSections containsIndex:indexPath.section]; + BOOL hasSectionFooter = [_footerSections containsIndex:indexPath.section]; + BOOL hasSectionItems = YES; + + BOOL hidesHeaderBackground = NO; + if ([self.styleManager.delegate + respondsToSelector:@selector(collectionView:shouldHideHeaderBackgroundForSection:)]) { + hidesHeaderBackground = + [self.styleManager.delegate collectionView:self.styleManager.collectionView + shouldHideHeaderBackgroundForSection:indexPath.section]; + } + + BOOL hidesFooterBackground = NO; + if ([self.styleManager.delegate + respondsToSelector:@selector(collectionView:shouldHideFooterBackgroundForSection:)]) { + hidesFooterBackground = + [self.styleManager.delegate collectionView:self.styleManager.collectionView + shouldHideFooterBackgroundForSection:indexPath.section]; + } + + if (attr.representedElementCategory == UICollectionElementCategoryCell) { + isTop = (indexPath.item == 0) && (!hasSectionHeader || hidesHeaderBackground); + isBottom = + (indexPath.item == numberOfItemsInSection - 1) && + (!hasSectionFooter || hidesFooterBackground); + } else if (attr.representedElementCategory == UICollectionElementCategorySupplementaryView) { + NSString *kind = attr.representedElementKind; + BOOL isElementHeader = ([kind isEqualToString:UICollectionElementKindSectionHeader]); + BOOL isElementFooter = ([kind isEqualToString:UICollectionElementKindSectionFooter]); + isTop = (isElementFooter && !hasSectionItems && !hasSectionHeader) || isElementHeader; + isBottom = (isElementHeader && !hasSectionItems && !hasSectionFooter) || isElementFooter; + } + + if (attr.editing || [self.styleManager isItemInlaidAtIndexPath:attr.indexPath]) { + isTop = YES; + isBottom = YES; + } + + if (!isTop && !isBottom) { + position |= MDCCollectionViewOrdinalPositionVerticalCenter; + } else { + position |= isTop ? MDCCollectionViewOrdinalPositionVerticalTop : position; + position |= isBottom ? MDCCollectionViewOrdinalPositionVerticalBottom : position; + } + return position; +} + +- (MDCCollectionViewOrdinalPosition)ordinalPositionForGridElementWithAttribute: + (MDCCollectionViewLayoutAttributes *)attr { + // Returns the ordinal position of cells and supplementary views within a grid layout. This is + // used to determine the layout attributes applied to their styling. + MDCCollectionViewOrdinalPosition position = 0; + NSIndexPath *indexPath = attr.indexPath; + NSInteger numberOfItemsInSection = [self numberOfItemsInSection:indexPath.section]; + NSInteger gridColumnCount = self.styleManager.gridColumnCount; + NSInteger maxRowIndex = (NSInteger)(floor(numberOfItemsInSection / gridColumnCount) - 1); + NSInteger maxColumnIndex = gridColumnCount - 1; + NSInteger ordinalRow = (NSInteger)(floor(indexPath.item / gridColumnCount)); + NSInteger ordinalColumn = (NSInteger)(floor(indexPath.item % gridColumnCount)); + + // Set vertical ordinal position. + if (ordinalRow > 0 && ordinalRow < maxRowIndex) { + position = position | MDCCollectionViewOrdinalPositionVerticalCenter; + } else { + position = (ordinalRow == 0) + ? position | MDCCollectionViewOrdinalPositionVerticalTop + : position; + position = (ordinalRow == maxRowIndex) + ? position | MDCCollectionViewOrdinalPositionVerticalBottom + : position; + } + + // Set horizontal ordinal position. + if (ordinalColumn > 0 && ordinalColumn < maxColumnIndex) { + position = position | MDCCollectionViewOrdinalPositionHorizontalCenter; + } else { + position = (ordinalColumn == 0) + ? position | MDCCollectionViewOrdinalPositionHorizontalLeft + : position; + position = (ordinalColumn == maxColumnIndex) + ? position | MDCCollectionViewOrdinalPositionHorizontalRight + : position; + } + return position; +} + +- (void)updateCellStateMaskWithAttribute:(MDCCollectionViewLayoutAttributes *)attr { + attr.shouldShowSelectorStateMask = NO; + attr.shouldShowSelectorStateMask = NO; + + // Determine proper state to show cell if editing. + if (attr.editing) { + if ([self.collectionView.dataSource + conformsToProtocol:@protocol(MDCCollectionViewEditingManagerDelegate)]) { + id editingDelegate = + (id)self.collectionView.dataSource; + + // Check if delegate can select during editing. + if ([editingDelegate respondsToSelector: + @selector(collectionView:canSelectItemDuringEditingAtIndexPath:)]) { + attr.shouldShowSelectorStateMask = [editingDelegate collectionView:self.collectionView + canSelectItemDuringEditingAtIndexPath:attr.indexPath]; + } + + // Check if delegate can reorder. + if ([editingDelegate respondsToSelector:@selector(collectionView:canMoveItemAtIndexPath:)]) { + attr.shouldShowReorderStateMask = [editingDelegate collectionView:self.collectionView + canMoveItemAtIndexPath:attr.indexPath]; + } + } + } +} + +- (void)inlayAttributeIfNecessary:(MDCCollectionViewLayoutAttributes *)attr { + // Inlay this attribute if necessary. + CGFloat inset = MDCCollectionViewCellStyleCardSectionInset; + UIEdgeInsets inlayInsets = UIEdgeInsetsZero; + NSInteger item = attr.indexPath.item; + NSArray *inlaidIndexPaths = [self.styleManager indexPathsForInlaidItems]; + + // Update ordinal position for index paths adjacent to inlaid index path. + for (NSIndexPath *inlaidIndexPath in inlaidIndexPaths) { + if (inlaidIndexPath.section == attr.indexPath.section) { + NSInteger numberOfItemsInSection = [self numberOfItemsInSection:inlaidIndexPath.section]; + if (attr.representedElementCategory == UICollectionElementCategoryCell) { + if (item == inlaidIndexPath.item) { + // Get previous and next index paths to the inlaid index path. + BOOL prevAttrIsInlaid = NO; + BOOL nextAttrIsInlaid = NO; + BOOL hasSectionHeader = [_headerSections containsIndex:inlaidIndexPath.section]; + BOOL hasSectionFooter = [_footerSections containsIndex:inlaidIndexPath.section]; + + if (inlaidIndexPath.item > 0 || hasSectionHeader) { + NSIndexPath *prevIndexPath = [NSIndexPath indexPathForItem:(inlaidIndexPath.item - 1) + inSection:inlaidIndexPath.section]; + prevAttrIsInlaid = [self.styleManager isItemInlaidAtIndexPath:prevIndexPath]; + inlayInsets.top = prevAttrIsInlaid ? inset / 2 : inset; + } + + if (inlaidIndexPath.item < numberOfItemsInSection - 1 || hasSectionFooter) { + NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:(inlaidIndexPath.item + 1) + inSection:inlaidIndexPath.section]; + nextAttrIsInlaid = [self.styleManager isItemInlaidAtIndexPath:nextIndexPath]; + inlayInsets.bottom = nextAttrIsInlaid ? inset / 2 : inset; + } + + // Is attribute to be inlaid. + attr.frame = UIEdgeInsetsInsetRect(attr.frame, inlayInsets); + attr.sectionOrdinalPosition = MDCCollectionViewOrdinalPositionVerticalTopBottom; + } else if (item == inlaidIndexPath.item - 1) { + // Is previous to inlaid attribute. + if (attr.sectionOrdinalPosition & MDCCollectionViewOrdinalPositionVerticalTop) { + attr.sectionOrdinalPosition = MDCCollectionViewOrdinalPositionVerticalTopBottom; + } else if (attr.sectionOrdinalPosition & + MDCCollectionViewOrdinalPositionVerticalCenter) { + attr.sectionOrdinalPosition = MDCCollectionViewOrdinalPositionVerticalBottom; + } + } else if (item == inlaidIndexPath.item + 1) { + // Is next to inlaid attribute. + if (attr.sectionOrdinalPosition & MDCCollectionViewOrdinalPositionVerticalCenter) { + attr.sectionOrdinalPosition = MDCCollectionViewOrdinalPositionVerticalTop; + } else if (attr.sectionOrdinalPosition & + MDCCollectionViewOrdinalPositionVerticalBottom) { + attr.sectionOrdinalPosition = MDCCollectionViewOrdinalPositionVerticalBottom; + } + } + + } else if (attr.representedElementCategory == UICollectionElementCategorySupplementaryView) { + // If header/footer attribute, update if adjacent to inlaid index path. + NSString *kind = attr.representedElementKind; + BOOL isElementHeader = ([kind isEqualToString:UICollectionElementKindSectionHeader]); + BOOL isElementFooter = ([kind isEqualToString:UICollectionElementKindSectionFooter]); + if (isElementHeader && inlaidIndexPath.item == 0) { + attr.sectionOrdinalPosition = MDCCollectionViewOrdinalPositionVerticalTopBottom; + } else if (isElementFooter && inlaidIndexPath.item == numberOfItemsInSection - 1) { + attr.sectionOrdinalPosition = MDCCollectionViewOrdinalPositionVerticalTopBottom; + } + } + } + } +} + +- (void)hideAttributeIfNecessary:(MDCCollectionViewLayoutAttributes *)attr { + // Hide the attribute if the MDCCollectionViewEditingManager is either currently handling + // a cell item or section swipe for dismissal, or is reordering a cell item. + if (self.editingManager) { + BOOL isCell = attr.representedElementCategory == UICollectionElementCategoryCell; + if (attr.indexPath.section == self.editingManager.dismissingSection || + ([attr.indexPath isEqual:self.editingManager.dismissingCellIndexPath] && isCell) || + ([attr.indexPath isEqual:self.editingManager.reorderingCellIndexPath] && isCell)) { + attr.hidden = YES; + } + } +} + +- (void)addInfoBarAttributesIfNecessary:(NSMutableArray *)attributes { + if (self.editingManager.isEditing && [attributes count] > 0) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; + + // Add header info bar if editing. + [attributes addObject: + [self layoutAttributesForSupplementaryViewOfKind:MDCCollectionInfoBarKindHeader + atIndexPath:indexPath]]; + + // Add footer info bar if editing and item(s) are selected. + NSInteger selectedItemCount = [self.collectionView.indexPathsForSelectedItems count]; + if (selectedItemCount > 0) { + [attributes addObject: + [self layoutAttributesForSupplementaryViewOfKind:MDCCollectionInfoBarKindFooter + atIndexPath:indexPath]]; + } + } +} + +- (void)addDecorationViewIfNecessary:(NSMutableArray *)attributes { + // If necessary, adds a decoration view to a section drawn below its items. This will only happen + // for a grid layout when it is either A) grouped-style or B) card-style with zero padding. When + // this happens, the background for those items will not be drawn, and instead this decoration + // view will extend to the bounds of the sum of its respective section item frames. Shadowing and + // border will be applied to this decoration view as per the style manager settings. + if (self.styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { + NSMutableSet *sectionSet = [NSMutableSet set]; + BOOL shouldShowGridBackground = NO; + NSMutableArray *decorationAttributes = [NSMutableArray array]; + for (MDCCollectionViewLayoutAttributes *attr in attributes) { + NSInteger section = attr.indexPath.section; + + // Only add one decoration view per section. + if (![sectionSet containsObject:@(section)]) { + NSIndexPath *decorationIndexPath = [NSIndexPath indexPathForItem:0 inSection:section]; + MDCCollectionViewLayoutAttributes *decorationAttr = + (MDCCollectionViewLayoutAttributes *) + [self layoutAttributesForDecorationViewOfKind:kMDCCollectionDecorationView + atIndexPath:decorationIndexPath]; + shouldShowGridBackground = [self shouldShowGridBackgroundWithAttribute:decorationAttr]; + decorationAttr.shouldShowGridBackground = shouldShowGridBackground; + decorationAttr.backgroundImage = shouldShowGridBackground + ? [self.styleManager backgroundImageForCellLayoutAttributes:decorationAttr] + : nil; + [decorationAttributes addObject:decorationAttr]; + [sectionSet addObject:@(section)]; + } + if (shouldShowGridBackground) { + attr.backgroundImage = nil; + } + } + [attributes addObjectsFromArray:decorationAttributes]; + } +} + +- (BOOL)shouldShowGridBackgroundWithAttribute:(MDCCollectionViewLayoutAttributes *)attr { + // Determine whether to show grid background. + if (self.styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { + if (self.styleManager.cellStyle == MDCCollectionViewCellStyleGrouped || + (self.styleManager.cellStyle == MDCCollectionViewCellStyleCard && + self.styleManager.gridPadding == 0)) { + return YES; + } + } + return NO; +} + +- (NSInteger)numberOfItemsInSection:(NSInteger)section { + return [self.collectionView numberOfItemsInSection:section]; +} + +#pragma mark - Cell Appearance Animation + +- (CGRect)boundsForAppearanceAnimationWithInitialBounds:(CGRect)initialBounds { + // Increase initial bounds by 25% allowing offscreen attributes to be included in the + // appearance animation. + if (self.styleManager.shouldAnimateCellsOnAppearance && + self.styleManager.willAnimateCellsOnAppearance) { + CGRect newBounds = initialBounds; + newBounds.size.height += (newBounds.size.height / 4); + return newBounds; + } + return initialBounds; +} + +- (void)beginCellAppearanceAnimationIfNecessary:(NSMutableArray *)attributes { + // Here we want to assign a delay to each attribute such that the animation will fade-in from the + // top downwards in a staggered manner. However, the array of attributes we receive here are not + // in the correct order and must be sorted and re-ordered to properly assign these delays. + // + // First we will sort the array of attributes by index path to ensure proper ordering. Secondly + // we will manipulate the array to bring any headers before their first respective cell items. + // + // When completed, we will end up with an array of attributes in the form of + // header -> item -> footer ... repeated for each section. Now we can use this ordered array + // to assign delays based on their proper ordinal position from top down. + NSInteger attributeCount = attributes.count; + NSTimeInterval duration = self.styleManager.animateCellsOnAppearanceDuration; + if (self.styleManager.shouldAnimateCellsOnAppearance && attributeCount > 0) { + // First sort by index path. + NSArray *sortedByIndexPath = + [attributes sortedArrayUsingComparator: + ^NSComparisonResult(MDCCollectionViewLayoutAttributes *attr1, + MDCCollectionViewLayoutAttributes *attr2) { + return [attr1.indexPath compare:attr2.indexPath]; + }]; + + // Next create new array containing attributes in the form of header -> item -> footer. + NSMutableArray *sortedAttributes = [NSMutableArray array]; + [sortedByIndexPath enumerateObjectsUsingBlock:^(MDCCollectionViewLayoutAttributes *attr, + NSUInteger idx, BOOL *stop) { + if (sortedAttributes.count > 0) { + // Check if current attribute is a header and previous attribute is an item. If so, + // insert the current header attribute before the cell. + MDCCollectionViewLayoutAttributes *prevAttr = [sortedAttributes objectAtIndex:idx - 1]; + BOOL prevAttrIsItem = + prevAttr.representedElementCategory == UICollectionElementCategoryCell; + BOOL attrIsHeader = + [attr.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]; + if (attrIsHeader && prevAttrIsItem) { + [sortedAttributes insertObject:attr atIndex:idx - 1]; + return; + } + } + [sortedAttributes addObject:attr]; + }]; + + // Now assign delays and add padding to frame Y coordinate which gets removed during animation. + [sortedAttributes enumerateObjectsUsingBlock:^(MDCCollectionViewLayoutAttributes *attr, + NSUInteger idx, BOOL *stop) { + attr.willAnimateCellsOnAppearance = self.styleManager.willAnimateCellsOnAppearance; + attr.animateCellsOnAppearanceDuration = self.styleManager.animateCellsOnAppearanceDuration; + attr.animateCellsOnAppearanceDelay = + (attributeCount > 0) ? ((CGFloat)idx / attributeCount) * duration : 0; + + if (self.styleManager.willAnimateCellsOnAppearance) { + CGRect frame = attr.frame; + frame.origin.y += self.styleManager.animateCellsOnAppearancePadding; + attr.frame = frame; + } + }]; + + // Call asynchronously to allow the current layout cycle to complete before issuing animations. + if (self.styleManager.willAnimateCellsOnAppearance) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.styleManager beginCellAppearanceAnimation]; + }); + } + } +} + +@end diff --git a/components/Collections/src/MDCCollectionViewStyleManager.h b/components/Collections/src/MDCCollectionViewStyleManager.h new file mode 100644 index 00000000000..9964a78cd9d --- /dev/null +++ b/components/Collections/src/MDCCollectionViewStyleManager.h @@ -0,0 +1,280 @@ +/* + 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 + +@protocol MDCCollectionViewStyleManagerDelegate; +@class MDCCollectionViewLayoutAttributes; + +/** The default section insets. Should be an even number to allow even division. */ +extern const CGFloat MDCCollectionViewCellStyleCardSectionInset; + +/** The styles in which the collectionView cells can be shown. */ +typedef NS_ENUM(NSUInteger, MDCCollectionViewCellStyle) { + /** Cells displayed full width without section padding. */ + MDCCollectionViewCellStyleDefault, + + /** Cells displayed full width with section padding. */ + MDCCollectionViewCellStyleGrouped, + + /** Cells displayed card style with sections padding. */ + MDCCollectionViewCellStyleCard, +}; + +/** The layout types in which the collectionView cells can be shown. */ +typedef NS_ENUM(NSUInteger, MDCCollectionViewCellLayoutType) { + /** Cells displayed in list layout. */ + MDCCollectionViewCellLayoutTypeList, + + /** Cells displayed in grid layout. */ + MDCCollectionViewCellLayoutTypeGrid, + + /** + Cells displayed in custom defined layout. A new UICollectionViewLayout must be + provided and set on the collection view. + */ + MDCCollectionViewCellLayoutTypeCustom +}; + +/** + MDCCollectionViewStyleManager provides a default implementation for a UICollectionView to set + its style properties. + */ +@interface MDCCollectionViewStyleManager : NSObject + +/** The associated collection view. */ +@property(nonatomic, readonly, weak, nullable) UICollectionView *collectionView; + +/** The delegate is sent messages when styles change. */ +@property(nonatomic, weak, nullable) id delegate; + +/** Indicates whether the collection view layout should be invalidated. */ +@property(nonatomic, assign) BOOL shouldInvalidateLayout; + +#pragma mark - Cell Styling + +/** The cell background color. Defaults to white color. */ +@property(nonatomic, strong, nonnull) UIColor *cellBackgroundColor; + +/** + The cell layout type. Defaults to MDCCollectionViewCellLayoutTypeList if not set. Not animated. + Defaults to MDCCollectionViewCellLayoutTypeList. + */ +@property(nonatomic, assign) MDCCollectionViewCellLayoutType cellLayoutType; + +/** + The grid column count. Requires cellLayoutType to be set to MDCCollectionViewCellLayoutTypeGrid. + Not animated. + */ +@property(nonatomic, assign) NSInteger gridColumnCount; + +/** + The grid padding property is used when the cellLayoutType is MDCCollectionViewCellLayoutTypeGrid + and the gridColumnCount is greater than or equal to 2. This value ensure that the left, center, + and right padding are equivalent. This prevents double padding at center. Not animated. + */ +@property(nonatomic, assign) CGFloat gridPadding; + +/** The cell style. Not animated. @c setCellStyle:animated: for animated layout type changes. */ +@property(nonatomic, assign) MDCCollectionViewCellStyle cellStyle; + +/** + Updates the cell style with/without animation. + + @param animated YES the transition will be animated; otherwise, NO. + */ +- (void)setCellStyle:(MDCCollectionViewCellStyle)cellStyle animated:(BOOL)animated; + +/** + The collection view cell style at the specified section index. + + @param section The collection view section. + @return The cell style at specified section. + */ +- (MDCCollectionViewCellStyle)cellStyleAtSectionIndex:(NSInteger)section; + +/** + The collection view cell background image view (utilized to render the background color and + shadows) edge outsets as determined for a cell and its layout attributes. + + @param attr The cell's layout attributes. + @return Edge outsets as detemined by cell style at this index. + */ +- (UIEdgeInsets)backgroundImageViewOutsetsForCellWithAttribute: + (nonnull MDCCollectionViewLayoutAttributes *)attr; + +/** + Returns an image for use with the given cell style and ordinal position within section. + + The returned image is cached internally after the first request. Changing any of the display + properties will invalidate the cached images. + + @param attr The cell's layout attributes. + @return Image as determined by cell style and section ordinal position. + */ +- (nonnull UIImage *)backgroundImageForCellLayoutAttributes: + (nonnull MDCCollectionViewLayoutAttributes *)attr; + +#pragma mark - Cell Separator + +/** Separator color. Defaults to #E0E0E0. */ +@property(nonatomic, strong, nullable) UIColor *separatorColor; + +/** Separator inset. Defaults to UIEdgeInsetsZero. */ +@property(nonatomic) UIEdgeInsets separatorInset; + +/** Separator line height. Defaults to 1.0f */ +@property(nonatomic) CGFloat separatorLineHeight; + +/* Whether to hide the cell separators. Defaults to NO. */ +@property(nonatomic) BOOL shouldHideSeparators; + +#pragma mark - Item Inlaying + +/** + Whether inlaying a collection view item is allowed. When an item is inlaid, it will be given + padding at its edges to make it inset from other sibling items. A typical use case for inlaying + is when setting a collection view into editing mode, in which each cell gets inlaid so that they + can individually be selected/unselected/swiped for deletion. + + If YES, call the -inlayItemAtIndexPath:animated method to inlay the item at the specified + index path. Remove the inlay by calling the -removeInlayAtIndexPath:animated: method. + Not animated. Defaults to YES. + */ +@property(nonatomic, assign) BOOL allowsItemInlay; + +/** Whether inlaying multiple items is allowed. Not animated. Defaults to NO. */ +@property(nonatomic, assign) BOOL allowsMultipleItemInlays; + +/** + Returns an array of collection view index paths for items that have their respective frames + inlaid. + + @return An array of index paths. + */ +- (nullable NSArray *)indexPathsForInlaidItems; + +/** + A Boolean value indicating if the collection view item at the specified index path has its + frame inlaid. + + @param indexPath The collection view index path. + @return YES if the cells are Material styled cards. Otherwise No. + */ +- (BOOL)isItemInlaidAtIndexPath:(nonnull NSIndexPath *)indexPath; + +/** + Inlays the item at the specified index path. + + Property allowsItemInlay must be set to YES, otherwise no-op. + + @param indexPath The specified index path. + @param animated YES the transition will be animated; otherwise, NO. + */ +- (void)applyInlayToItemAtIndexPath:(nonnull NSIndexPath *)indexPath animated:(BOOL)animated; + +/** + Removes the inlaid state of the item at the specified index path. + + @param indexPath The specified index path. + @param animated YES the transition will be animated; otherwise, NO. + */ +- (void)removeInlayFromItemAtIndexPath:(nonnull NSIndexPath *)indexPath animated:(BOOL)animated; + +/** + Inlays items at all available index paths. + + Property allowsItemInlay must be set to YES, otherwise no-op. + + @param animated YES the transition will be animated; otherwise, NO. + */ +- (void)applyInlayToAllItemsAnimated:(BOOL)animated; + +/** + Removes the inlaid state of items at all available index paths. + + @param animated YES the transition will be animated; otherwise, NO. + */ +- (void)removeInlayFromAllItemsAnimated:(BOOL)animated; + +/** + Resets the internal storage array of all item index paths for inlaid items. When modifying + items in the collection view model, a call to this method will ensure that all inserted + and/or deleted index paths are reflected properly in the return array from the + method -indexPathsForInlaidItems. Calling this is not animated. + */ +- (void)resetIndexPathsForInlaidItems; + +#pragma mark - Cell Appearance Animation + +/** + Whether cells should animate on appearance. This property typically should be set in the + -viewDidLoad method of the controller. If YES, cells will animate the following properties: + + A) Cell heights will begin expanded by the padding specified by animateCellsOnAppearancePadding. + It will then animate to original height. + + B) Cell frame will begin with positive y-offset as specified by animateCellsOnAppearancePadding. + It will animate upwards to original y coordinate. + + C) Cell contentView and separatorView will begin hidden by setting alpha to 0. It will then + animate with a fade-in transition in a staggered fashion from the top cell down to last + visible cell. + */ +@property(nonatomic, assign) BOOL shouldAnimateCellsOnAppearance; + +/** Whether cells should calculate their initial properties before animation on appearance. */ +@property(nonatomic, readonly, assign) BOOL willAnimateCellsOnAppearance; + +/* + The cell appearance animation padding. This value is ignored unless + shouldAnimateCellsOnAppearance is set to YES. Its value is set at initialization time by the + constant MDCCollectionViewAnimatedAppearancePadding. + */ +@property(nonatomic, readonly, assign) CGFloat animateCellsOnAppearancePadding; + +/* + The cell appearance animation duration. This value is ignored unless + shouldAnimateCellsOnAppearance is set to YES. Its value is set at initialization time by the + constant MDCCollectionViewAnimatedAppearanceDuration. + */ +@property(nonatomic, readonly, assign) NSTimeInterval animateCellsOnAppearanceDuration; + +/** + This method should only be called from MDCCollectionViewFlowLayout class once all visible + collection view attributes are available. By calling -updateLayoutAnimated within this method's + animation block, it will trigger the collection view to query both the item and supplementary + view sizing methods of the UICollectionViewDelegateFlowLayout protocol, resulting in an + animated resizing of the cells by the height specified in animateCellsOnAppearancePadding. + */ +- (void)beginCellAppearanceAnimation; + +#pragma mark - Initializers + +/** Unavailable superclass initializer. */ +- (nonnull instancetype)init NS_UNAVAILABLE; + +/** + Initializes and returns a newly allocated style manager object with the specified collection view. + + Designated initializer. + + @param collectionView The controller's collection view. + */ +- (nonnull instancetype)initWithCollectionView: + (nonnull UICollectionView *)collectionView NS_DESIGNATED_INITIALIZER; + +@end diff --git a/components/Collections/src/MDCCollectionViewStyleManager.m b/components/Collections/src/MDCCollectionViewStyleManager.m new file mode 100644 index 00000000000..997df96d5e6 --- /dev/null +++ b/components/Collections/src/MDCCollectionViewStyleManager.m @@ -0,0 +1,734 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "MDCCollectionViewStyleManager.h" + +#import "MDCCollectionViewStyleManagerDelegate.h" +#import "MaterialCollectionLayoutAttributes.h" + +#import + +#define RGBCOLOR(r, g, b) [UIColor colorWithRed:(r) / 255.0f green:(g) / 255.0f blue:(b) / 255.0f alpha:1] + +typedef NS_OPTIONS(NSUInteger, BackgroundCacheKey) { + BackgroundCacheKeyFlat = 0, + BackgroundCacheKeyTop = 1 << 0, + BackgroundCacheKeyBottom = 1 << 1, + BackgroundCacheKeyCard = 1 << 2, + BackgroundCacheKeyGrouped = 1 << 3, + BackgroundCacheKeyHighlighted = 1 << 4, + BackgroundCacheKeyMax = 1 << 5, +}; + +const CGFloat MDCCollectionViewCellStyleCardSectionInset = 8.0f; + +/** Cell content view insets for card-style cells */ +static const CGFloat kFourThirds = 4.0f / 3.0f; +static const UIEdgeInsets kCollectionViewCellContentInsetsRetina3x = { + kFourThirds, kFourThirds, kFourThirds, kFourThirds}; +static const UIEdgeInsets kCollectionViewCellContentInsetsRetina = {1.5, 1.5, 1.5, 1.5}; +static const UIEdgeInsets kCollectionViewCellContentInsets = {1, 2, 1, 2}; + +/** Default cell separator style settings */ +static const CGFloat kCollectionViewCellSeparatorDefaultHeight = 1.0f; + +/** Grid layout defaults */ +static const NSInteger kCollectionViewGridDefaultColumnCount = 2; +static const CGFloat kCollectionViewGridDefaultPadding = 4.0f; + +/** The drawn cell background */ +static const CGSize kCellImageSize = {44, 44}; +static const CGFloat kCollectionViewCellDefaultBorderWidth = 1.0f; +static const CGFloat kCollectionViewCellDefaultBorderRadius = 1.5f; +static inline UIColor *kCollectionViewCellDefaultBorderColor() { + return [UIColor colorWithWhite:0 alpha:0.05f]; +} + +/** Cell shadowing */ +static const CGFloat kCollectionViewCellDefaultShadowWidth = 1.0f; +static inline CGSize kCollectionViewCellDefaultShadowOffset() { + return CGSizeMake(0, 1); +} +static inline UIColor *kCollectionViewCellDefaultShadowColor() { + return [UIColor colorWithWhite:0 alpha:0.1f]; +} + +/** Animate cell on appearance settings */ +static const CGFloat kCollectionViewAnimatedAppearancePadding = 20.0f; +static const NSTimeInterval kCollectionViewAnimatedAppearanceDelay = 0.1; +static const NSTimeInterval kCollectionViewAnimatedAppearanceDuration = 0.3; + +/** Modifies only the right and bottom edges of a CGRect. */ +NS_INLINE CGRect RectContract(CGRect rect, CGFloat dx, CGFloat dy) { + return CGRectMake(rect.origin.x, rect.origin.y, rect.size.width - dx, rect.size.height - dy); +} + +/** Modifies only the top and left edges of a CGRect. */ +NS_INLINE CGRect RectShift(CGRect rect, CGFloat dx, CGFloat dy) { + return CGRectOffset(RectContract(rect, dx, dy), dx, dy); +} + +@interface MDCCollectionViewStyleManager () + +/** Convenience property defining the exact color that the collection view background should be. */ +@property(nonatomic, readonly) UIColor *collectionViewBackgroundColor; + +/** + A dictionary of NSPointerArray caches, keyed by UIColor, for cached cell background images + using that background color. Index into the NSPointerArray using the results of + backgroundCacheKeyForCardStyle:isGroupedStyle:isTop:isBottom:isHighlighted: + */ +@property(nonatomic, readonly) NSMutableDictionary *cellBackgroundCaches; + +/** An set of index paths for items that are inlaid. */ +@property(nonatomic, strong) NSMutableSet *inlaidIndexPathSet; + +@end + +@implementation MDCCollectionViewStyleManager { + UIColor *_collectionViewBackgroundColor; +} + +- (instancetype)initWithCollectionView:(UICollectionView *)collectionView { + self = [super init]; + if (self) { + _collectionView = collectionView; + + // Cell default style properties. + _cellBackgroundColor = [UIColor whiteColor]; + _cellStyle = MDCCollectionViewCellStyleDefault; + _collectionView.backgroundColor = [UIColor whiteColor]; + _inlaidIndexPathSet = [NSMutableSet set]; + + // Cell separator defaults. + _separatorColor = RGBCOLOR(224, 224, 224); + _separatorInset = UIEdgeInsetsZero; + _separatorLineHeight = kCollectionViewCellSeparatorDefaultHeight; + _shouldHideSeparators = NO; + + // Grid defaults. + _cellLayoutType = MDCCollectionViewCellLayoutTypeList; + _gridColumnCount = kCollectionViewGridDefaultColumnCount; + _gridPadding = kCollectionViewGridDefaultPadding; + + // Animate cell on appearance settings. + _animateCellsOnAppearancePadding = kCollectionViewAnimatedAppearancePadding; + _animateCellsOnAppearanceDuration = kCollectionViewAnimatedAppearanceDuration; + + // Caching. + _cellBackgroundCaches = [NSMutableDictionary dictionary]; + } + return self; +} + +- (BOOL)isEqual:(id)object { + // If shouldInvalidateLayout property is NO, prevent a collection view layout caused when + // layout attributes check here if they are equal. + return (self == object) && ![self shouldInvalidateLayoutForStyleChange]; +} + +#pragma mark - Cell Appearance Animation + +- (void)setShouldAnimateCellsOnAppearance:(BOOL)shouldAnimateCellsOnAppearance { + _shouldAnimateCellsOnAppearance = shouldAnimateCellsOnAppearance; + _willAnimateCellsOnAppearance = shouldAnimateCellsOnAppearance; +} + +- (void)beginCellAppearanceAnimation { + if (_shouldAnimateCellsOnAppearance) { + _willAnimateCellsOnAppearance = NO; + [UIView animateWithDuration:_animateCellsOnAppearanceDuration + delay:kCollectionViewAnimatedAppearanceDelay + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [self updateLayoutAnimated:YES]; + } + completion:^(BOOL finished) { + [self setShouldAnimateCellsOnAppearance:NO]; + }]; + } +} + +#pragma mark - Caching + +- (BackgroundCacheKey)backgroundCacheKeyForCardStyle:(BOOL)isCardStyle + isGroupedStyle:(BOOL)isGroupedStyle + isTop:(BOOL)isTop + isBottom:(BOOL)isBottom + isHighlighted:(BOOL)isHighlighted { + if (!isCardStyle && !isGroupedStyle) { + return BackgroundCacheKeyFlat; + } + BackgroundCacheKey options = isTop ? BackgroundCacheKeyTop : 0; + options |= isBottom ? BackgroundCacheKeyBottom : 0; + options |= isCardStyle ? BackgroundCacheKeyCard : 0; + options |= isGroupedStyle ? BackgroundCacheKeyGrouped : 0; + options |= isHighlighted ? BackgroundCacheKeyHighlighted : 0; + NSAssert(isCardStyle != isGroupedStyle, @"Cannot be both card and grouped style"); + return options; +} + +- (NSPointerArray *)cellBackgroundCache { + NSPointerArray *cache = [NSPointerArray strongObjectsPointerArray]; + cache.count = BackgroundCacheKeyMax; + return cache; +} + +#pragma mark - Separators + +- (void)setSeparatorColor:(UIColor *)separatorColor { + if (_separatorColor == separatorColor) { + return; + } + [self invalidateLayoutForStyleChange]; + _separatorColor = separatorColor; +} + +- (void)setSeparatorInset:(UIEdgeInsets)separatorInset { + if (UIEdgeInsetsEqualToEdgeInsets(_separatorInset, separatorInset)) { + return; + } + [self invalidateLayoutForStyleChange]; + _separatorInset = separatorInset; +} + +- (void)setSeparatorLineHeight:(CGFloat)separatorLineHeight { + if (_separatorLineHeight == separatorLineHeight) { + return; + } + [self invalidateLayoutForStyleChange]; + _separatorLineHeight = separatorLineHeight; +} + +- (void)setShouldHideSeparators:(BOOL)shouldHideSeparators { + if (_shouldHideSeparators == shouldHideSeparators) { + return; + } + [self invalidateLayoutForStyleChange]; + _shouldHideSeparators = shouldHideSeparators; +} + +- (void)setCellStyle:(MDCCollectionViewCellStyle)cellStyle { + if (_cellStyle == cellStyle) { + return; + } + [_cellBackgroundCaches removeAllObjects]; + [self invalidateLayoutForStyleChange]; + _cellStyle = cellStyle; +} + +#pragma mark - Public + +- (UIEdgeInsets)backgroundImageViewOutsetsForCellWithAttribute: + (MDCCollectionViewLayoutAttributes *)attr { + // Inset contentView to allow for shadowed borders in cards. + UIEdgeInsets insets = UIEdgeInsetsZero; + + MDCCollectionViewCellStyle cellStyle = [self cellStyleAtSectionIndex:attr.indexPath.section]; + BOOL isCardStyle = cellStyle == MDCCollectionViewCellStyleCard; + BOOL isGroupedStyle = cellStyle == MDCCollectionViewCellStyleGrouped; + BOOL isHighlighted = NO; + + MDCCollectionViewOrdinalPosition position = attr.sectionOrdinalPosition; + + if ([self drawShadowForCellWithIsCardStye:isCardStyle + isGroupStyle:isGroupedStyle + isHighlighted:isHighlighted]) { + CGFloat mainScreenScale = [[UIScreen mainScreen] scale]; + if (mainScreenScale > (CGFloat)2.1) { + insets = kCollectionViewCellContentInsetsRetina3x; + } else if (mainScreenScale > (CGFloat)1.1) { + insets = kCollectionViewCellContentInsetsRetina; + } else { + insets = kCollectionViewCellContentInsets; + } + + if (!isCardStyle) { + insets = UIEdgeInsetsMake(insets.top, 0, insets.bottom, 0); + } + + switch (position) { + case MDCCollectionViewOrdinalPositionVerticalTop: + insets = UIEdgeInsetsMake(insets.top, insets.left, 0, insets.right); + break; + case MDCCollectionViewOrdinalPositionVerticalBottom: + insets = UIEdgeInsetsMake(0, insets.left, insets.bottom, insets.right); + break; + case MDCCollectionViewOrdinalPositionVerticalCenter: + insets = UIEdgeInsetsMake(0, insets.left, 0, insets.right); + break; + default: + break; + } + } + + return insets; +} + +- (void)setCellStyle:(MDCCollectionViewCellStyle)cellStyle animated:(BOOL)animated { + _cellStyle = cellStyle; + [self updateLayoutAnimated:animated]; +} + +- (MDCCollectionViewCellStyle)cellStyleAtSectionIndex:(NSInteger)section { + MDCCollectionViewCellStyle cellStyle = self.cellStyle; + if (self.delegate && + [self.delegate respondsToSelector:@selector(collectionView:cellStyleForSection:)]) { + cellStyle = [self.delegate collectionView:_collectionView cellStyleForSection:section]; + } + return cellStyle; +} + +- (NSArray *)indexPathsForInlaidItems { + return [_inlaidIndexPathSet allObjects]; +} + +- (BOOL)isItemInlaidAtIndexPath:(NSIndexPath *)indexPath { + return [[_inlaidIndexPathSet allObjects] containsObject:indexPath]; +} + +- (void)applyInlayToItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated { + if (_allowsItemInlay) { + // If necessary, remove any previously inlaid items. + if (!_allowsMultipleItemInlays) { + for (NSIndexPath *inlaidIndexPath in [self indexPathsForInlaidItems]) { + [self removeInlayFromItemAtIndexPath:inlaidIndexPath animated:animated]; + } + } + + void (^completionBlock)(BOOL finished) = ^(BOOL finished) { + if ([self.delegate respondsToSelector: + @selector(collectionView:didApplyInlayToItemAtIndexPaths:)]) { + [self.delegate collectionView:_collectionView + didApplyInlayToItemAtIndexPaths:@[ indexPath ]]; + } + }; + + // Inlay this item. + [_inlaidIndexPathSet addObject:indexPath]; + [self updateLayoutAnimated:animated completion:completionBlock]; + } +} + +- (void)removeInlayFromItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated { + [_inlaidIndexPathSet removeObject:indexPath]; + + void (^completionBlock)(BOOL finished) = ^(BOOL finished) { + if ([self.delegate respondsToSelector: + @selector(collectionView:didRemoveInlayFromItemAtIndexPaths:)]) { + [self.delegate collectionView:_collectionView + didRemoveInlayFromItemAtIndexPaths:@[ indexPath ]]; + } + }; + + [self updateLayoutAnimated:animated completion:completionBlock]; +} + +- (void)applyInlayToAllItemsAnimated:(BOOL)animated { + if (_allowsItemInlay && _allowsMultipleItemInlays) { + // Store all index paths. + [_inlaidIndexPathSet removeAllObjects]; + NSInteger sections = [_collectionView numberOfSections]; + for (NSInteger section = 0; section < sections; section++) { + for (NSInteger item = 0; item < [_collectionView numberOfItemsInSection:section]; item++) { + [_inlaidIndexPathSet addObject:[NSIndexPath indexPathForItem:item inSection:section]]; + } + } + + void (^completionBlock)(BOOL finished) = ^(BOOL finished) { + if ([self.delegate respondsToSelector: + @selector(collectionView:didApplyInlayToItemAtIndexPaths:)]) { + [self.delegate collectionView:_collectionView + didApplyInlayToItemAtIndexPaths:[_inlaidIndexPathSet allObjects]]; + } + }; + + // Inlay all items. + [self updateLayoutAnimated:animated completion:completionBlock]; + } +} + +- (void)removeInlayFromAllItemsAnimated:(BOOL)animated { + NSArray *indexPaths = [_inlaidIndexPathSet allObjects]; + [_inlaidIndexPathSet removeAllObjects]; + + void (^completionBlock)(BOOL finished) = ^(BOOL finished) { + if ([self.delegate respondsToSelector: + @selector(collectionView:didRemoveInlayFromItemAtIndexPaths:)]) { + [self.delegate collectionView:_collectionView + didRemoveInlayFromItemAtIndexPaths:indexPaths]; + } + }; + + [self updateLayoutAnimated:animated completion:completionBlock]; +} + +- (void)resetIndexPathsForInlaidItems { + [_inlaidIndexPathSet removeAllObjects]; + [self applyInlayToAllItemsAnimated:NO]; +} + +- (void)updateLayoutAnimated:(BOOL)animated { + [self updateLayoutAnimated:animated completion:nil]; +} + +#pragma mark - Private + +- (void)updateLayoutAnimated:(BOOL)animated completion:(void (^)(BOOL finished))completion { + if (animated) { + // Invalidate current layout while allowing animation to new layout. + [_collectionView performBatchUpdates:nil + completion:^(BOOL finished) { + if (completion) { + completion(finished); + } + }]; + } else { + _shouldInvalidateLayout = YES; + + // Create new layout with existing layout properties. + NSData *data = + [NSKeyedArchiver archivedDataWithRootObject:_collectionView.collectionViewLayout]; + UICollectionViewFlowLayout *newLayout = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + [_collectionView setCollectionViewLayout:newLayout + animated:animated + completion:^(BOOL finished) { + if (completion) { + completion(finished); + } + }]; + } +} + +- (void)invalidateLayoutForStyleChange { + _shouldInvalidateLayout = YES; +} + +- (BOOL)shouldInvalidateLayoutForStyleChange { + // Whether the collection view layout should be invalidated due to a style property that has + // changed value. + return _shouldInvalidateLayout; +} + +#pragma mark - Default Colors + +- (UIColor *)collectionViewBackgroundColor { + if (!_collectionViewBackgroundColor) { + _collectionViewBackgroundColor = RGBCOLOR(0xEE, 0xEE, 0xEE); + } + return _collectionViewBackgroundColor; +} + +#pragma mark - Cell Image Background + +- (BOOL)drawShadowForCellWithIsCardStye:(BOOL)isCardStyle + isGroupStyle:(BOOL)isGroupStyle + isHighlighted:(BOOL)isHighlighted { + return (isCardStyle || isGroupStyle) && + kCollectionViewCellDefaultShadowWidth > 0 && + !isHighlighted; +} + +- (UIImage *)backgroundImageForCellLayoutAttributes:(MDCCollectionViewLayoutAttributes *)attr { + BOOL isSectionHeader = + [attr.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]; + BOOL isSectionFooter = + [attr.representedElementKind isEqualToString:UICollectionElementKindSectionFooter]; + BOOL isDecorationView = + attr.representedElementCategory == UICollectionElementCategoryDecorationView; + BOOL isTop = attr.sectionOrdinalPosition & MDCCollectionViewOrdinalPositionVerticalTop; + BOOL isBottom = attr.sectionOrdinalPosition & MDCCollectionViewOrdinalPositionVerticalBottom; + + MDCCollectionViewCellStyle cellStyle = [self cellStyleAtSectionIndex:attr.indexPath.section]; + BOOL isCardStyle = cellStyle == MDCCollectionViewCellStyleCard; + BOOL isGroupedStyle = cellStyle == MDCCollectionViewCellStyleGrouped; + BOOL isGridLayout = (_cellLayoutType == MDCCollectionViewCellLayoutTypeGrid); + if (!isCardStyle && !isGroupedStyle) { + // If not card or grouped style, revert |isBottom| to allow drawing separator at bottom. + isBottom = NO; + } else { + // Update background color for card/grouped styles. + _collectionView.backgroundColor = self.collectionViewBackgroundColor; + } + CGFloat borderRadius = (isCardStyle) ? kCollectionViewCellDefaultBorderRadius : 0.0f; + + // Allowance for grid decoration view. + if (isGridLayout) { + if (!isDecorationView && attr.shouldShowGridBackground) { + return nil; + } else { + isTop = isBottom = YES; + } + } + + // If no-background section header, return nil image. + BOOL hidesHeaderBackground = NO; + if ([_delegate respondsToSelector: + @selector(collectionView:shouldHideHeaderBackgroundForSection:)]) { + hidesHeaderBackground = [_delegate collectionView:_collectionView + shouldHideHeaderBackgroundForSection:attr.indexPath.section]; + } + if (hidesHeaderBackground && isSectionHeader) { + return nil; + } + + // If no-background section footer, return nil image. + BOOL hidesFooterBackground = NO; + if ([_delegate respondsToSelector: + @selector(collectionView:shouldHideFooterBackgroundForSection:)]) { + hidesFooterBackground = [_delegate collectionView:_collectionView + shouldHideFooterBackgroundForSection:attr.indexPath.section]; + } + if (hidesFooterBackground && isSectionFooter) { + return nil; + } + + // If no-background section item, return nil image. + BOOL hidesBackground = NO; + if ([_delegate respondsToSelector: + @selector(collectionView:shouldHideItemBackgroundAtIndexPath:)]) { + hidesBackground = [_delegate collectionView:_collectionView + shouldHideItemBackgroundAtIndexPath:attr.indexPath]; + } + if (hidesBackground && !(isDecorationView || isSectionFooter || isSectionHeader)) { + return nil; + } + + BOOL isHighlighted = NO; + + BackgroundCacheKey backgroundCacheKey = + [self backgroundCacheKeyForCardStyle:isCardStyle + isGroupedStyle:isGroupedStyle + isTop:isTop + isBottom:isBottom + isHighlighted:isHighlighted]; + + if (backgroundCacheKey > BackgroundCacheKeyMax) { + NSAssert(NO, @"Invalid style manager cell background cache key"); + return nil; + } + + // Get cell color. + UIColor *backgroundColor = _cellBackgroundColor; + if ([_delegate respondsToSelector:@selector(collectionView:cellBackgroundColorAtIndexPath:)]) { + backgroundColor = [_delegate collectionView:_collectionView + cellBackgroundColorAtIndexPath:attr.indexPath]; + } + + NSPointerArray *cellBackgroundCache = _cellBackgroundCaches[backgroundColor]; + if (!cellBackgroundCache) { + cellBackgroundCache = [self cellBackgroundCache]; + _cellBackgroundCaches[backgroundColor] = cellBackgroundCache; + } else if ([cellBackgroundCache pointerAtIndex:backgroundCacheKey]) { + return (__bridge UIImage *)[cellBackgroundCache pointerAtIndex:backgroundCacheKey]; + } + + CGRect imageRect = CGRectMake(0, 0, kCellImageSize.width, kCellImageSize.height); + UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, 0); + + CGContextRef cx = UIGraphicsGetCurrentContext(); + + // Create a transparent background. + CGContextClearRect(cx, imageRect); + + // Inner background color + CGContextSetFillColorWithColor(cx, backgroundColor.CGColor); + + CGRect contentFrame = imageRect; + + // Draw the shadow. + if ([self drawShadowForCellWithIsCardStye:isCardStyle + isGroupStyle:isGroupedStyle + isHighlighted:isHighlighted]) { + if (isCardStyle) { + contentFrame = CGRectInset(imageRect, kCollectionViewCellDefaultShadowWidth, 0); + } + if (isTop) { + contentFrame = RectShift(contentFrame, 0, kCollectionViewCellDefaultShadowWidth); + } + if (isBottom) { + contentFrame = RectContract(contentFrame, 0, kCollectionViewCellDefaultShadowWidth); + } + + CGContextSaveGState(cx); + CGRect shadowFrame = contentFrame; + + // We want the shadow to clip to the top and bottom edges of the image so that when two cells + // are next to each other their shadows line up perfectly. + if (!isTop) { + shadowFrame = RectShift(shadowFrame, 0, -kCollectionViewCellDefaultShadowWidth); + } + if (!isBottom) { + shadowFrame = RectContract(shadowFrame, 0, -kCollectionViewCellDefaultShadowWidth); + } + + [self applyBackgroundPathToContext:cx + rect:shadowFrame + isTop:isTop + isBottom:isBottom + isCard:(isCardStyle || isGroupedStyle) + borderRadius:borderRadius]; + CGContextSetShadowWithColor(cx, + kCollectionViewCellDefaultShadowOffset(), + kCollectionViewCellDefaultShadowWidth, + kCollectionViewCellDefaultShadowColor().CGColor); + CGContextDrawPath(cx, kCGPathFill); + CGContextRestoreGState(cx); + } else { + // Draw a flat cell background. + CGContextSaveGState(cx); + [self applyBackgroundPathToContext:cx + rect:contentFrame + isTop:isTop + isBottom:isBottom + isCard:(isCardStyle || isGroupedStyle) + borderRadius:borderRadius]; + CGContextFillPath(cx); + CGContextRestoreGState(cx); + } + // Draw border paths for cells. We want the cell border to overlap the shadow and the content. + if ((isCardStyle || isGroupedStyle) && !isHighlighted) { + CGFloat minPixelOffset = [self minPixelOffset]; + CGRect borderFrame = CGRectInset(contentFrame, -minPixelOffset, -minPixelOffset); + CGContextSaveGState(cx); + CGContextSetLineWidth(cx, kCollectionViewCellDefaultBorderWidth); + CGContextSetStrokeColorWithColor(cx, kCollectionViewCellDefaultBorderColor().CGColor); + [self applyBorderPathToContext:cx + rect:borderFrame + isTop:isTop + isBottom:isBottom + isCard:isCardStyle + borderRadius:borderRadius]; + CGContextStrokePath(cx); + CGContextRestoreGState(cx); + } + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + UIImage *resizableImage = [self resizableImage:image]; + [cellBackgroundCache replacePointerAtIndex:backgroundCacheKey + withPointer:(__bridge void *)(resizableImage)]; + return resizableImage; +} + +#pragma mark - Private Context Paths + +// We want to draw the borders and shadows on single retina-pixel boundaries if possible, but +// we need to avoid doing this on non-retina devices because it'll look blurry. +- (CGFloat)minPixelOffset { + return 1.0f / [[UIScreen mainScreen] scale]; +} + +- (UIImage *)resizableImage:(UIImage *)image { + // Returns a resizable version of this image with cap insets equal to center point. + CGFloat capWidth = (CGFloat)floor(image.size.width / 2); + CGFloat capHeight = (CGFloat)floor(image.size.height / 2); + UIEdgeInsets capInsets = UIEdgeInsetsMake(capHeight, capWidth, capHeight, capWidth); + return [image resizableImageWithCapInsets:capInsets]; +} + +- (void)applyBackgroundPathToContext:(CGContextRef)c + rect:(CGRect)rect + isTop:(BOOL)isTop + isBottom:(BOOL)isBottom + isCard:(BOOL)isCard + borderRadius:(CGFloat)borderRadius { + // Draw background paths for cell. + CGFloat minPixelOffset = (isCard) ? [self minPixelOffset] : 0.0f; + CGFloat minX = CGRectGetMinX(rect) + minPixelOffset; + CGFloat midX = CGRectGetMidX(rect) + minPixelOffset; + CGFloat maxX = CGRectGetMaxX(rect) - minPixelOffset; + CGFloat minY = CGRectGetMinY(rect) - minPixelOffset; + CGFloat midY = CGRectGetMidY(rect) - minPixelOffset; + CGFloat maxY = CGRectGetMaxY(rect) + minPixelOffset; + + CGContextBeginPath(c); + + CGContextMoveToPoint(c, minX, midY); + if (isTop && isCard) { + CGContextAddArcToPoint(c, minX, minY + 1, midX, minY + 1, borderRadius); + CGContextAddArcToPoint(c, maxX, minY + 1, maxX, midY, borderRadius); + } else { + CGContextAddLineToPoint(c, minX, minY); + CGContextAddLineToPoint(c, maxX, minY); + } + + CGContextAddLineToPoint(c, maxX, midY); + + if (isBottom & isCard) { + CGContextAddArcToPoint(c, maxX, maxY - 1, midX, maxY - 1, borderRadius); + CGContextAddArcToPoint(c, minX, maxY - 1, minX, midY, borderRadius); + } else { + CGContextAddLineToPoint(c, maxX, maxY); + CGContextAddLineToPoint(c, minX, maxY); + } + CGContextAddLineToPoint(c, minX, midY); + + CGContextClosePath(c); +} + +- (void)applyBorderPathToContext:(CGContextRef)c + rect:(CGRect)rect + isTop:(BOOL)isTop + isBottom:(BOOL)isBottom + isCard:(BOOL)isCard + borderRadius:(CGFloat)borderRadius { + // Draw border paths for cell. + CGFloat minPixelOffset = (isCard) ? [self minPixelOffset] : 0.0f; + CGFloat minX = CGRectGetMinX(rect) + minPixelOffset; + CGFloat midX = CGRectGetMidX(rect) + minPixelOffset; + CGFloat maxX = CGRectGetMaxX(rect) - minPixelOffset; + CGFloat minY = CGRectGetMinY(rect) - minPixelOffset; + CGFloat midY = CGRectGetMidY(rect) - minPixelOffset; + CGFloat maxY = CGRectGetMaxY(rect) + minPixelOffset; + + CGContextBeginPath(c); + + if (isTop && isBottom) { + CGContextMoveToPoint(c, minX, midY); + CGContextAddArcToPoint(c, minX, minY + 1, midX, minY + 1, borderRadius); + CGContextAddArcToPoint(c, maxX, minY + 1, maxX, midY, borderRadius); + CGContextAddLineToPoint(c, maxX, midY); + CGContextAddArcToPoint(c, maxX, maxY - 1, midX, maxY - 1, borderRadius); + CGContextAddArcToPoint(c, minX, maxY - 1, minX, midY, borderRadius); + CGContextAddLineToPoint(c, minX, midY); + } else if (isTop) { + CGContextMoveToPoint(c, minX, maxY); + CGContextAddLineToPoint(c, minX, midY); + CGContextAddArcToPoint(c, minX, minY + 1, midX, minY + 1, borderRadius); + CGContextAddArcToPoint(c, maxX, minY + 1, maxX, midY, borderRadius); + CGContextAddLineToPoint(c, maxX, maxY); + } else if (isBottom) { + CGContextMoveToPoint(c, maxX, minY); + CGContextAddLineToPoint(c, maxX, midY); + CGContextAddArcToPoint(c, maxX, maxY - 1, midX, maxY - 1, borderRadius); + CGContextAddArcToPoint(c, minX, maxY - 1, minX, midY, borderRadius); + CGContextAddLineToPoint(c, minX, minY); + } else { + CGContextMoveToPoint(c, minX, minY); + CGContextAddLineToPoint(c, minX, maxY); + CGContextMoveToPoint(c, maxX, minY); + CGContextAddLineToPoint(c, maxX, maxY); + } + + CGContextClosePath(c); +} + +@end diff --git a/components/Collections/src/MDCCollectionViewStyleManagerDelegate.h b/components/Collections/src/MDCCollectionViewStyleManagerDelegate.h new file mode 100644 index 00000000000..6a6367610a5 --- /dev/null +++ b/components/Collections/src/MDCCollectionViewStyleManagerDelegate.h @@ -0,0 +1,139 @@ +/* + 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 "MDCCollectionViewStyleManager.h" + +@class MDCInkTouchController; +@class MDCInkView; + +/** A delegate protocol which allows setting collection view cell styles. */ +@protocol MDCCollectionViewStyleManagerDelegate +@optional + +#pragma mark - Styling + +/** + Asks the delegate for the cell height at the specified collection view index path. The style + controller will make padding/insets adjustments to this value as needed depending on the cell + style and editing mode. + + @param collectionView The collection view. + @param indexPath The item's index path. + @return The cell height at the specified index path. + */ +- (CGFloat)collectionView:(nonnull UICollectionView *)collectionView + cellHeightAtIndexPath:(nonnull NSIndexPath *)indexPath; + +/** + Asks the delegate for the cell style at the specified collection view section index. All + remaining sections to have their cells styled per the style managers |cellStyle| property. + + @param collectionView The collection view. + @param section The collection view section. + @return The cell style to be used at the specified index. + */ +- (MDCCollectionViewCellStyle)collectionView:(nonnull UICollectionView *)collectionView + cellStyleForSection:(NSInteger)section; + +/** + Asks the delegate for the cell background color at the specified collection view index path. + + @param collectionView The collection view. + @param indexPath The item's index path. + @return The cell background color at the specified index path. + */ +- (nullable UIColor *)collectionView:(nonnull UICollectionView *)collectionView + cellBackgroundColorAtIndexPath:(nonnull NSIndexPath *)indexPath; + +/** + Asks the delegate whether the specified item should hide its background image/shadowing. + + @param collectionView The collection view. + @param indexPath The item's index path. + @return if the item background should be hidden at the specified index path. + */ +- (BOOL)collectionView:(nonnull UICollectionView *)collectionView + shouldHideItemBackgroundAtIndexPath:(nonnull NSIndexPath *)indexPath; + +/** + Asks the delegate whether the specified header should hide its background image/shadowing. + + @param collectionView The collection view. + @param section The collection view section. + @return if the header background should be hidden at the specified section. + */ +- (BOOL)collectionView:(nonnull UICollectionView *)collectionView + shouldHideHeaderBackgroundForSection:(NSInteger)section; + +/** + Asks the delegate whether the specified footer should hide its background image/shadowing. + + @param collectionView The collection view. + @param section The collection view section. + @return if the footer background should be hidden at the specified section. + */ +- (BOOL)collectionView:(nonnull UICollectionView *)collectionView + shouldHideFooterBackgroundForSection:(NSInteger)section; + +#pragma mark - Item inlaying + +/** + Allows the receiver to receive notification that items at the specified index paths did + inlay their frame. + + @param collectionView The collection view. + @param indexPaths An array of index paths. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + didApplyInlayToItemAtIndexPaths:(nonnull NSArray *)indexPaths; + +/** + Allows the receiver to receive notification that items at the specified index paths did + remove the inlay of their frames. + + @param collectionView The collection view. + @param indexPaths An array of index paths. + */ +- (void)collectionView:(nonnull UICollectionView *)collectionView + didRemoveInlayFromItemAtIndexPaths:(nonnull NSArray *)indexPaths; + +#pragma mark - Ink Touches + +/** + Asks the delegate whether the inkVIew for a specified index path will is hidden. + + @param collectionView The collection view. + @param indexPath The collection view index path. + @return if the ink view should be hidden at the specified index path. + */ +- (BOOL)collectionView:(nonnull UICollectionView *)collectionView + hidesInkViewAtIndexPath:(nonnull NSIndexPath *)indexPath; + +/** + Allows the receiver to set the ink view at the specified collection view index path. + + @param collectionView The collection view. + @param inkTouchController The ink controller of the ink view. + @param indexPath The collection view index path. + @return The inkView to be used at the specified index path. + */ +- (nonnull MDCInkView *)collectionView:(nonnull UICollectionView *)collectionView + inkTouchController:(nonnull MDCInkTouchController *)inkTouchController + inkViewAtIndexPath:(nonnull NSIndexPath *)indexPath; + +@end diff --git a/components/Collections/src/MaterialCollections.bundle/Resources/en.lproj/MaterialCollections.strings b/components/Collections/src/MaterialCollections.bundle/Resources/en.lproj/MaterialCollections.strings new file mode 100644 index 00000000000..4e9cd34d774 --- /dev/null +++ b/components/Collections/src/MaterialCollections.bundle/Resources/en.lproj/MaterialCollections.strings @@ -0,0 +1,4 @@ +/* Label for a button indicating a collectionView row can be deleted. (40 chars.) */ +"DeleteButton" = "Delete"; +/* Label for info bar indicating available editing gestures for collectionView. (40 chars.) */ +"InfoBarGestureHint" = "Swipe an item to delete"; diff --git a/components/Collections/src/MaterialCollections.h b/components/Collections/src/MaterialCollections.h new file mode 100644 index 00000000000..d0f063c9f68 --- /dev/null +++ b/components/Collections/src/MaterialCollections.h @@ -0,0 +1,23 @@ +/* + 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 "MDCCollectionViewController.h" +#import "MDCCollectionViewEditingManager.h" +#import "MDCCollectionViewEditingManagerDelegate.h" +#import "MDCCollectionViewFlowLayout.h" +#import "MDCCollectionViewStyleManager.h" +#import "MDCCollectionViewStyleManagerDelegate.h" +#import "MaterialCollectionCells.h" diff --git a/components/Collections/src/private/MDCCollectionGridBackgroundView.h b/components/Collections/src/private/MDCCollectionGridBackgroundView.h new file mode 100644 index 00000000000..e4499df244f --- /dev/null +++ b/components/Collections/src/private/MDCCollectionGridBackgroundView.h @@ -0,0 +1,29 @@ +/* + 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 + +/** + The MDCCollectionGridBackgroundView class provides an implementation of UICollectionReusableView + that displays a background view under cells at each section of a collection view in grid layout. + + This will only happen for a grid layout when it is either A) grouped-style or B) card-style with + zero padding. When this happens, the background for the section cells will not be drawn, and + instead this view will extend to the bounds of the sum of its respective section item frames. + */ +@interface MDCCollectionGridBackgroundView : UICollectionReusableView + +@end diff --git a/components/Collections/src/private/MDCCollectionGridBackgroundView.m b/components/Collections/src/private/MDCCollectionGridBackgroundView.m new file mode 100644 index 00000000000..12e3ec3fa46 --- /dev/null +++ b/components/Collections/src/private/MDCCollectionGridBackgroundView.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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "MDCCollectionGridBackgroundView.h" + +#import "MaterialCollectionLayoutAttributes.h" + +@implementation MDCCollectionGridBackgroundView { + UIImageView *_backgroundImageView; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self commonMDCCollectionGridBackgroundViewInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonMDCCollectionGridBackgroundViewInit]; + } + return self; +} + +- (void)commonMDCCollectionGridBackgroundViewInit { + _backgroundImageView = [[UIImageView alloc] initWithFrame:self.bounds]; + _backgroundImageView.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self addSubview:_backgroundImageView]; +} + +- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes { + [super applyLayoutAttributes:layoutAttributes]; + NSAssert([layoutAttributes isKindOfClass:[MDCCollectionViewLayoutAttributes class]], + @"LayoutAttributes must be a subclass of MDCCollectionViewLayoutAttributes."); + MDCCollectionViewLayoutAttributes *attr = (MDCCollectionViewLayoutAttributes *)layoutAttributes; + _backgroundImageView.image = attr.backgroundImage; +} + +@end diff --git a/components/Collections/src/private/MDCCollectionInfoBarView.h b/components/Collections/src/private/MDCCollectionInfoBarView.h new file mode 100644 index 00000000000..ef71d5de345 --- /dev/null +++ b/components/Collections/src/private/MDCCollectionInfoBarView.h @@ -0,0 +1,184 @@ +/* + 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 + +@class MDCCollectionInfoBarView; + +/** The info bar supplementary view kind when shown in header. */ +extern NSString *__nonnull const MDCCollectionInfoBarKindHeader; + +/** The info bar supplementary view kind when shown in footer. */ +extern NSString *__nonnull const MDCCollectionInfoBarKindFooter; + +/** The animation duration for the info bar. */ +extern const CGFloat MDCCollectionInfoBarAnimationDuration; + +/** Height for the info bar when shown in header. */ +extern const CGFloat MDCCollectionInfoBarHeaderHeight; + +/** Height for the info bar when shown in footer. */ +extern const CGFloat MDCCollectionInfoBarFooterHeight; + +/** The available styles that the info bar can be shown within a collectionView. */ +typedef NS_ENUM(NSUInteger, MDCCollectionInfoBarViewStyle) { + MDCCollectionInfoBarViewStyleActionable, + MDCCollectionInfoBarViewStyleHUD +}; + +/** Delegate protocol for the MDCCollectionInfoBarView. */ +@protocol MDCCollectionInfoBarViewDelegate +@required +/** + Allows the receiver to update the info bar after it has been created. + + @param infoBar The MDCCollectionInfoBarView info bar. + */ +- (void)updateControllerWithInfoBar:(nonnull MDCCollectionInfoBarView *)infoBar; + +@optional +/** + Allows the receiver to get notifed when a tap gesture has been performed on the info bar. + + @param infoBar The MDCCollectionInfoBarView info bar. + */ +- (void)didTapInfoBar:(nonnull MDCCollectionInfoBarView *)infoBar; + +/** + Allows the receiver to get notifed when an info bar will show. + + @param infoBar The MDCCollectionInfoBarView info bar. + @param animated YES the transition will be animated; otherwise, NO. + @param willAutoDismiss YES the info bar will be auto-dismissed after the time interval + set in |autoDismissAfterDuration|. + */ +- (void)infoBar:(nonnull MDCCollectionInfoBarView *)infoBar + willShowAnimated:(BOOL)animated + willAutoDismiss:(BOOL)willAutoDismiss; + +/** + Allows the receiver to get notifed after an info bar did show. + + @param infoBar The MDCCollectionInfoBarView info bar. + @param animated YES the transition was animated; otherwise, NO. + @param willAutoDismiss YES the info bar will be auto-dismissed after the time interval + set in |autoDismissAfterDuration|. + */ +- (void)infoBar:(nonnull MDCCollectionInfoBarView *)infoBar + didShowAnimated:(BOOL)animated + willAutoDismiss:(BOOL)willAutoDismiss; + +/** + Allows the receiver to get notifed when an info bar will dismiss. + + @param infoBar The MDCCollectionInfoBarView info bar. + @param animated YES the transition will be animated; otherwise, NO. + @param willAutoDismiss YES the info bar will be auto-dismissed after the time interval + set in |autoDismissAfterDuration|. + */ +- (void)infoBar:(nonnull MDCCollectionInfoBarView *)infoBar + willDismissAnimated:(BOOL)animated + willAutoDismiss:(BOOL)willAutoDismiss; + +/** + Allows the receiver to get notifed after an info bar has been dismissed. + + @param infoBar The MDCCollectionInfoBarView info bar. + @param animated YES the transition was animated; otherwise, NO. + @param didAutoDismiss YES the info bar was auto-dismissed. + */ +- (void)infoBar:(nonnull MDCCollectionInfoBarView *)infoBar + didDismissAnimated:(BOOL)animated + didAutoDismiss:(BOOL)didAutoDismiss; + +@end + +/** + The MDCCollectionInfoBarView class provides an implementation of + UICollectionReusableView that displays an animated view in either the header + or footer. + */ +@interface MDCCollectionInfoBarView : UICollectionReusableView + +/** A delegate through which the MDCCollectionInfoBarView may inform of updates. */ +@property(nonatomic, weak, nullable) id delegate; + +/** The background view containing the message label. This view is animatable on show/dismiss. */ +@property(nonatomic, readonly, strong, nullable) UIView *backgroundView; + +/** Whether the background view should be shown with a shadow. */ +@property(nonatomic, assign) BOOL shouldApplyBackgroundViewShadow; + +/** + The color assigned to the background view. + + Defaults to #448AFF for header, and white for footer. + */ +@property(nonatomic, strong, null_resettable) UIColor *tintColor; + +/** + The color assigned to the info bar message label. + + Defaults to white for header, and #F44336 for footer. + */ +@property(nonatomic, strong, nullable) UIColor *titleColor; + +/** The info bar message label. */ +@property(nonatomic, readonly, strong, nullable) UILabel *titleLabel; + +/** The info bar message text. */ +@property(nonatomic, strong, nullable) NSString *message; + +/** The horizontal position of bar message. */ +@property(nonatomic, assign) NSTextAlignment textAlignment; + +/** Whether the background view can receive a tap gesture. */ +@property(nonatomic, assign) BOOL allowsTap; + +/** The optional style that the info bar can be shown. */ +@property(nonatomic, assign) MDCCollectionInfoBarViewStyle style; + +/** + The info bar supplementary view kind as set by the collectionView model + |collectionView:viewForSupplementaryElementOfKind:atIndexPath| method. Possible values + are MDCCollectionInfoBarKindHeader or MDCCollectionInfoBarKindFooter. + */ +@property(nonatomic, strong, nonnull) NSString *kind; + +/** + The desired duration after which the info bar will be automatically dismissed. If set to + 0, autoDismiss will be ignored. + */ +@property(nonatomic, assign) NSTimeInterval autoDismissAfterDuration; + +/** Whether the info bar is currently being shown. */ +@property(nonatomic, readonly, assign) BOOL isVisible; + +/** + Shows the info bar with/without animation. + + @param animated YES the transition will be animated; otherwise, NO. + */ +- (void)showAnimated:(BOOL)animated; + +/** + Dismisses the info bar with/without animation. + + @param animated YES the transition will be animated; otherwise, NO. + */ +- (void)dismissAnimated:(BOOL)animated; + +@end diff --git a/components/Collections/src/private/MDCCollectionInfoBarView.m b/components/Collections/src/private/MDCCollectionInfoBarView.m new file mode 100644 index 00000000000..962210fd389 --- /dev/null +++ b/components/Collections/src/private/MDCCollectionInfoBarView.m @@ -0,0 +1,247 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "MDCCollectionInfoBarView.h" + +#import "MaterialShadowLayer.h" +#import "MaterialTypography.h" + +NSString *const MDCCollectionInfoBarKindHeader = @"MDCCollectionInfoBarKindHeader"; +NSString *const MDCCollectionInfoBarKindFooter = @"MDCCollectionInfoBarKindFooter"; + +const CGFloat MDCCollectionInfoBarAnimationDuration = 0.3f; +const CGFloat MDCCollectionInfoBarHeaderHeight = 48.0f; +const CGFloat MDCCollectionInfoBarFooterHeight = 48.0f; + +static const CGFloat MDCCollectionInfoBarLabelHorizontalPadding = 16.0f; + +// Colors derived from http://www.google.com/design/spec/style/color.html#color-color-palette . +static const uint32_t kCollectionInfoBarBlueColor = 0x448AFF; // Blue A200 +static const uint32_t kCollectionInfoBarRedColor = 0xF44336; // Red 500 + +// Creates a UIColor from a 24-bit RGB color encoded as an integer. +static inline UIColor *ColorFromRGB(uint32_t rgbValue) { + return [UIColor colorWithRed:((CGFloat)((rgbValue & 0xFF0000) >> 16)) / 255 + green:((CGFloat)((rgbValue & 0x00FF00) >> 8)) / 255 + blue:((CGFloat)((rgbValue & 0x0000FF) >> 0)) / 255 + alpha:1]; +} + +@interface ShadowedView : UIView +@end + +@implementation ShadowedView ++ (Class)layerClass { + return [MDCShadowLayer class]; +} +@end + +@implementation MDCCollectionInfoBarView { + CGFloat _backgroundTransformY; + UITapGestureRecognizer *_tapGesture; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self commonMDCCollectionInfoBarViewInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonMDCCollectionInfoBarViewInit]; + } + return self; +} + +- (void)commonMDCCollectionInfoBarViewInit { + self.backgroundColor = [UIColor clearColor]; + self.userInteractionEnabled = NO; + _backgroundView = [[ShadowedView alloc] initWithFrame:self.bounds]; + _backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _backgroundView.hidden = YES; + + _titleLabel = + [[UILabel alloc] initWithFrame:CGRectInset(self.bounds, + MDCCollectionInfoBarLabelHorizontalPadding, + 0)]; + _titleLabel.backgroundColor = [UIColor clearColor]; + _titleLabel.textAlignment = NSTextAlignmentCenter; + _titleLabel.font = [MDCTypography body1Font]; + _titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [_backgroundView addSubview:_titleLabel]; + + [self addSubview:_backgroundView]; + + _tapGesture = + [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(handleTapGesture:)]; + [_backgroundView addGestureRecognizer:_tapGesture]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + if (_shouldApplyBackgroundViewShadow) { + [self setShouldApplyBackgroundViewShadow:_shouldApplyBackgroundViewShadow]; + } +} + +- (void)setTintColor:(UIColor *)tintColor { + _tintColor = tintColor; + _backgroundView.backgroundColor = _tintColor; +} + +- (void)setMessage:(NSString *)message { + _message = message; + _titleLabel.text = message; +} + +- (void)setKind:(NSString *)kind { + _kind = kind; + if ([kind isEqualToString:MDCCollectionInfoBarKindHeader]) { + _backgroundTransformY = -MDCCollectionInfoBarHeaderHeight; + } else { + _backgroundTransformY = MDCCollectionInfoBarFooterHeight; + } + _backgroundView.transform = CGAffineTransformMakeTranslation(0, _backgroundTransformY); +} + +- (void)setShouldApplyBackgroundViewShadow:(BOOL)shouldApplyBackgroundViewShadow { + _shouldApplyBackgroundViewShadow = shouldApplyBackgroundViewShadow; + MDCShadowLayer *shadowLayer = (MDCShadowLayer *)_backgroundView.layer; + shadowLayer.elevation = shouldApplyBackgroundViewShadow ? 1 : 0; +} + +- (void)setTextAlignment:(NSTextAlignment)textAlignment { + _textAlignment = textAlignment; + _titleLabel.textAlignment = textAlignment; +} + +- (void)setStyle:(MDCCollectionInfoBarViewStyle)style { + _style = style; + if (style == MDCCollectionInfoBarViewStyleHUD) { + self.allowsTap = NO; + self.shouldApplyBackgroundViewShadow = NO; + self.textAlignment = NSTextAlignmentLeft; + self.tintColor = ColorFromRGB(kCollectionInfoBarBlueColor); + self.titleLabel.textColor = [UIColor whiteColor]; + self.autoDismissAfterDuration = 1.0f; + self.backgroundView.alpha = 0.9f; + } else if (style == MDCCollectionInfoBarViewStyleActionable) { + self.allowsTap = YES; + self.shouldApplyBackgroundViewShadow = YES; + self.textAlignment = NSTextAlignmentCenter; + self.tintColor = [UIColor whiteColor]; + self.titleLabel.textColor = ColorFromRGB(kCollectionInfoBarRedColor); + self.autoDismissAfterDuration = 0.0f; + self.backgroundView.alpha = 1.0f; + self.isAccessibilityElement = YES; + self.accessibilityTraits = UIAccessibilityTraitButton; + self.accessibilityLabel = self.message; + } +} + +- (BOOL)isVisible { + return !_backgroundView.hidden; +} + +- (void)showAnimated:(BOOL)animated { + _backgroundView.hidden = NO; + // Notify delegate. + if ([_delegate respondsToSelector:@selector(infoBar:willShowAnimated:willAutoDismiss:)]) { + [_delegate infoBar:self willShowAnimated:animated willAutoDismiss:[self shouldAutoDismiss]]; + } + + NSTimeInterval duration = (animated) ? MDCCollectionInfoBarAnimationDuration : 0.0f; + [UIView animateWithDuration:duration + delay:0 + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + _backgroundView.transform = CGAffineTransformIdentity; + } + completion:^(BOOL finished) { + self.userInteractionEnabled = _allowsTap; + + // Notify delegate. + if ([_delegate respondsToSelector: + @selector(infoBar:didShowAnimated:willAutoDismiss:)]) { + [_delegate infoBar:self + didShowAnimated:animated + willAutoDismiss:[self shouldAutoDismiss]]; + } + + [self autoDismissIfNecessaryWithAnimation:animated]; + }]; +} + +- (void)dismissAnimated:(BOOL)animated { + // Notify delegate. + if ([_delegate respondsToSelector:@selector(infoBar:willDismissAnimated:willAutoDismiss:)]) { + [_delegate infoBar:self willDismissAnimated:animated willAutoDismiss:[self shouldAutoDismiss]]; + } + + NSTimeInterval duration = (animated) ? MDCCollectionInfoBarAnimationDuration : 0.0f; + [UIView animateWithDuration:duration + delay:0 + options:UIViewAnimationOptionCurveEaseIn + animations:^{ + _backgroundView.transform = + CGAffineTransformMakeTranslation(0, _backgroundTransformY); + } + completion:^(BOOL finished) { + self.userInteractionEnabled = NO; + _backgroundView.hidden = YES; + + // Notify delegate. + if ([_delegate respondsToSelector: + @selector(infoBar:didDismissAnimated:didAutoDismiss:)]) { + [_delegate infoBar:self + didDismissAnimated:animated + didAutoDismiss:[self shouldAutoDismiss]]; + } + }]; +} + +#pragma mark - Private + +- (void)handleTapGesture:(UITapGestureRecognizer *)recognizer { + if ([_delegate respondsToSelector:@selector(didTapInfoBar:)]) { + [_delegate didTapInfoBar:self]; + } +} + +- (BOOL)shouldAutoDismiss { + return (_autoDismissAfterDuration > 0); +} + +- (void)autoDismissIfNecessaryWithAnimation:(BOOL)animation { + if ([self shouldAutoDismiss]) { + dispatch_time_t popTime = + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_autoDismissAfterDuration * NSEC_PER_SEC)); + dispatch_after(popTime, dispatch_get_main_queue(), ^{ + [self dismissAnimated:animation]; + }); + } +} + +@end diff --git a/components/Collections/src/private/MDCCollectionStringResources.h b/components/Collections/src/private/MDCCollectionStringResources.h new file mode 100644 index 00000000000..8abde92d310 --- /dev/null +++ b/components/Collections/src/private/MDCCollectionStringResources.h @@ -0,0 +1,36 @@ +/* + 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 + +/** + Shorthand for returning a resource from MDCCollectionStringResources's singleton. + */ +#define MDCCollectionStringResources(sel) [[MDCCollectionStringResources sharedInstance] sel] + +/** String resources that are used for collection views. */ +@interface MDCCollectionStringResources : NSObject + +/** Returns the shared resources singleton instance. */ ++ (nonnull instancetype)sharedInstance; + +/** Returns cell delete string. */ +- (nonnull NSString *)deleteButtonString; + +/** Returns cell gesture hint string. */ +- (nonnull NSString *)infoBarGestureHintString; + +@end diff --git a/components/Collections/src/private/MDCCollectionStringResources.m b/components/Collections/src/private/MDCCollectionStringResources.m new file mode 100644 index 00000000000..bc9c4068a45 --- /dev/null +++ b/components/Collections/src/private/MDCCollectionStringResources.m @@ -0,0 +1,76 @@ +/* + 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "MDCCollectionStringResources.h" + +#import "MaterialCollectionsStrings.h" +#import "MaterialCollectionsStrings_table.h" + +// The Bundle for string resources. +static NSString *const kBundleName = @"MaterialCollections.bundle"; + +@implementation MDCCollectionStringResources + ++ (instancetype)sharedInstance { + static MDCCollectionStringResources *sharedInstance; + @synchronized(self) { + if (sharedInstance == nil) { + sharedInstance = [[MDCCollectionStringResources alloc] init]; + } + } + return sharedInstance; +} + +- (NSString *)stringForId:(MaterialCollectionsStringId)stringID { + NSString *stringKey = kMaterialCollectionsStringTable[stringID]; + NSBundle *bundle = [[self class] bundle]; + NSString *tableName = [kBundleName stringByDeletingPathExtension]; + return [bundle localizedStringForKey:stringKey value:nil table:tableName]; +} + +- (NSString *)deleteButtonString { + return [self stringForId:kStr_MaterialCollectionsDeleteButton]; +} + +- (NSString *)infoBarGestureHintString { + return [self stringForId:kStr_MaterialCollectionsInfoBarGestureHint]; +} + +#pragma mark - Resource bundle + ++ (NSBundle *)bundle { + static NSBundle *bundle = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + bundle = [NSBundle bundleWithPath:[self bundlePathWithName:kBundleName]]; + }); + + return bundle; +} + ++ (NSString *)bundlePathWithName:(NSString *)bundleName { + // In iOS 8+, we could be included by way of a dynamic framework, and our resource bundles may + // not be in the main .app bundle, but rather in a nested framework, so figure out where we live + // and use that as the search location. + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *resourcePath = [(nil == bundle ? [NSBundle mainBundle] : bundle)resourcePath]; + return [resourcePath stringByAppendingPathComponent:bundleName]; +} +@end diff --git a/components/Collections/src/private/MaterialCollectionsStrings.h b/components/Collections/src/private/MaterialCollectionsStrings.h new file mode 100644 index 00000000000..f91ad5c36f2 --- /dev/null +++ b/components/Collections/src/private/MaterialCollectionsStrings.h @@ -0,0 +1,20 @@ +/* + 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. + */ + +typedef enum { + kStr_MaterialCollectionsInfoBarGestureHint = 0, + kStr_MaterialCollectionsDeleteButton = 1, +} MaterialCollectionsStringId; diff --git a/components/Collections/src/private/MaterialCollectionsStrings_table.h b/components/Collections/src/private/MaterialCollectionsStrings_table.h new file mode 100644 index 00000000000..0b8414be054 --- /dev/null +++ b/components/Collections/src/private/MaterialCollectionsStrings_table.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. + */ + +// A table of string keys to look-up localized strings in the bundle. +// This table is to be indexed using the generated enum. + +static NSString *const kMaterialCollectionsStringTable[] = { + @"InfoBarGestureHint", // Swipe an item to delete + @"DeleteButton", // Delete +}; +#define kNumMaterialCollectionsStrings 2 +#define kMaterialCollectionsStringsOffset 0 +#define kMaterialCollectionsStringsEnd 10000 +static NSString *const kMaterialCollectionsStringsTableName = @"MaterialCollections"; From 4c1030d84099e710f378a669e7b175f9bb4dee1c Mon Sep 17 00:00:00 2001 From: Chris Cox Date: Fri, 22 Apr 2016 12:10:33 -0400 Subject: [PATCH 097/129] Updates podfiles. Reviewers: randallli, #mdc_ios_owners Reviewed By: randallli, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D726 --- catalog/Podfile.lock | 23 +++++++++++++++++++---- demos/Pesto/Podfile.lock | 19 +++++++++++++++++-- demos/Shrine/Podfile.lock | 19 +++++++++++++++++-- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/catalog/Podfile.lock b/catalog/Podfile.lock index 2b92aab9408..d2952d72770 100644 --- a/catalog/Podfile.lock +++ b/catalog/Podfile.lock @@ -3,6 +3,9 @@ PODS: - MaterialComponents/AppBar (= 4.0.1) - MaterialComponents/ButtonBar (= 4.0.1) - MaterialComponents/Buttons (= 4.0.1) + - MaterialComponents/CollectionCells (= 4.0.1) + - MaterialComponents/CollectionLayoutAttributes (= 4.0.1) + - MaterialComponents/Collections (= 4.0.1) - MaterialComponents/FlexibleHeader (= 4.0.1) - MaterialComponents/FontDiskLoader (= 4.0.1) - MaterialComponents/HeaderStackView (= 4.0.1) @@ -32,6 +35,18 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography + - MaterialComponents/CollectionCells (4.0.1): + - MaterialComponents/CollectionLayoutAttributes + - MaterialComponents/Ink + - MaterialComponents/Typography + - MaterialComponents/CollectionLayoutAttributes (4.0.1) + - MaterialComponents/Collections (4.0.1): + - MaterialComponents/CollectionCells + - MaterialComponents/CollectionLayoutAttributes + - MaterialComponents/Ink + - MaterialComponents/ShadowElevations + - MaterialComponents/ShadowLayer + - MaterialComponents/Typography - MaterialComponents/FlexibleHeader (4.0.1) - MaterialComponents/FontDiskLoader (4.0.1) - MaterialComponents/HeaderStackView (4.0.1) @@ -79,14 +94,14 @@ DEPENDENCIES: EXTERNAL SOURCES: MaterialComponents: - :path: ../ + :path: "../" MaterialComponentsCatalog: - :path: ../ + :path: "../" MaterialComponentsUnitTests: - :path: ../ + :path: "../" SPEC CHECKSUMS: - MaterialComponents: 3993ec396d4d7f94073890f0a35c87013762f333 + MaterialComponents: e5e22c3f3bef3695355326d2c595c0d1e85d053f MaterialComponentsCatalog: 4769fcabbbdb4b40373eb428f329dbc4c2b53dac MaterialComponentsUnitTests: 8fa9520a8dae5ee0b7962ce449619b5be13445e6 diff --git a/demos/Pesto/Podfile.lock b/demos/Pesto/Podfile.lock index a301c4c75ea..1455e247133 100644 --- a/demos/Pesto/Podfile.lock +++ b/demos/Pesto/Podfile.lock @@ -3,6 +3,9 @@ PODS: - MaterialComponents/AppBar (= 4.0.1) - MaterialComponents/ButtonBar (= 4.0.1) - MaterialComponents/Buttons (= 4.0.1) + - MaterialComponents/CollectionCells (= 4.0.1) + - MaterialComponents/CollectionLayoutAttributes (= 4.0.1) + - MaterialComponents/Collections (= 4.0.1) - MaterialComponents/FlexibleHeader (= 4.0.1) - MaterialComponents/FontDiskLoader (= 4.0.1) - MaterialComponents/HeaderStackView (= 4.0.1) @@ -32,6 +35,18 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography + - MaterialComponents/CollectionCells (4.0.1): + - MaterialComponents/CollectionLayoutAttributes + - MaterialComponents/Ink + - MaterialComponents/Typography + - MaterialComponents/CollectionLayoutAttributes (4.0.1) + - MaterialComponents/Collections (4.0.1): + - MaterialComponents/CollectionCells + - MaterialComponents/CollectionLayoutAttributes + - MaterialComponents/Ink + - MaterialComponents/ShadowElevations + - MaterialComponents/ShadowLayer + - MaterialComponents/Typography - MaterialComponents/FlexibleHeader (4.0.1) - MaterialComponents/FontDiskLoader (4.0.1) - MaterialComponents/HeaderStackView (4.0.1) @@ -73,9 +88,9 @@ DEPENDENCIES: EXTERNAL SOURCES: MaterialComponents: - :path: ../../ + :path: "../../" SPEC CHECKSUMS: - MaterialComponents: 3993ec396d4d7f94073890f0a35c87013762f333 + MaterialComponents: e5e22c3f3bef3695355326d2c595c0d1e85d053f COCOAPODS: 0.39.0 diff --git a/demos/Shrine/Podfile.lock b/demos/Shrine/Podfile.lock index a301c4c75ea..1455e247133 100644 --- a/demos/Shrine/Podfile.lock +++ b/demos/Shrine/Podfile.lock @@ -3,6 +3,9 @@ PODS: - MaterialComponents/AppBar (= 4.0.1) - MaterialComponents/ButtonBar (= 4.0.1) - MaterialComponents/Buttons (= 4.0.1) + - MaterialComponents/CollectionCells (= 4.0.1) + - MaterialComponents/CollectionLayoutAttributes (= 4.0.1) + - MaterialComponents/Collections (= 4.0.1) - MaterialComponents/FlexibleHeader (= 4.0.1) - MaterialComponents/FontDiskLoader (= 4.0.1) - MaterialComponents/HeaderStackView (= 4.0.1) @@ -32,6 +35,18 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography + - MaterialComponents/CollectionCells (4.0.1): + - MaterialComponents/CollectionLayoutAttributes + - MaterialComponents/Ink + - MaterialComponents/Typography + - MaterialComponents/CollectionLayoutAttributes (4.0.1) + - MaterialComponents/Collections (4.0.1): + - MaterialComponents/CollectionCells + - MaterialComponents/CollectionLayoutAttributes + - MaterialComponents/Ink + - MaterialComponents/ShadowElevations + - MaterialComponents/ShadowLayer + - MaterialComponents/Typography - MaterialComponents/FlexibleHeader (4.0.1) - MaterialComponents/FontDiskLoader (4.0.1) - MaterialComponents/HeaderStackView (4.0.1) @@ -73,9 +88,9 @@ DEPENDENCIES: EXTERNAL SOURCES: MaterialComponents: - :path: ../../ + :path: "../../" SPEC CHECKSUMS: - MaterialComponents: 3993ec396d4d7f94073890f0a35c87013762f333 + MaterialComponents: e5e22c3f3bef3695355326d2c595c0d1e85d053f COCOAPODS: 0.39.0 From 5af7de707cbf034930e6b53a1da50dff48354742 Mon Sep 17 00:00:00 2001 From: Chris Cox Date: Fri, 22 Apr 2016 13:26:00 -0400 Subject: [PATCH 098/129] Updates podfiles. Reviewers: randallli, #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D729 --- catalog/Podfile.lock | 6 +++--- demos/Pesto/Podfile.lock | 2 +- demos/Shrine/Podfile.lock | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/catalog/Podfile.lock b/catalog/Podfile.lock index d2952d72770..3f062ca7493 100644 --- a/catalog/Podfile.lock +++ b/catalog/Podfile.lock @@ -94,11 +94,11 @@ DEPENDENCIES: EXTERNAL SOURCES: MaterialComponents: - :path: "../" + :path: ../ MaterialComponentsCatalog: - :path: "../" + :path: ../ MaterialComponentsUnitTests: - :path: "../" + :path: ../ SPEC CHECKSUMS: MaterialComponents: e5e22c3f3bef3695355326d2c595c0d1e85d053f diff --git a/demos/Pesto/Podfile.lock b/demos/Pesto/Podfile.lock index 1455e247133..2ac40b909f9 100644 --- a/demos/Pesto/Podfile.lock +++ b/demos/Pesto/Podfile.lock @@ -88,7 +88,7 @@ DEPENDENCIES: EXTERNAL SOURCES: MaterialComponents: - :path: "../../" + :path: ../../ SPEC CHECKSUMS: MaterialComponents: e5e22c3f3bef3695355326d2c595c0d1e85d053f diff --git a/demos/Shrine/Podfile.lock b/demos/Shrine/Podfile.lock index 1455e247133..2ac40b909f9 100644 --- a/demos/Shrine/Podfile.lock +++ b/demos/Shrine/Podfile.lock @@ -88,7 +88,7 @@ DEPENDENCIES: EXTERNAL SOURCES: MaterialComponents: - :path: "../../" + :path: ../../ SPEC CHECKSUMS: MaterialComponents: e5e22c3f3bef3695355326d2c595c0d1e85d053f From 634cb3f5240063913ab7eebcaa3238a9d6bd42b2 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Fri, 22 Apr 2016 13:38:31 -0400 Subject: [PATCH 099/129] [howto/build-env] Add Ruby version. Summary: Ruby 2.3.0 generates different Podfile.lock output than Ruby 2.0.0, so documenting our team's use of Ruby 2.0.0 until we all move forward. Reviewers: ajsecord, #mdc_ios_owners Reviewed By: ajsecord, #mdc_ios_owners Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D730 --- howto/build-env/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/howto/build-env/README.md b/howto/build-env/README.md index 1919f21377f..ec12f15ff79 100644 --- a/howto/build-env/README.md +++ b/howto/build-env/README.md @@ -42,3 +42,8 @@ versions. ### iOS All components are expected to support **iOS 7.0 and above**. + +### Ruby + +The core team uses **Ruby 2.0.0**. Newer versions of ruby have subtle modifications that affect our +`Podfile.lock` output. From a037ae0184ade3b7d91595e7180a7fe573808590 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Fri, 22 Apr 2016 12:55:00 -0400 Subject: [PATCH 100/129] [Catalog] Add +injectedInkViewForView extension to MDCInkTouchController and add ink to Catalog's root controller. Summary: This method makes it possible to easily add ink to an arbitrary view without having to subclass the view or store an associated property. We use this to add ink to the Catalog's root collection view. Part of https://github.com/google/material-components-ios/issues/368. Reviewers: ajsecord, #mdc_ios_owners Reviewed By: ajsecord, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D727 --- catalog/MDCCatalog.xcodeproj/project.pbxproj | 6 +++ .../MDCCatalog/MDCCatalog-Bridging-Header.h | 1 + .../MDCCatalogComponentsController.swift | 40 ++++++++++++++++--- .../MDCInkTouchController+Injection.h | 36 +++++++++++++++++ .../MDCInkTouchController+Injection.m | 37 +++++++++++++++++ 5 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 catalog/MDCCatalog/MDCInkTouchController+Injection.h create mode 100644 catalog/MDCCatalog/MDCInkTouchController+Injection.m diff --git a/catalog/MDCCatalog.xcodeproj/project.pbxproj b/catalog/MDCCatalog.xcodeproj/project.pbxproj index b64f8303b65..70e332359e1 100644 --- a/catalog/MDCCatalog.xcodeproj/project.pbxproj +++ b/catalog/MDCCatalog.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 664524B91C6BA62A001ADBF8 /* MDCNodeListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664524B81C6BA62A001ADBF8 /* MDCNodeListViewController.swift */; }; 664524BE1C6BA62A001ADBF8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664524BD1C6BA62A001ADBF8 /* Assets.xcassets */; }; 664524C11C6BA62A001ADBF8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 664524BF1C6BA62A001ADBF8 /* LaunchScreen.storyboard */; }; + 66519B071CCA980600E5423E /* MDCInkTouchController+Injection.m in Sources */ = {isa = PBXBuildFile; fileRef = 66519B061CCA980600E5423E /* MDCInkTouchController+Injection.m */; }; 666CA70D1CAEBCA9001B1884 /* CBCNodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 666CA70A1CAEBCA9001B1884 /* CBCNodeViewController.m */; }; 6681FDFD1CC586660013A0C7 /* MDCCatalogTileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6681FDFC1CC586660013A0C7 /* MDCCatalogTileView.swift */; }; DE1944861CBD9E40009E0321 /* MDCCatalogTileData.m in Sources */ = {isa = PBXBuildFile; fileRef = DE1944681CBD9E40009E0321 /* MDCCatalogTileData.m */; }; @@ -89,6 +90,8 @@ 664524BD1C6BA62A001ADBF8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 664524C01C6BA62A001ADBF8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 664524C21C6BA62A001ADBF8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 66519B051CCA97E100E5423E /* MDCInkTouchController+Injection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MDCInkTouchController+Injection.h"; sourceTree = ""; }; + 66519B061CCA980600E5423E /* MDCInkTouchController+Injection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MDCInkTouchController+Injection.m"; sourceTree = ""; }; 665A34D91C6BD01900962055 /* MDCCatalog-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MDCCatalog-Bridging-Header.h"; sourceTree = ""; }; 666CA7081CAEBCA9001B1884 /* CBCCatalogExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBCCatalogExample.h; sourceTree = ""; }; 666CA7091CAEBCA9001B1884 /* CBCNodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBCNodeViewController.h; sourceTree = ""; }; @@ -195,6 +198,8 @@ 6681FDFC1CC586660013A0C7 /* MDCCatalogTileView.swift */, 0B4A5E6C1CC9307A00D2AC5D /* MDCCatalogWindow.swift */, 664524B81C6BA62A001ADBF8 /* MDCNodeListViewController.swift */, + 66519B051CCA97E100E5423E /* MDCInkTouchController+Injection.h */, + 66519B061CCA980600E5423E /* MDCInkTouchController+Injection.m */, 665A34DE1C6BDAE700962055 /* Resources */, ); path = MDCCatalog; @@ -459,6 +464,7 @@ DE19448C1CBD9E40009E0321 /* MDCCatalogTileDataInk.m in Sources */, 0B4A5E6D1CC9307A00D2AC5D /* MDCCatalogWindow.swift in Sources */, 664524B91C6BA62A001ADBF8 /* MDCNodeListViewController.swift in Sources */, + 66519B071CCA980600E5423E /* MDCInkTouchController+Injection.m in Sources */, DE19448D1CBD9E40009E0321 /* MDCCatalogTileDataMisc.m in Sources */, DE19448B1CBD9E40009E0321 /* MDCCatalogTileDataHeaderStackView.m in Sources */, DE1944881CBD9E40009E0321 /* MDCCatalogTileDataButtonBar.m in Sources */, diff --git a/catalog/MDCCatalog/MDCCatalog-Bridging-Header.h b/catalog/MDCCatalog/MDCCatalog-Bridging-Header.h index 40303f51e17..ef680637595 100644 --- a/catalog/MDCCatalog/MDCCatalog-Bridging-Header.h +++ b/catalog/MDCCatalog/MDCCatalog-Bridging-Header.h @@ -16,3 +16,4 @@ #import "CatalogByConvention.h" #import "MDCCatalogTiles.h" +#import "MDCInkTouchController+Injection.h" diff --git a/catalog/MDCCatalog/MDCCatalogComponentsController.swift b/catalog/MDCCatalog/MDCCatalogComponentsController.swift index 2731c9f60eb..bc8c50332cc 100644 --- a/catalog/MDCCatalog/MDCCatalogComponentsController.swift +++ b/catalog/MDCCatalog/MDCCatalogComponentsController.swift @@ -17,7 +17,7 @@ limitations under the License. import UIKit import MaterialComponents -class MDCCatalogComponentsController: UICollectionViewController { +class MDCCatalogComponentsController: UICollectionViewController, MDCInkTouchControllerDelegate { let spacing = CGFloat(1) let inset = CGFloat(16) @@ -25,6 +25,8 @@ class MDCCatalogComponentsController: UICollectionViewController { var headerViewController: MDCFlexibleHeaderViewController let imageNames = NSMutableArray() + var inkController: MDCInkTouchController? + init(collectionViewLayout ignoredLayout: UICollectionViewLayout, node: CBCNode) { self.node = node @@ -61,6 +63,11 @@ class MDCCatalogComponentsController: UICollectionViewController { override func viewDidLoad() { super.viewDidLoad() + inkController = MDCInkTouchController(view: self.collectionView!)! + inkController!.addInkView() + inkController!.delaysInkSpread = true + inkController!.delegate = self + let containerView = UIView(frame: self.headerViewController.headerView.bounds) containerView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] @@ -120,10 +127,30 @@ class MDCCatalogComponentsController: UICollectionViewController { return node.children.count } + func inkViewForView(view: UIView) -> MDCInkView { + let foundInkView = MDCInkTouchController.injectedInkViewForView(view) + foundInkView.inkStyle = .Unbounded + foundInkView.inkColor = UIColor(red: 0.012, green: 0.663, blue: 0.957, alpha: 0.2) + return foundInkView + } + + // MARK: MDCInkTouchControllerDelegate + + func inkTouchController(inkTouchController: MDCInkTouchController, shouldProcessInkTouchesAtTouchLocation location: CGPoint) -> Bool { + return self.collectionView!.indexPathForItemAtPoint(location) != nil + } + + func inkTouchController(inkTouchController: MDCInkTouchController, inkViewAtTouchLocation location: CGPoint) -> MDCInkView { + if let indexPath = self.collectionView!.indexPathForItemAtPoint(location) { + let cell = self.collectionView!.cellForItemAtIndexPath(indexPath) + return self.inkViewForView(cell!) + } + return MDCInkView() + } + // MARK: UICollectionViewDelegate - override func collectionView(collectionView: UICollectionView, - cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MDCCatalogCollectionViewCell", forIndexPath: indexPath) cell.backgroundColor = UIColor.whiteColor() @@ -133,12 +160,13 @@ class MDCCatalogComponentsController: UICollectionViewController { catalogCell.populateView(componentName) } + // Ensure that ink animations aren't recycled. + MDCInkTouchController.injectedInkViewForView(view).cancelAllAnimationsAnimated(false) + return cell } - func collectionView(collectionView: UICollectionView, - layout collectionViewLayout: UICollectionViewLayout, - sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { + func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { let pad = CGFloat(1) var cellWidth = (self.view.frame.size.width - 3 * pad) / 2 if (self.view.frame.size.width > self.view.frame.size.height) { diff --git a/catalog/MDCCatalog/MDCInkTouchController+Injection.h b/catalog/MDCCatalog/MDCInkTouchController+Injection.h new file mode 100644 index 00000000000..996bec7ae43 --- /dev/null +++ b/catalog/MDCCatalog/MDCInkTouchController+Injection.h @@ -0,0 +1,36 @@ +/* + 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 "MaterialInk.h" + +@interface MDCInkTouchController (ManyInkViews) + +/** + Enumerates the given view's subviews for an instance of MDCInkView and returns it if found, or + creates and adds a new instance of MDCInkView if not. + + This method is a convenience method for adding ink to an arbitrary view without needing to subclass + the target view. Use this method in situations where you expect there to be many distinct ink views + in existence for a single ink touch controller. Example scenarios include: + + - Adding ink to individual collection view/table view cells + + This method can be used in your MDCInkTouchController delegate's + -inkTouchController:inkViewAtTouchLocation; implementation. + */ ++ (nonnull MDCInkView *)injectedInkViewForView:(nonnull UIView *)view; + +@end diff --git a/catalog/MDCCatalog/MDCInkTouchController+Injection.m b/catalog/MDCCatalog/MDCInkTouchController+Injection.m new file mode 100644 index 00000000000..cf67de3ec5d --- /dev/null +++ b/catalog/MDCCatalog/MDCInkTouchController+Injection.m @@ -0,0 +1,37 @@ +/* + 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 "MDCInkTouchController+Injection.h" + +@implementation MDCInkTouchController (ManyInkViews) + ++ (MDCInkView *)injectedInkViewForView:(UIView *)view { + MDCInkView *foundInkView = nil; + for (MDCInkView *subview in view.subviews) { + if ([subview isKindOfClass:[MDCInkView class]]) { + foundInkView = subview; + break; + } + } + if (!foundInkView) { + foundInkView = [[MDCInkView alloc] initWithFrame:view.bounds]; + foundInkView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [view addSubview:foundInkView]; + } + return foundInkView; +} + +@end From 1272a1275b219d252dffcefc7748b0e025e50829 Mon Sep 17 00:00:00 2001 From: randallli Date: Fri, 22 Apr 2016 13:42:41 -0400 Subject: [PATCH 101/129] [PageControl] Created swift example for page control and added it to readme Summary: ongoing work for https://github.com/google/material-components-ios/issues/318 Reviewers: featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Subscribers: featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D672 --- components/PageControl/README.md | 48 ++++++- .../PageControlTypicalUseExample.swift | 123 ++++++++++++++++++ 2 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 components/PageControl/examples/PageControlTypicalUseExample.swift diff --git a/components/PageControl/README.md b/components/PageControl/README.md index 59b3f257f34..f0b4d6de006 100644 --- a/components/PageControl/README.md +++ b/components/PageControl/README.md @@ -156,8 +156,38 @@ notified of page changes. } ~~~ - +#### Swift + +~~~ swift +class PageControlSwiftExampleViewController: UIViewController, UIScrollViewDelegate { + + let pageControl = MDCPageControl() + let scrollView = UIScrollView() + let pages = NSMutableArray() + + override func viewDidLoad() { + super.viewDidLoad() + scrollView.delegate = self + view.addSubview(scrollView) + + pageControl.numberOfPages = pageColors.count + + let pageControlSize = pageControl.sizeThatFits(view.bounds.size) + pageControl.frame = CGRectMake(0, view.bounds.height - pageControlSize.height, view.bounds.width, pageControlSize.height); + pageControl.addTarget(self, action: "didChangePage:", forControlEvents: .ValueChanged) + pageControl.autoresizingMask = [.FlexibleTopMargin, .FlexibleWidth]; + view.addSubview(pageControl) + } + + func didChangePage(sender: MDCPageControl){ + var offset = scrollView.contentOffset + offset.x = CGFloat(sender.currentPage) * scrollView.bounds.size.width; + scrollView.setContentOffset(offset, animated: true) + } + +~~~ + ### Step 2: Forwarding the required scroll view delegate methods @@ -184,4 +214,20 @@ scrolling movement of the designated scroll view. } ~~~ +#### Swift + +~~~ swift +func scrollViewDidScroll(scrollView: UIScrollView) { + pageControl.scrollViewDidScroll(scrollView) +} + +func scrollViewDidEndDecelerating(scrollView: UIScrollView) { + pageControl.scrollViewDidEndDecelerating(scrollView) +} + +func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) { + pageControl.scrollViewDidEndScrollingAnimation(scrollView) +} +~~~ + diff --git a/components/PageControl/examples/PageControlTypicalUseExample.swift b/components/PageControl/examples/PageControlTypicalUseExample.swift new file mode 100644 index 00000000000..1a20da3b503 --- /dev/null +++ b/components/PageControl/examples/PageControlTypicalUseExample.swift @@ -0,0 +1,123 @@ +/* +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 Foundation + +import MaterialComponents + +class PageControlSwiftExampleViewController: UIViewController, UIScrollViewDelegate { + + let pageControl = MDCPageControl() + let scrollView = UIScrollView() + let pages = NSMutableArray() + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = UIColor.whiteColor() + + let pageColors = [ + self.dynamicType.ColorFromRGB(0x55C4f5), + self.dynamicType.ColorFromRGB(0x35B7F3), + self.dynamicType.ColorFromRGB(0x1EAAF1), + self.dynamicType.ColorFromRGB(0x55C4f5), + self.dynamicType.ColorFromRGB(0x35B7F3), + self.dynamicType.ColorFromRGB(0x1EAAF1), + ]; + + scrollView.frame = self.view.bounds + scrollView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] + scrollView.delegate = self + scrollView.pagingEnabled = true + scrollView.contentSize = CGSizeMake(view.bounds.width * CGFloat(pageColors.count), view.bounds.height) + scrollView.showsHorizontalScrollIndicator = false; + view.addSubview(scrollView) + + // Add pages to scrollView. + for (var i:NSInteger = 0; i < pageColors.count; i++) { + let pageFrame:CGRect = CGRectOffset(self.view.bounds, CGFloat(i) * view.bounds.width, 0); + let page = UILabel.init(frame:pageFrame) + page.text = String(format: "Page %zd", i + 1) + page.font = page.font.fontWithSize(50) + page.textColor = UIColor.init(white: 0, alpha: 0.8) + page.backgroundColor = pageColors[i]; + page.textAlignment = NSTextAlignment.Center; + page.autoresizingMask = [.FlexibleTopMargin, .FlexibleBottomMargin] + scrollView.addSubview(page) + pages.addObject(page) + } + + pageControl.numberOfPages = pageColors.count + + let pageControlSize = pageControl.sizeThatFits(view.bounds.size) + pageControl.frame = CGRectMake(0, view.bounds.height - pageControlSize.height, view.bounds.width, pageControlSize.height); + pageControl.addTarget(self, action: "didChangePage:", forControlEvents: .ValueChanged) + pageControl.autoresizingMask = [.FlexibleTopMargin, .FlexibleWidth]; + view.addSubview(pageControl) + } + + // MARK: - Frame changes + + override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + let pageBeforeFrameChange = pageControl.currentPage; + for (var i = 0; i < pages.count; i++) { + let page:UILabel = pages.objectAtIndex(i) as! UILabel + page.frame = CGRectOffset(view.bounds, CGFloat(i) * view.bounds.width, 0); + } + scrollView.contentSize = CGSizeMake(view.bounds.width * CGFloat(pages.count), view.bounds.height); + var offset = scrollView.contentOffset; + offset.x = CGFloat(pageBeforeFrameChange) * view.bounds.width; + // This non-anmiated change of offset ensures we keep the same page + scrollView.contentOffset = offset; + } + + // MARK: - UIScrollViewDelegate + + func scrollViewDidScroll(scrollView: UIScrollView) { + pageControl.scrollViewDidScroll(scrollView) + } + + func scrollViewDidEndDecelerating(scrollView: UIScrollView) { + pageControl.scrollViewDidEndDecelerating(scrollView) + } + + func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) { + pageControl.scrollViewDidEndScrollingAnimation(scrollView) + } + + // MARK: - User events + + func didChangePage(sender: MDCPageControl){ + var offset = scrollView.contentOffset + offset.x = CGFloat(sender.currentPage) * scrollView.bounds.size.width; + scrollView.setContentOffset(offset, animated: true) + } + + // MARK: CatalogByConvention + + class func catalogBreadcrumbs() -> [String] { + return [ "Page Control", "Swift example"] + } + + // Creates a UIColor from a 24-bit RGB color encoded as an integer. + // Pass in hex color values like so: ColorFromRGB(0x1EAAF1). + class func ColorFromRGB(rgbValue: UInt32) -> UIColor { + return UIColor.init(red: ((CGFloat)((rgbValue & 0xFF0000) >> 16)) / 255, + green: ((CGFloat)((rgbValue & 0x00FF00) >> 8)) / 255, + blue: ((CGFloat)((rgbValue & 0x0000FF) >> 0)) / 255, + alpha: 1) + } +} From 6dfac14b518bd81dc62c3398f4416a3cf1fe57e3 Mon Sep 17 00:00:00 2001 From: Ian Gordon Date: Fri, 22 Apr 2016 10:57:39 -0400 Subject: [PATCH 102/129] [Ink] Update README to include Swift examples Reviewers: randallli, #mdc_ios_owners, featherless Reviewed By: randallli, #mdc_ios_owners, featherless Subscribers: featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D676 --- components/Ink/README.md | 47 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/components/Ink/README.md b/components/Ink/README.md index 2d40bb1d0f7..1d8d16bc561 100644 --- a/components/Ink/README.md +++ b/components/Ink/README.md @@ -71,6 +71,7 @@ Before using Ink, you'll need to import it: ~~~ swift import MaterialComponents ~~~ + The Ink component exposes two interfaces that you can use to add material-like @@ -88,6 +89,7 @@ The simplest method of using ink in your views is to use a `MDCInkTouchController`: + #### Objective-C ~~~ objc UIButton *myButton = [UIButton buttonWithType:UIButtonTypeSystem]; @@ -96,6 +98,15 @@ MDCInkTouchController *inkTouchController = [[MDCInkTouchController alloc] initWithView:myButton]; [inkTouchController addInkView]; ~~~ + +#### Swift +~~~ swift +let myButton = UIButton(type: .System) +myButton.setTitle("Tap Me", forState: .Normal) +let inkTouchController = MDCInkTouchController(view: myButton) +inkTouchController?.addInkView() +~~~ + @@ -107,6 +118,7 @@ touches, the following code uses the delegate's `inkTouchController:shouldProcessInkTouchesAtTouchLocation:` method: + #### Objective-C ~~~ objc @interface MyDelegate @@ -132,6 +144,28 @@ inkTouchController.delegate = myDelegate; ~~~ +#### Swift +~~~ swift +class MyDelegate: NSObject, MDCInkTouchControllerDelegate { + + func inkTouchController(inkTouchController: MDCInkTouchController, + shouldProcessInkTouchesAtTouchLocation location: CGPoint) -> Bool { + // Determine if we want to display the ink + return true + } + +} + +... + +let myButton = UIButton(type: .System) +let myDelegate = MyDelegate() +let inkTouchController = MDCInkTouchController(view: myButton) +inkTouchController?.delegate = myDelegate +inkTouchController?.addInkView() + +~~~ + ### MDCInkView @@ -140,6 +174,7 @@ Alternatively, you can use MCDInkView directly to display ink ripples using your own touch processing: + #### Objective-C ~~~ objc MyCustomView *myCustomView = [[MyCustomView alloc] initWithFrame:CGRectZero]; @@ -147,7 +182,17 @@ MDCInkView *inkView = [[MDCInkView alloc] init]; inkView.inkColor = [UIColor redColor]; [myCustomView addSubview:inkView]; ... -[inkView spreadFromPoint:CGPointMake(100, 100) completion:NULL]; +[inkView spreadInkFromPoint:CGPointMake(100, 100) completion:NULL]; +~~~ + +#### Swift +~~~ swift +let myCustomView = MyCustomView(frame: CGRectZero) +let inkView = MDCInkView() +inkView.inkColor = UIColor.redColor() +myCustomView.addSubview(inkView) +... +myCustomView.spreadInk(CGPoint(), completion:nil) ~~~ From e46a7a032171b138b2fd62c74e1d8893fd95b166 Mon Sep 17 00:00:00 2001 From: Ian Gordon Date: Thu, 21 Apr 2016 18:39:43 -0400 Subject: [PATCH 103/129] [FlexibleHeader] Update README to include Swift examples Reviewers: featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Subscribers: featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D718 --- components/FlexibleHeader/README.md | 57 ++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/components/FlexibleHeader/README.md b/components/FlexibleHeader/README.md index 86dd1dc06f2..1380d549eb9 100644 --- a/components/FlexibleHeader/README.md +++ b/components/FlexibleHeader/README.md @@ -148,6 +148,19 @@ controller to a MDCFlexibleHeaderView instance. #### Swift ~~~ swift +let headerViewController = MDCFlexibleHeaderViewController() + +override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + + addChildViewController(headerViewController) +} + +required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + + addChildViewController(headerViewController) +} ~~~ @@ -171,6 +184,13 @@ ensure that the Flexible Header is in front of all other views. #### Swift ~~~ swift +override func viewDidLoad() { + super.viewDidLoad() + + headerViewController.view.frame = view.bounds + view.addSubview(headerViewController.view) + headerViewController.didMoveToParentViewController(self) +} ~~~ @@ -197,7 +217,7 @@ self.headerViewController.headerView.trackingScrollView = scrollView; #### Swift ~~~ swift -self.headerViewController.headerView.trackingScrollView = scrollView +headerViewController.headerView.trackingScrollView = scrollView ~~~ @@ -220,7 +240,7 @@ scrollView.delegate = self.headerViewController; #### Swift ~~~ swift -scrollView.delegate = self.headerViewController +scrollView.delegate = headerViewController ~~~ @@ -264,27 +284,29 @@ UIScrollView subclass. #### Swift ~~~ swift +// MARK: UIScrollViewDelegate + override func scrollViewDidScroll(scrollView: UIScrollView) { - if scrollView == self.headerViewController.headerView.trackingScrollView { - self.headerViewController.headerView.trackingScrollViewDidScroll() + if scrollView == headerViewController.headerView.trackingScrollView { + headerViewController.headerView.trackingScrollViewDidScroll() } } override func scrollViewDidEndDecelerating(scrollView: UIScrollView) { - if scrollView == self.headerViewController.headerView.trackingScrollView { - self.headerViewController.headerView.trackingScrollViewDidEndDecelerating() + if scrollView == headerViewController.headerView.trackingScrollView { + headerViewController.headerView.trackingScrollViewDidEndDecelerating() } } override func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) { - let headerView = self.headerViewController.headerView + let headerView = headerViewController.headerView if scrollView == headerView.trackingScrollView { headerView.trackingScrollViewDidEndDraggingWillDecelerate(decelerate) } } override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { - let headerView = self.headerViewController.headerView + let headerView = headerViewController.headerView if scrollView == headerView.trackingScrollView { headerView.trackingScrollViewWillEndDraggingWithVelocity(velocity, targetContentOffset: targetContentOffset) } @@ -354,7 +376,7 @@ MDCFlexibleHeaderViewController instance's `layoutDelegate`. // Set yourself as the delegate. headerViewController.layoutDelegate = self; -#pragma mark - MDCFlexibleHeaderViewLayoutDelegate +#pragma - MDCFlexibleHeaderViewLayoutDelegate - (void)flexibleHeaderViewController:(MDCFlexibleHeaderViewController *)flexibleHeaderViewController flexibleHeaderViewFrameDidChange:(MDCFlexibleHeaderView *)flexibleHeaderView { @@ -365,6 +387,14 @@ headerViewController.layoutDelegate = self; #### Swift ~~~ swift +class MyViewController: UIViewController, MDCFlexibleHeaderViewLayoutDelegate { + + // MARK: MDCFlexibleHeaderViewLayoutDelegate + func flexibleHeaderViewController(flexibleHeaderViewController: MDCFlexibleHeaderViewController, + flexibleHeaderViewFrameDidChange flexibleHeaderView: MDCFlexibleHeaderView) { + // Called whenever the frame changes. + } +} ~~~ @@ -383,6 +413,7 @@ take the z-index into account: #### Swift ~~~ swift +view.insertSubview(myCustomView, belowSubview: headerViewController.headerView) ~~~ @@ -412,7 +443,7 @@ animating in/out in a reasonable manner. override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) - self.navigationController?.setNavigationBarHidden(true, animated: animated) + navigationController?.setNavigationBarHidden(true, animated: animated) } ~~~ @@ -434,7 +465,7 @@ Add the following to view controllers that don't have an app bar: override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) - self.navigationController?.setNavigationBarHidden(false, animated: animated) + navigationController?.setNavigationBarHidden(false, animated: animated) } ~~~ @@ -474,7 +505,7 @@ controller. #### Swift ~~~ swift override func childViewControllerForStatusBarStyle() -> UIViewController? { - return self.headerViewController + return headerViewController } ~~~ @@ -499,7 +530,7 @@ imageView.clipsToBounds = YES; #### Swift ~~~ swift -let headerView = self.headerViewController!.headerView +let headerView = headerViewController!.headerView let imageView = ... imageView.frame = headerView.bounds From 132750ec68e73e38a72ac096a210070d541616bb Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Fri, 22 Apr 2016 14:08:44 -0400 Subject: [PATCH 104/129] [Catalog] Import proper header. Summary: Resolves build breakage introduced in a037ae0184ade3b7d91595e7180a7fe573808590.. Reviewers: ajsecord, #mdc_ios_owners Reviewed By: ajsecord, #mdc_ios_owners Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D733 --- catalog/MDCCatalog/MDCInkTouchController+Injection.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalog/MDCCatalog/MDCInkTouchController+Injection.h b/catalog/MDCCatalog/MDCInkTouchController+Injection.h index 996bec7ae43..8ffd4b66992 100644 --- a/catalog/MDCCatalog/MDCInkTouchController+Injection.h +++ b/catalog/MDCCatalog/MDCInkTouchController+Injection.h @@ -14,7 +14,7 @@ limitations under the License. */ -#import "MaterialInk.h" +#import @interface MDCInkTouchController (ManyInkViews) From 60ffaa55909fd931d976ecbd55f91b5d71394ce4 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Fri, 22 Apr 2016 14:32:33 -0400 Subject: [PATCH 105/129] [Collections] Replace EditingManager with an Editing protocol. Reviewers: cjcox, randallli, #mdc_ios_owners Reviewed By: cjcox, randallli, #mdc_ios_owners Subscribers: randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D719 --- .../examples/CollectionCellsLayoutExample.m | 4 +- .../CollectionsCellAccessoryExample.m | 4 +- .../examples/CollectionsEditingExample.m | 8 +-- .../CollectionsSwipeToDismissRowExample.m | 2 +- .../CollectionsSwipeToDismissSectionExample.m | 2 +- .../src/MDCCollectionViewController.h | 9 ++-- .../src/MDCCollectionViewController.m | 14 ++--- ...ngManager.h => MDCCollectionViewEditing.h} | 51 ++++++------------- ...e.h => MDCCollectionViewEditingDelegate.h} | 2 +- .../src/MDCCollectionViewFlowLayout.m | 32 ++++++------ .../Collections/src/MaterialCollections.h | 4 +- .../src/private/MDCCollectionViewEditor.h | 34 +++++++++++++ .../MDCCollectionViewEditor.m} | 19 ++++--- 13 files changed, 102 insertions(+), 83 deletions(-) rename components/Collections/src/{MDCCollectionViewEditingManager.h => MDCCollectionViewEditing.h} (51%) rename components/Collections/src/{MDCCollectionViewEditingManagerDelegate.h => MDCCollectionViewEditingDelegate.h} (99%) create mode 100644 components/Collections/src/private/MDCCollectionViewEditor.h rename components/Collections/src/{MDCCollectionViewEditingManager.m => private/MDCCollectionViewEditor.m} (98%) diff --git a/components/CollectionCells/examples/CollectionCellsLayoutExample.m b/components/CollectionCells/examples/CollectionCellsLayoutExample.m index 5d07f40184d..9fb186aa914 100644 --- a/components/CollectionCells/examples/CollectionCellsLayoutExample.m +++ b/components/CollectionCells/examples/CollectionCellsLayoutExample.m @@ -154,7 +154,7 @@ - (CGFloat)collectionView:(UICollectionView *)collectionView return MDCCellDefaultOneLineHeight; } -#pragma mark - +#pragma mark - - (BOOL)collectionViewAllowsEditing:(UICollectionView *)collectionView { return YES; @@ -178,7 +178,7 @@ - (BOOL)collectionView:(UICollectionView *)collectionView - (void)didSwitch:(id)sender { MDCSwitch *switchControl = sender; - [self.editingManager setEditing:switchControl.isOn animated:YES]; + [self.editor setEditing:switchControl.isOn animated:YES]; } #pragma mark - Private helper methods diff --git a/components/Collections/examples/CollectionsCellAccessoryExample.m b/components/Collections/examples/CollectionsCellAccessoryExample.m index 001ee015dfe..478ad9a3e71 100644 --- a/components/Collections/examples/CollectionsCellAccessoryExample.m +++ b/components/Collections/examples/CollectionsCellAccessoryExample.m @@ -93,7 +93,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView return cell; } -#pragma mark - +#pragma mark - - (BOOL)collectionViewAllowsEditing:(UICollectionView *)collectionView { return NO; @@ -117,7 +117,7 @@ - (BOOL)collectionView:(UICollectionView *)collectionView - (void)didSwitch:(id)sender { MDCSwitch *switchControl = sender; - [self.editingManager setEditing:switchControl.isOn animated:YES]; + [self.editor setEditing:switchControl.isOn animated:YES]; } @end diff --git a/components/Collections/examples/CollectionsEditingExample.m b/components/Collections/examples/CollectionsEditingExample.m index e1af20304ff..eb5127683ba 100644 --- a/components/Collections/examples/CollectionsEditingExample.m +++ b/components/Collections/examples/CollectionsEditingExample.m @@ -66,9 +66,9 @@ - (void)updatedRightBarButtonItem:(BOOL)isEditing { } - (void)toggleEditMode:(id)sender { - BOOL isEditing = self.editingManager.isEditing; + BOOL isEditing = self.editor.isEditing; [self updatedRightBarButtonItem:!isEditing]; - [self.editingManager setEditing:!isEditing animated:YES]; + [self.editor setEditing:!isEditing animated:YES]; } #pragma mark - @@ -91,7 +91,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView return cell; } -#pragma mark - +#pragma mark - - (BOOL)collectionViewAllowsEditing:(UICollectionView *)collectionView { return YES; @@ -102,7 +102,7 @@ - (BOOL)collectionViewAllowsReordering:(UICollectionView *)collectionView { } - (BOOL)collectionViewAllowsSwipeToDismissItem:(UICollectionView *)collectionView { - return self.editingManager.isEditing; + return self.editor.isEditing; } - (void)collectionView:(UICollectionView *)collectionView diff --git a/components/Collections/examples/CollectionsSwipeToDismissRowExample.m b/components/Collections/examples/CollectionsSwipeToDismissRowExample.m index 4389001d009..3fb876c9e1f 100644 --- a/components/Collections/examples/CollectionsSwipeToDismissRowExample.m +++ b/components/Collections/examples/CollectionsSwipeToDismissRowExample.m @@ -77,7 +77,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView return cell; } -#pragma mark - +#pragma mark - - (BOOL)collectionViewAllowsSwipeToDismissItem:(UICollectionView *)collectionView { return YES; diff --git a/components/Collections/examples/CollectionsSwipeToDismissSectionExample.m b/components/Collections/examples/CollectionsSwipeToDismissSectionExample.m index 10efcaa4854..23046a1dbfc 100644 --- a/components/Collections/examples/CollectionsSwipeToDismissSectionExample.m +++ b/components/Collections/examples/CollectionsSwipeToDismissSectionExample.m @@ -79,7 +79,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView return cell; } -#pragma mark - +#pragma mark - - (BOOL)collectionViewAllowsSwipeToDismissSection:(UICollectionView *)collectionView { return YES; diff --git a/components/Collections/src/MDCCollectionViewController.h b/components/Collections/src/MDCCollectionViewController.h index 7a2c739b8e9..2cc157eee61 100644 --- a/components/Collections/src/MDCCollectionViewController.h +++ b/components/Collections/src/MDCCollectionViewController.h @@ -16,8 +16,8 @@ #import -#import "MDCCollectionViewEditingManager.h" -#import "MDCCollectionViewEditingManagerDelegate.h" +#import "MDCCollectionViewEditing.h" +#import "MDCCollectionViewEditingDelegate.h" #import "MDCCollectionViewStyleManager.h" #import "MDCCollectionViewStyleManagerDelegate.h" @@ -27,7 +27,7 @@ */ @interface MDCCollectionViewController : UICollectionViewController < /** Allows for editing notifications/permissions. */ - MDCCollectionViewEditingManagerDelegate, + MDCCollectionViewEditingDelegate, /** Allows for styling updates. */ MDCCollectionViewStyleManagerDelegate, @@ -39,8 +39,7 @@ @property(nonatomic, strong, readonly, nonnull) MDCCollectionViewStyleManager *styleManager; /** The collection view editing manager. */ -@property(nonatomic, strong, readonly, nonnull) - MDCCollectionViewEditingManager *editingManager; +@property(nonatomic, strong, readonly, nonnull) id editor; #pragma mark - Subclassing diff --git a/components/Collections/src/MDCCollectionViewController.m b/components/Collections/src/MDCCollectionViewController.m index a8b92888b26..3619ad83f17 100644 --- a/components/Collections/src/MDCCollectionViewController.m +++ b/components/Collections/src/MDCCollectionViewController.m @@ -25,6 +25,7 @@ #import "MaterialInk.h" #import "private/MDCCollectionInfoBarView.h" #import "private/MDCCollectionStringResources.h" +#import "private/MDCCollectionViewEditor.h" #import @@ -82,9 +83,8 @@ - (void)viewDidLoad { _styleManager.delegate = self; // Editing manager. - _editingManager = - [[MDCCollectionViewEditingManager alloc] initWithCollectionView:self.collectionView]; - _editingManager.delegate = self; + _editor = [[MDCCollectionViewEditor alloc] initWithCollectionView:self.collectionView]; + _editor.delegate = self; // Set up ink touch controller. _inkTouchController = [[MDCInkTouchController alloc] initWithView:self.collectionView]; @@ -353,7 +353,7 @@ - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView - (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath { - if (_editingManager.isEditing) { + if (_editor.isEditing) { if ([self collectionView:collectionView canEditItemAtIndexPath:indexPath]) { return [self collectionView:collectionView canSelectItemDuringEditingAtIndexPath:indexPath]; } @@ -377,7 +377,7 @@ - (void)collectionView:(UICollectionView *)collectionView [self updateFooterInfoBarIfNecessary]; } -#pragma mark - +#pragma mark - - (BOOL)collectionViewAllowsEditing:(UICollectionView *)collectionView { return NO; @@ -518,7 +518,7 @@ - (void)deleteSections:(NSIndexSet *)sections { } - (void)updateHeaderInfoBarIfNecessary { - if (_editingManager.isEditing) { + if (_editor.isEditing) { // Show HUD only once before autodissmissing. BOOL allowsSwipeToDismissItem = NO; if ([self respondsToSelector:@selector(collectionViewAllowsSwipeToDismissItem:)]) { @@ -535,7 +535,7 @@ - (void)updateHeaderInfoBarIfNecessary { - (void)updateFooterInfoBarIfNecessary { NSInteger selectedItemCount = [self.collectionView.indexPathsForSelectedItems count]; - if (_editingManager.isEditing) { + if (_editor.isEditing) { // Invalidate layout to add info bar if necessary. [self.collectionView.collectionViewLayout invalidateLayout]; if (_footerInfoBar) { diff --git a/components/Collections/src/MDCCollectionViewEditingManager.h b/components/Collections/src/MDCCollectionViewEditing.h similarity index 51% rename from components/Collections/src/MDCCollectionViewEditingManager.h rename to components/Collections/src/MDCCollectionViewEditing.h index c47af6f2b1d..f9fb1490478 100644 --- a/components/Collections/src/MDCCollectionViewEditingManager.h +++ b/components/Collections/src/MDCCollectionViewEditing.h @@ -16,54 +16,33 @@ #import -@protocol MDCCollectionViewEditingManagerDelegate; +@protocol MDCCollectionViewEditingDelegate; -/** - The MDCCollectionViewEditingManager class provides an implementation for a UICollectionView to - set its editing properties. - */ -@interface MDCCollectionViewEditingManager : NSObject - -/** Unavailable superclass initializers. */ -- (nonnull instancetype)init NS_UNAVAILABLE; -- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder NS_UNAVAILABLE; - -/** - Initialize the controller with a collection view. - - Designated initializer. - - @param collectionView The controller's collection view. - */ -- (nonnull instancetype)initWithCollectionView:(nonnull UICollectionView *)collectionView - NS_DESIGNATED_INITIALIZER; +/** The MDCCollectionViewEditing protocol defines the editing state for a UICollectionView. */ +@protocol MDCCollectionViewEditing -/** The controller's collection view. */ +/** The associated collection view. */ @property(nonatomic, readonly, weak, nullable) UICollectionView *collectionView; -/** - A delegate through which the MDCCollectionViewEditingManager may inform of changes in status. - */ -@property(nonatomic, weak, nullable) id delegate; +/** The delegate will be informed of editing state changes. */ +@property(nonatomic, weak, nullable) id delegate; -/** - The index path of a cell that is currently being moved/reordered within a collection View. - */ +/** The index path of the cell being moved or reordered, if any. */ @property(nonatomic, readonly, strong, nullable) NSIndexPath *reorderingCellIndexPath; -/** - The index path of a cell that is currently being dragged for dismissal within a collection View. - */ +/** The index path of the cell being dragged for dismissal, if any. */ @property(nonatomic, readonly, strong, nullable) NSIndexPath *dismissingCellIndexPath; -/** The section being dragged for dismissal within a collection View. */ +/** The index of the section being dragged for dismissal, or NSNotFound if none. */ @property(nonatomic, readonly, assign) NSInteger dismissingSection; /** - A boolean value indicating whether the a visible cell within the collectionView is being - edited. When set, all rows show or hide editing controls without animation. To animate the - state change see @c setEditing:animated:. Setting the editing state of this class does not - propagate to the parent view controller's editing state. + A Boolean value indicating whether the a visible cell within the collectionView is being + edited. + + When set, all rows show or hide editing controls without animation. To animate the state change see + @c setEditing:animated:. Setting the editing state of this class does not propagate to the parent + view controller's editing state. */ @property(nonatomic, getter=isEditing) BOOL editing; diff --git a/components/Collections/src/MDCCollectionViewEditingManagerDelegate.h b/components/Collections/src/MDCCollectionViewEditingDelegate.h similarity index 99% rename from components/Collections/src/MDCCollectionViewEditingManagerDelegate.h rename to components/Collections/src/MDCCollectionViewEditingDelegate.h index a35a9088cbe..bda7364657f 100644 --- a/components/Collections/src/MDCCollectionViewEditingManagerDelegate.h +++ b/components/Collections/src/MDCCollectionViewEditingDelegate.h @@ -24,7 +24,7 @@ - Individual cells being swiped horizontally for dismissal. - Entire cell sections being swiped horizontally for dismissal. */ -@protocol MDCCollectionViewEditingManagerDelegate +@protocol MDCCollectionViewEditingDelegate @optional diff --git a/components/Collections/src/MDCCollectionViewFlowLayout.m b/components/Collections/src/MDCCollectionViewFlowLayout.m index db4a499ba3b..e723553ea73 100644 --- a/components/Collections/src/MDCCollectionViewFlowLayout.m +++ b/components/Collections/src/MDCCollectionViewFlowLayout.m @@ -21,12 +21,12 @@ #import "MDCCollectionViewFlowLayout.h" #import "MDCCollectionViewController.h" -#import "MDCCollectionViewEditingManager.h" -#import "MDCCollectionViewEditingManagerDelegate.h" +#import "MDCCollectionViewEditingDelegate.h" #import "MDCCollectionViewStyleManager.h" #import "MaterialCollectionLayoutAttributes.h" #import "private/MDCCollectionGridBackgroundView.h" #import "private/MDCCollectionInfoBarView.h" +#import "private/MDCCollectionViewEditor.h" #import @@ -60,11 +60,11 @@ - (instancetype)init { return self; } -- (MDCCollectionViewEditingManager *)editingManager { +- (id)editor { if ([self.collectionView.delegate isKindOfClass:[MDCCollectionViewController class]]) { MDCCollectionViewController *controller = (MDCCollectionViewController *)self.collectionView.delegate; - return controller.editingManager; + return controller.editor; } return nil; } @@ -108,7 +108,7 @@ - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { if (!CGSizeEqualToSize(self.collectionView.bounds.size, newBounds.size) || - self.editingManager.isEditing) { + self.editor.isEditing) { [self invalidateLayout]; return YES; } @@ -318,7 +318,7 @@ - (void)storeSupplementaryViewsWithAttributes:(NSArray *)attributes { - (MDCCollectionViewLayoutAttributes *)updateAttribute:(MDCCollectionViewLayoutAttributes *)attr { if (attr.representedElementKind == UICollectionElementCategoryCell) { - attr.editing = self.editingManager.isEditing; + attr.editing = self.editor.isEditing; } attr.isGridLayout = NO; if (self.styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeList) { @@ -495,9 +495,9 @@ - (void)updateCellStateMaskWithAttribute:(MDCCollectionViewLayoutAttributes *)at // Determine proper state to show cell if editing. if (attr.editing) { if ([self.collectionView.dataSource - conformsToProtocol:@protocol(MDCCollectionViewEditingManagerDelegate)]) { - id editingDelegate = - (id)self.collectionView.dataSource; + conformsToProtocol:@protocol(MDCCollectionViewEditingDelegate)]) { + id editingDelegate = + (id)self.collectionView.dataSource; // Check if delegate can select during editing. if ([editingDelegate respondsToSelector: @@ -585,20 +585,20 @@ - (void)inlayAttributeIfNecessary:(MDCCollectionViewLayoutAttributes *)attr { } - (void)hideAttributeIfNecessary:(MDCCollectionViewLayoutAttributes *)attr { - // Hide the attribute if the MDCCollectionViewEditingManager is either currently handling - // a cell item or section swipe for dismissal, or is reordering a cell item. - if (self.editingManager) { + if (self.editor) { + // Hide the attribute if the editor is either currently handling a cell item or section swipe + // for dismissal, or is reordering a cell item. BOOL isCell = attr.representedElementCategory == UICollectionElementCategoryCell; - if (attr.indexPath.section == self.editingManager.dismissingSection || - ([attr.indexPath isEqual:self.editingManager.dismissingCellIndexPath] && isCell) || - ([attr.indexPath isEqual:self.editingManager.reorderingCellIndexPath] && isCell)) { + if (attr.indexPath.section == self.editor.dismissingSection || + ([attr.indexPath isEqual:self.editor.dismissingCellIndexPath] && isCell) || + ([attr.indexPath isEqual:self.editor.reorderingCellIndexPath] && isCell)) { attr.hidden = YES; } } } - (void)addInfoBarAttributesIfNecessary:(NSMutableArray *)attributes { - if (self.editingManager.isEditing && [attributes count] > 0) { + if (self.editor.isEditing && [attributes count] > 0) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; // Add header info bar if editing. diff --git a/components/Collections/src/MaterialCollections.h b/components/Collections/src/MaterialCollections.h index d0f063c9f68..bf47b7438c8 100644 --- a/components/Collections/src/MaterialCollections.h +++ b/components/Collections/src/MaterialCollections.h @@ -15,8 +15,8 @@ */ #import "MDCCollectionViewController.h" -#import "MDCCollectionViewEditingManager.h" -#import "MDCCollectionViewEditingManagerDelegate.h" +#import "MDCCollectionViewEditing.h" +#import "MDCCollectionViewEditingDelegate.h" #import "MDCCollectionViewFlowLayout.h" #import "MDCCollectionViewStyleManager.h" #import "MDCCollectionViewStyleManagerDelegate.h" diff --git a/components/Collections/src/private/MDCCollectionViewEditor.h b/components/Collections/src/private/MDCCollectionViewEditor.h new file mode 100644 index 00000000000..99d6e430f7d --- /dev/null +++ b/components/Collections/src/private/MDCCollectionViewEditor.h @@ -0,0 +1,34 @@ +/* + 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 "MDCCollectionViewEditing.h" + +/** + The MDCCollectionViewEditingManager class provides an implementation for a UICollectionView to + set its editing properties. + */ +@interface MDCCollectionViewEditor : NSObject + +/** + Initialize the controller with a collection view. + + Designated initializer. + + @param collectionView The controller's collection view. + */ +- (instancetype)initWithCollectionView:(UICollectionView *)collectionView NS_DESIGNATED_INITIALIZER; + +@end diff --git a/components/Collections/src/MDCCollectionViewEditingManager.m b/components/Collections/src/private/MDCCollectionViewEditor.m similarity index 98% rename from components/Collections/src/MDCCollectionViewEditingManager.m rename to components/Collections/src/private/MDCCollectionViewEditor.m index 2f65989001c..05ac9528257 100644 --- a/components/Collections/src/MDCCollectionViewEditingManager.m +++ b/components/Collections/src/private/MDCCollectionViewEditor.m @@ -18,9 +18,9 @@ #error "This file requires ARC support." #endif -#import "MDCCollectionViewEditingManager.h" +#import "MDCCollectionViewEditor.h" -#import "MDCCollectionViewEditingManagerDelegate.h" +#import "MDCCollectionViewEditingDelegate.h" #import "MaterialShadowLayer.h" #import @@ -54,10 +54,10 @@ + (Class)layerClass { } @end -@interface MDCCollectionViewEditingManager () +@interface MDCCollectionViewEditor () @end -@implementation MDCCollectionViewEditingManager { +@implementation MDCCollectionViewEditor { UILongPressGestureRecognizer *_longPressGestureRecognizer; UIPanGestureRecognizer *_panGestureRecognizer; CGPoint _selectedCellLocation; @@ -65,16 +65,23 @@ @implementation MDCCollectionViewEditingManager { ShadowedSnapshotView *_cellSnapshot; } +@synthesize collectionView = _collectionView; +@synthesize delegate = _delegate; +@synthesize reorderingCellIndexPath = _reorderingCellIndexPath; +@synthesize dismissingCellIndexPath = _dismissingCellIndexPath; +@synthesize dismissingSection = _dismissingSection; +@synthesize editing = _editing; + #pragma mark - Public - (instancetype)init { [self doesNotRecognizeSelector:_cmd]; - return nil; + return [self initWithCollectionView:nil]; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { [self doesNotRecognizeSelector:_cmd]; - return nil; + return [self initWithCollectionView:nil]; } - (instancetype)initWithCollectionView:(UICollectionView *)collectionView { From 8b925d001c8de48e226de4c83556ad5c440d9301 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Fri, 22 Apr 2016 14:52:53 -0400 Subject: [PATCH 106/129] [Collections] Replace StyleManager with a Styling protocol. Reviewers: randallli, cjcox, #mdc_ios_owners Reviewed By: randallli, cjcox, #mdc_ios_owners Subscribers: randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D720 --- .../examples/CollectionCellsLayoutExample.m | 4 +- .../examples/CollectionCellsTextExample.m | 2 +- .../CollectionsAppearanceAnimationExample.m | 4 +- .../CollectionsCellAccessoryExample.m | 2 +- .../examples/CollectionsCellColorExample.m | 4 +- .../CollectionsCellSeparatorExample.m | 2 +- .../examples/CollectionsContainerExample.m | 3 +- .../examples/CollectionsEditingExample.m | 2 +- .../examples/CollectionsGridExample.m | 16 ++-- .../examples/CollectionsHeaderFooterExample.m | 2 +- .../examples/CollectionsInlayExample.m | 12 +-- .../examples/CollectionsSimpleDemo.m | 2 +- .../CollectionsSwipeToDismissRowExample.m | 2 +- .../CollectionsSwipeToDismissSectionExample.m | 2 +- .../src/MDCCollectionViewController.h | 10 +-- .../src/MDCCollectionViewController.m | 56 +++++++------- .../src/MDCCollectionViewFlowLayout.m | 76 +++++++++---------- ...leManager.h => MDCCollectionViewStyling.h} | 25 ++---- ...e.h => MDCCollectionViewStylingDelegate.h} | 4 +- .../Collections/src/MaterialCollections.h | 4 +- .../src/private/MDCCollectionViewStyler.h | 37 +++++++++ .../MDCCollectionViewStyler.m} | 27 ++++++- 22 files changed, 168 insertions(+), 130 deletions(-) rename components/Collections/src/{MDCCollectionViewStyleManager.h => MDCCollectionViewStyling.h} (92%) rename components/Collections/src/{MDCCollectionViewStyleManagerDelegate.h => MDCCollectionViewStylingDelegate.h} (98%) create mode 100644 components/Collections/src/private/MDCCollectionViewStyler.h rename components/Collections/src/{MDCCollectionViewStyleManager.m => private/MDCCollectionViewStyler.m} (95%) diff --git a/components/CollectionCells/examples/CollectionCellsLayoutExample.m b/components/CollectionCells/examples/CollectionCellsLayoutExample.m index 9fb186aa914..e69b1f95c88 100644 --- a/components/CollectionCells/examples/CollectionCellsLayoutExample.m +++ b/components/CollectionCells/examples/CollectionCellsLayoutExample.m @@ -95,7 +95,7 @@ - (void)viewDidLoad { textLineArray:@[ @(2), @(1) ]]]; // Customize collection view settings. - self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; } #pragma mark - @@ -138,7 +138,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView return cell; } -#pragma mark - +#pragma mark - - (CGFloat)collectionView:(UICollectionView *)collectionView cellHeightAtIndexPath:(NSIndexPath *)indexPath { diff --git a/components/CollectionCells/examples/CollectionCellsTextExample.m b/components/CollectionCells/examples/CollectionCellsTextExample.m index 7e9fddd89ad..6639a1ce8c2 100644 --- a/components/CollectionCells/examples/CollectionCellsTextExample.m +++ b/components/CollectionCells/examples/CollectionCellsTextExample.m @@ -92,7 +92,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView return cell; } -#pragma mark - +#pragma mark - - (CGFloat)collectionView:(UICollectionView *)collectionView cellHeightAtIndexPath:(NSIndexPath *)indexPath { diff --git a/components/Collections/examples/CollectionsAppearanceAnimationExample.m b/components/Collections/examples/CollectionsAppearanceAnimationExample.m index 7eacdb0c451..9f438945986 100644 --- a/components/Collections/examples/CollectionsAppearanceAnimationExample.m +++ b/components/Collections/examples/CollectionsAppearanceAnimationExample.m @@ -53,8 +53,8 @@ - (void)viewDidLoad { } // Customize collection view settings. - self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; - self.styleManager.shouldAnimateCellsOnAppearance = YES; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.shouldAnimateCellsOnAppearance = YES; } #pragma mark - diff --git a/components/Collections/examples/CollectionsCellAccessoryExample.m b/components/Collections/examples/CollectionsCellAccessoryExample.m index 478ad9a3e71..53596506ffb 100644 --- a/components/Collections/examples/CollectionsCellAccessoryExample.m +++ b/components/Collections/examples/CollectionsCellAccessoryExample.m @@ -57,7 +57,7 @@ - (void)viewDidLoad { @"No Accessory View" ]]; // Customize collection view settings. - self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; } #pragma mark - diff --git a/components/Collections/examples/CollectionsCellColorExample.m b/components/Collections/examples/CollectionsCellColorExample.m index dcb3ca60762..2672b3aeccd 100644 --- a/components/Collections/examples/CollectionsCellColorExample.m +++ b/components/Collections/examples/CollectionsCellColorExample.m @@ -53,7 +53,7 @@ - (void)viewDidLoad { @"Default White Color" ]]; // Customize collection view settings. - self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; } #pragma mark - @@ -76,7 +76,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView return cell; } -#pragma mark - +#pragma mark - - (UIColor *)collectionView:(UICollectionView *)collectionView cellBackgroundColorAtIndexPath:(NSIndexPath *)indexPath { diff --git a/components/Collections/examples/CollectionsCellSeparatorExample.m b/components/Collections/examples/CollectionsCellSeparatorExample.m index df5437fe447..555f923e853 100644 --- a/components/Collections/examples/CollectionsCellSeparatorExample.m +++ b/components/Collections/examples/CollectionsCellSeparatorExample.m @@ -51,7 +51,7 @@ - (void)viewDidLoad { } // Customize collection view settings. - self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; } #pragma mark - diff --git a/components/Collections/examples/CollectionsContainerExample.m b/components/Collections/examples/CollectionsContainerExample.m index fa9c80f98dc..8480d4a2fed 100644 --- a/components/Collections/examples/CollectionsContainerExample.m +++ b/components/Collections/examples/CollectionsContainerExample.m @@ -72,8 +72,7 @@ - (void)viewDidLoad { } // Customize collection view settings. - MDCCollectionViewStyleManager *styleManager = _collectionsController.styleManager; - styleManager.cellStyle = MDCCollectionViewCellStyleCard; + _collectionsController.styler.cellStyle = MDCCollectionViewCellStyleCard; } #pragma mark - diff --git a/components/Collections/examples/CollectionsEditingExample.m b/components/Collections/examples/CollectionsEditingExample.m index eb5127683ba..82cab2bb0ca 100644 --- a/components/Collections/examples/CollectionsEditingExample.m +++ b/components/Collections/examples/CollectionsEditingExample.m @@ -54,7 +54,7 @@ - (void)viewDidLoad { } // Customize collection view settings. - self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; } - (void)updatedRightBarButtonItem:(BOOL)isEditing { diff --git a/components/Collections/examples/CollectionsGridExample.m b/components/Collections/examples/CollectionsGridExample.m index b62132a38c8..ea1b7992f80 100644 --- a/components/Collections/examples/CollectionsGridExample.m +++ b/components/Collections/examples/CollectionsGridExample.m @@ -59,10 +59,10 @@ - (void)viewDidLoad { } // Customize collection view settings. - self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; - self.styleManager.cellLayoutType = MDCCollectionViewCellLayoutTypeGrid; - self.styleManager.gridPadding = 8; - self.styleManager.gridColumnCount = 2; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.cellLayoutType = MDCCollectionViewCellLayoutTypeGrid; + self.styler.gridPadding = 8; + self.styler.gridColumnCount = 2; } #pragma mark - @@ -89,16 +89,16 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView - (void)toggleCellLayoutType { // Toggles between list and grid layout. - BOOL isListLayout = (self.styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeList); - self.styleManager.cellLayoutType = + BOOL isListLayout = (self.styler.cellLayoutType == MDCCollectionViewCellLayoutTypeList); + self.styler.cellLayoutType = isListLayout ? MDCCollectionViewCellLayoutTypeGrid : MDCCollectionViewCellLayoutTypeList; [self.collectionView performBatchUpdates:nil completion:nil]; } - (void)toggleCellStyle { // Toggles between card and grouped styles. - BOOL isCardStyle = (self.styleManager.cellStyle == MDCCollectionViewCellStyleCard); - self.styleManager.cellStyle = + BOOL isCardStyle = (self.styler.cellStyle == MDCCollectionViewCellStyleCard); + self.styler.cellStyle = isCardStyle ? MDCCollectionViewCellStyleGrouped : MDCCollectionViewCellStyleCard; [self.collectionView performBatchUpdates:nil completion:nil]; } diff --git a/components/Collections/examples/CollectionsHeaderFooterExample.m b/components/Collections/examples/CollectionsHeaderFooterExample.m index a82f67d2720..768f2d84534 100644 --- a/components/Collections/examples/CollectionsHeaderFooterExample.m +++ b/components/Collections/examples/CollectionsHeaderFooterExample.m @@ -63,7 +63,7 @@ - (void)viewDidLoad { } // Customize collection view settings. - self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; } #pragma mark - diff --git a/components/Collections/examples/CollectionsInlayExample.m b/components/Collections/examples/CollectionsInlayExample.m index 5fd00dce895..023aeb2ed93 100644 --- a/components/Collections/examples/CollectionsInlayExample.m +++ b/components/Collections/examples/CollectionsInlayExample.m @@ -40,9 +40,9 @@ - (void)viewDidLoad { forCellWithReuseIdentifier:kReusableIdentifierItem]; // Customize collection view settings. - self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; - self.styleManager.allowsItemInlay = YES; - self.styleManager.allowsMultipleItemInlays = YES; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.allowsItemInlay = YES; + self.styler.allowsMultipleItemInlays = YES; } #pragma mark - @@ -66,11 +66,11 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { [super collectionView:collectionView didSelectItemAtIndexPath:indexPath]; - BOOL isInlaid = [self.styleManager isItemInlaidAtIndexPath:indexPath]; + BOOL isInlaid = [self.styler isItemInlaidAtIndexPath:indexPath]; if (isInlaid) { - [self.styleManager removeInlayFromItemAtIndexPath:indexPath animated:YES]; + [self.styler removeInlayFromItemAtIndexPath:indexPath animated:YES]; } else { - [self.styleManager applyInlayToItemAtIndexPath:indexPath animated:YES]; + [self.styler applyInlayToItemAtIndexPath:indexPath animated:YES]; } } diff --git a/components/Collections/examples/CollectionsSimpleDemo.m b/components/Collections/examples/CollectionsSimpleDemo.m index 662f7daefb0..94b5e734077 100644 --- a/components/Collections/examples/CollectionsSimpleDemo.m +++ b/components/Collections/examples/CollectionsSimpleDemo.m @@ -64,7 +64,7 @@ - (void)viewDidLoad { } // Customize collection view settings. - self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; } #pragma mark - diff --git a/components/Collections/examples/CollectionsSwipeToDismissRowExample.m b/components/Collections/examples/CollectionsSwipeToDismissRowExample.m index 3fb876c9e1f..57e8d4476cb 100644 --- a/components/Collections/examples/CollectionsSwipeToDismissRowExample.m +++ b/components/Collections/examples/CollectionsSwipeToDismissRowExample.m @@ -54,7 +54,7 @@ - (void)viewDidLoad { [_content insertObject:@[ @"This cell cannot be deleted." ] atIndex:0]; // Customize collection view settings. - self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; } #pragma mark - diff --git a/components/Collections/examples/CollectionsSwipeToDismissSectionExample.m b/components/Collections/examples/CollectionsSwipeToDismissSectionExample.m index 23046a1dbfc..0616258025d 100644 --- a/components/Collections/examples/CollectionsSwipeToDismissSectionExample.m +++ b/components/Collections/examples/CollectionsSwipeToDismissSectionExample.m @@ -56,7 +56,7 @@ - (void)viewDidLoad { atIndex:0]; // Customize collection view settings. - self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; } #pragma mark - diff --git a/components/Collections/src/MDCCollectionViewController.h b/components/Collections/src/MDCCollectionViewController.h index 2cc157eee61..f77efb9fc2f 100644 --- a/components/Collections/src/MDCCollectionViewController.h +++ b/components/Collections/src/MDCCollectionViewController.h @@ -18,8 +18,8 @@ #import "MDCCollectionViewEditing.h" #import "MDCCollectionViewEditingDelegate.h" -#import "MDCCollectionViewStyleManager.h" -#import "MDCCollectionViewStyleManagerDelegate.h" +#import "MDCCollectionViewStyling.h" +#import "MDCCollectionViewStylingDelegate.h" /** Controller that implements a collection view that adheres to Material design layout @@ -30,13 +30,13 @@ MDCCollectionViewEditingDelegate, /** Allows for styling updates. */ - MDCCollectionViewStyleManagerDelegate, + MDCCollectionViewStylingDelegate, /** Adheres to flow layout. */ UICollectionViewDelegateFlowLayout> -/** The collection view style manager. */ -@property(nonatomic, strong, readonly, nonnull) MDCCollectionViewStyleManager *styleManager; +/** The collection view styler. */ +@property(nonatomic, strong, readonly, nonnull) id styler; /** The collection view editing manager. */ @property(nonatomic, strong, readonly, nonnull) id editor; diff --git a/components/Collections/src/MDCCollectionViewController.m b/components/Collections/src/MDCCollectionViewController.m index 3619ad83f17..bbd34f3e07c 100644 --- a/components/Collections/src/MDCCollectionViewController.m +++ b/components/Collections/src/MDCCollectionViewController.m @@ -26,6 +26,7 @@ #import "private/MDCCollectionInfoBarView.h" #import "private/MDCCollectionStringResources.h" #import "private/MDCCollectionViewEditor.h" +#import "private/MDCCollectionViewStyler.h" #import @@ -77,12 +78,9 @@ - (void)viewDidLoad { self.collectionView.backgroundColor = [UIColor whiteColor]; self.collectionView.alwaysBounceVertical = YES; - // Style manager. - _styleManager = - [[MDCCollectionViewStyleManager alloc] initWithCollectionView:self.collectionView]; - _styleManager.delegate = self; + _styler = [[MDCCollectionViewStyler alloc] initWithCollectionView:self.collectionView]; + _styler.delegate = self; - // Editing manager. _editor = [[MDCCollectionViewEditor alloc] initWithCollectionView:self.collectionView]; _editor.delegate = self; @@ -94,7 +92,7 @@ - (void)viewDidLoad { - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; - _styleManager.shouldInvalidateLayout = NO; + _styler.shouldInvalidateLayout = NO; } - (UICollectionViewLayout *)collectionViewLayout { @@ -145,11 +143,11 @@ - (void)infoBar:(MDCCollectionInfoBarView *)infoBar } } -#pragma mark - +#pragma mark - - (MDCCollectionViewCellStyle)collectionView:(UICollectionView *)collectionView cellStyleForSection:(NSInteger)section { - return _styleManager.cellStyle; + return _styler.cellStyle; } #pragma mark - @@ -174,8 +172,8 @@ - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { if ([collectionViewLayout isKindOfClass:[UICollectionViewFlowLayout class]]) { - if (_styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { - return _styleManager.gridPadding; + if (_styler.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { + return _styler.gridPadding; } return [(UICollectionViewFlowLayout *)collectionViewLayout minimumLineSpacing]; } @@ -184,10 +182,10 @@ - (CGFloat)collectionView:(UICollectionView *)collectionView - (CGSize)sizeWithAttribute:(UICollectionViewLayoutAttributes *)attr { CGFloat height = MDCCellDefaultOneLineHeight; - if ([_styleManager.delegate respondsToSelector: - @selector(collectionView:cellHeightAtIndexPath:)]) { - height = [_styleManager.delegate collectionView:self.collectionView - cellHeightAtIndexPath:attr.indexPath]; + if ([_styler.delegate respondsToSelector: + @selector(collectionView:cellHeightAtIndexPath:)]) { + height = [_styler.delegate collectionView:self.collectionView + cellHeightAtIndexPath:attr.indexPath]; } CGFloat width = [self cellWidthAtSectionIndex:attr.indexPath.section]; @@ -199,10 +197,10 @@ - (CGFloat)cellWidthAtSectionIndex:(NSInteger)section { self.collectionView.contentInset)); UIEdgeInsets sectionInsets = [self insetsAtSectionIndex:section]; CGFloat insets = sectionInsets.left + sectionInsets.right; - if (_styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { + if (_styler.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { CGFloat cellWidth = - bounds - insets - (_styleManager.gridPadding * (_styleManager.gridColumnCount - 1)); - return cellWidth / _styleManager.gridColumnCount; + bounds - insets - (_styler.gridPadding * (_styler.gridColumnCount - 1)); + return cellWidth / _styler.gridColumnCount; } return bounds - insets; } @@ -214,7 +212,7 @@ - (UIEdgeInsets)insetsAtSectionIndex:(NSInteger)section { NSInteger numberOfSections = self.collectionView.numberOfSections; BOOL isTop = (section == 0); BOOL isBottom = (section == numberOfSections - 1); - MDCCollectionViewCellStyle cellStyle = [_styleManager cellStyleAtSectionIndex:section]; + MDCCollectionViewCellStyle cellStyle = [_styler cellStyleAtSectionIndex:section]; BOOL isCardStyle = cellStyle == MDCCollectionViewCellStyleCard; BOOL isGroupedStyle = cellStyle == MDCCollectionViewCellStyleGrouped; // Set left/right insets. @@ -234,7 +232,7 @@ - (CGSize)inlaidSizeAtIndexPath:(NSIndexPath *)indexPath withSize:(CGSize)size { // If object is inlaid, return its adjusted size. UICollectionView *collectionView = self.collectionView; - if ([_styleManager isItemInlaidAtIndexPath:indexPath]) { + if ([_styler isItemInlaidAtIndexPath:indexPath]) { CGFloat inset = MDCCollectionViewCellStyleCardSectionInset; UIEdgeInsets inlayInsets = UIEdgeInsetsZero; BOOL prevCellIsInlaid = NO; @@ -263,7 +261,7 @@ - (CGSize)inlaidSizeAtIndexPath:(NSIndexPath *)indexPath NSIndexPath *prevIndexPath = [NSIndexPath indexPathForItem:(indexPath.item - 1) inSection:indexPath.section]; - prevCellIsInlaid = [_styleManager isItemInlaidAtIndexPath:prevIndexPath]; + prevCellIsInlaid = [_styler isItemInlaidAtIndexPath:prevIndexPath]; inlayInsets.top = prevCellIsInlaid ? inset / 2 : inset; } @@ -273,7 +271,7 @@ - (CGSize)inlaidSizeAtIndexPath:(NSIndexPath *)indexPath NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:(indexPath.item + 1) inSection:indexPath.section]; - nextCellIsInlaid = [_styleManager isItemInlaidAtIndexPath:nextIndexPath]; + nextCellIsInlaid = [_styler isItemInlaidAtIndexPath:nextIndexPath]; inlayInsets.bottom = nextCellIsInlaid ? inset / 2 : inset; } @@ -289,10 +287,10 @@ - (BOOL)inkTouchControllerShouldProcessInkTouches:(MDCInkTouchController *)inkTo atTouchLocation:(CGPoint)location { NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:location]; if (indexPath) { - if ([_styleManager.delegate respondsToSelector: - @selector(collectionView:hidesInkViewAtIndexPath:)]) { - return [_styleManager.delegate collectionView:self.collectionView - hidesInkViewAtIndexPath:indexPath]; + if ([_styler.delegate respondsToSelector: + @selector(collectionView:hidesInkViewAtIndexPath:)]) { + return [_styler.delegate collectionView:self.collectionView + hidesInkViewAtIndexPath:indexPath]; } } return YES; @@ -385,15 +383,15 @@ - (BOOL)collectionViewAllowsEditing:(UICollectionView *)collectionView { - (void)collectionViewWillBeginEditing:(UICollectionView *)collectionView { // Inlay all items. - _styleManager.allowsItemInlay = YES; - _styleManager.allowsMultipleItemInlays = YES; - [_styleManager applyInlayToAllItemsAnimated:YES]; + _styler.allowsItemInlay = YES; + _styler.allowsMultipleItemInlays = YES; + [_styler applyInlayToAllItemsAnimated:YES]; [self updateHeaderInfoBarIfNecessary]; } - (void)collectionViewWillEndEditing:(UICollectionView *)collectionView { // Remove inlay of all items. - [_styleManager removeInlayFromAllItemsAnimated:YES]; + [_styler removeInlayFromAllItemsAnimated:YES]; [self updateFooterInfoBarIfNecessary]; } diff --git a/components/Collections/src/MDCCollectionViewFlowLayout.m b/components/Collections/src/MDCCollectionViewFlowLayout.m index e723553ea73..d063dd7a442 100644 --- a/components/Collections/src/MDCCollectionViewFlowLayout.m +++ b/components/Collections/src/MDCCollectionViewFlowLayout.m @@ -22,7 +22,7 @@ #import "MDCCollectionViewController.h" #import "MDCCollectionViewEditingDelegate.h" -#import "MDCCollectionViewStyleManager.h" +#import "MDCCollectionViewStyling.h" #import "MaterialCollectionLayoutAttributes.h" #import "private/MDCCollectionGridBackgroundView.h" #import "private/MDCCollectionInfoBarView.h" @@ -69,11 +69,11 @@ - (instancetype)init { return nil; } -- (MDCCollectionViewStyleManager *)styleManager { +- (id)styler { if ([self.collectionView.delegate isKindOfClass:[MDCCollectionViewController class]]) { MDCCollectionViewController *controller = (MDCCollectionViewController *)self.collectionView.delegate; - return controller.styleManager; + return controller.styler; } return nil; } @@ -321,9 +321,9 @@ - (MDCCollectionViewLayoutAttributes *)updateAttribute:(MDCCollectionViewLayoutA attr.editing = self.editor.isEditing; } attr.isGridLayout = NO; - if (self.styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeList) { + if (self.styler.cellLayoutType == MDCCollectionViewCellLayoutTypeList) { attr.sectionOrdinalPosition = [self ordinalPositionForListElementWithAttribute:attr]; - } else if (self.styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { + } else if (self.styler.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { attr.sectionOrdinalPosition = [self ordinalPositionForGridElementWithAttribute:attr]; attr.isGridLayout = YES; } @@ -335,13 +335,13 @@ - (MDCCollectionViewLayoutAttributes *)updateAttribute:(MDCCollectionViewLayoutA } // Set cell background. - attr.backgroundImage = [self.styleManager backgroundImageForCellLayoutAttributes:attr]; + attr.backgroundImage = [self.styler backgroundImageForCellLayoutAttributes:attr]; // Set separator styling. - attr.separatorColor = self.styleManager.separatorColor; - attr.separatorInset = self.styleManager.separatorInset; - attr.separatorLineHeight = self.styleManager.separatorLineHeight; - attr.shouldHideSeparators = self.styleManager.shouldHideSeparators; + attr.separatorColor = self.styler.separatorColor; + attr.separatorInset = self.styler.separatorInset; + attr.separatorLineHeight = self.styler.separatorLineHeight; + attr.shouldHideSeparators = self.styler.shouldHideSeparators; // Set inlay and hidden state if necessary. [self inlayAttributeIfNecessary:attr]; @@ -377,7 +377,7 @@ - (UIEdgeInsets)insetsAtSectionIndex:(NSInteger)section { NSInteger numberOfSections = self.collectionView.numberOfSections; BOOL isTop = (section == 0); BOOL isBottom = (section == numberOfSections - 1); - MDCCollectionViewCellStyle cellStyle = [self.styleManager cellStyleAtSectionIndex:section]; + MDCCollectionViewCellStyle cellStyle = [self.styler cellStyleAtSectionIndex:section]; BOOL isCardStyle = cellStyle == MDCCollectionViewCellStyleCard; BOOL isGroupedStyle = cellStyle == MDCCollectionViewCellStyleGrouped; // Set left/right insets. @@ -407,19 +407,19 @@ - (MDCCollectionViewOrdinalPosition)ordinalPositionForListElementWithAttribute: BOOL hasSectionItems = YES; BOOL hidesHeaderBackground = NO; - if ([self.styleManager.delegate + if ([self.styler.delegate respondsToSelector:@selector(collectionView:shouldHideHeaderBackgroundForSection:)]) { hidesHeaderBackground = - [self.styleManager.delegate collectionView:self.styleManager.collectionView - shouldHideHeaderBackgroundForSection:indexPath.section]; + [self.styler.delegate collectionView:self.styler.collectionView + shouldHideHeaderBackgroundForSection:indexPath.section]; } BOOL hidesFooterBackground = NO; - if ([self.styleManager.delegate + if ([self.styler.delegate respondsToSelector:@selector(collectionView:shouldHideFooterBackgroundForSection:)]) { hidesFooterBackground = - [self.styleManager.delegate collectionView:self.styleManager.collectionView - shouldHideFooterBackgroundForSection:indexPath.section]; + [self.styler.delegate collectionView:self.styler.collectionView + shouldHideFooterBackgroundForSection:indexPath.section]; } if (attr.representedElementCategory == UICollectionElementCategoryCell) { @@ -435,7 +435,7 @@ - (MDCCollectionViewOrdinalPosition)ordinalPositionForListElementWithAttribute: isBottom = (isElementHeader && !hasSectionItems && !hasSectionFooter) || isElementFooter; } - if (attr.editing || [self.styleManager isItemInlaidAtIndexPath:attr.indexPath]) { + if (attr.editing || [self.styler isItemInlaidAtIndexPath:attr.indexPath]) { isTop = YES; isBottom = YES; } @@ -456,7 +456,7 @@ - (MDCCollectionViewOrdinalPosition)ordinalPositionForGridElementWithAttribute: MDCCollectionViewOrdinalPosition position = 0; NSIndexPath *indexPath = attr.indexPath; NSInteger numberOfItemsInSection = [self numberOfItemsInSection:indexPath.section]; - NSInteger gridColumnCount = self.styleManager.gridColumnCount; + NSInteger gridColumnCount = self.styler.gridColumnCount; NSInteger maxRowIndex = (NSInteger)(floor(numberOfItemsInSection / gridColumnCount) - 1); NSInteger maxColumnIndex = gridColumnCount - 1; NSInteger ordinalRow = (NSInteger)(floor(indexPath.item / gridColumnCount)); @@ -520,7 +520,7 @@ - (void)inlayAttributeIfNecessary:(MDCCollectionViewLayoutAttributes *)attr { CGFloat inset = MDCCollectionViewCellStyleCardSectionInset; UIEdgeInsets inlayInsets = UIEdgeInsetsZero; NSInteger item = attr.indexPath.item; - NSArray *inlaidIndexPaths = [self.styleManager indexPathsForInlaidItems]; + NSArray *inlaidIndexPaths = [self.styler indexPathsForInlaidItems]; // Update ordinal position for index paths adjacent to inlaid index path. for (NSIndexPath *inlaidIndexPath in inlaidIndexPaths) { @@ -537,14 +537,14 @@ - (void)inlayAttributeIfNecessary:(MDCCollectionViewLayoutAttributes *)attr { if (inlaidIndexPath.item > 0 || hasSectionHeader) { NSIndexPath *prevIndexPath = [NSIndexPath indexPathForItem:(inlaidIndexPath.item - 1) inSection:inlaidIndexPath.section]; - prevAttrIsInlaid = [self.styleManager isItemInlaidAtIndexPath:prevIndexPath]; + prevAttrIsInlaid = [self.styler isItemInlaidAtIndexPath:prevIndexPath]; inlayInsets.top = prevAttrIsInlaid ? inset / 2 : inset; } if (inlaidIndexPath.item < numberOfItemsInSection - 1 || hasSectionFooter) { NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:(inlaidIndexPath.item + 1) inSection:inlaidIndexPath.section]; - nextAttrIsInlaid = [self.styleManager isItemInlaidAtIndexPath:nextIndexPath]; + nextAttrIsInlaid = [self.styler isItemInlaidAtIndexPath:nextIndexPath]; inlayInsets.bottom = nextAttrIsInlaid ? inset / 2 : inset; } @@ -622,7 +622,7 @@ - (void)addDecorationViewIfNecessary:(NSMutableArray *)attributes { // this happens, the background for those items will not be drawn, and instead this decoration // view will extend to the bounds of the sum of its respective section item frames. Shadowing and // border will be applied to this decoration view as per the style manager settings. - if (self.styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { + if (self.styler.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { NSMutableSet *sectionSet = [NSMutableSet set]; BOOL shouldShowGridBackground = NO; NSMutableArray *decorationAttributes = [NSMutableArray array]; @@ -639,7 +639,7 @@ - (void)addDecorationViewIfNecessary:(NSMutableArray *)attributes { shouldShowGridBackground = [self shouldShowGridBackgroundWithAttribute:decorationAttr]; decorationAttr.shouldShowGridBackground = shouldShowGridBackground; decorationAttr.backgroundImage = shouldShowGridBackground - ? [self.styleManager backgroundImageForCellLayoutAttributes:decorationAttr] + ? [self.styler backgroundImageForCellLayoutAttributes:decorationAttr] : nil; [decorationAttributes addObject:decorationAttr]; [sectionSet addObject:@(section)]; @@ -654,10 +654,10 @@ - (void)addDecorationViewIfNecessary:(NSMutableArray *)attributes { - (BOOL)shouldShowGridBackgroundWithAttribute:(MDCCollectionViewLayoutAttributes *)attr { // Determine whether to show grid background. - if (self.styleManager.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { - if (self.styleManager.cellStyle == MDCCollectionViewCellStyleGrouped || - (self.styleManager.cellStyle == MDCCollectionViewCellStyleCard && - self.styleManager.gridPadding == 0)) { + if (self.styler.cellLayoutType == MDCCollectionViewCellLayoutTypeGrid) { + if (self.styler.cellStyle == MDCCollectionViewCellStyleGrouped || + (self.styler.cellStyle == MDCCollectionViewCellStyleCard && + self.styler.gridPadding == 0)) { return YES; } } @@ -673,8 +673,8 @@ - (NSInteger)numberOfItemsInSection:(NSInteger)section { - (CGRect)boundsForAppearanceAnimationWithInitialBounds:(CGRect)initialBounds { // Increase initial bounds by 25% allowing offscreen attributes to be included in the // appearance animation. - if (self.styleManager.shouldAnimateCellsOnAppearance && - self.styleManager.willAnimateCellsOnAppearance) { + if (self.styler.shouldAnimateCellsOnAppearance && + self.styler.willAnimateCellsOnAppearance) { CGRect newBounds = initialBounds; newBounds.size.height += (newBounds.size.height / 4); return newBounds; @@ -694,8 +694,8 @@ - (void)beginCellAppearanceAnimationIfNecessary:(NSMutableArray *)attributes { // header -> item -> footer ... repeated for each section. Now we can use this ordered array // to assign delays based on their proper ordinal position from top down. NSInteger attributeCount = attributes.count; - NSTimeInterval duration = self.styleManager.animateCellsOnAppearanceDuration; - if (self.styleManager.shouldAnimateCellsOnAppearance && attributeCount > 0) { + NSTimeInterval duration = self.styler.animateCellsOnAppearanceDuration; + if (self.styler.shouldAnimateCellsOnAppearance && attributeCount > 0) { // First sort by index path. NSArray *sortedByIndexPath = [attributes sortedArrayUsingComparator: @@ -727,22 +727,22 @@ - (void)beginCellAppearanceAnimationIfNecessary:(NSMutableArray *)attributes { // Now assign delays and add padding to frame Y coordinate which gets removed during animation. [sortedAttributes enumerateObjectsUsingBlock:^(MDCCollectionViewLayoutAttributes *attr, NSUInteger idx, BOOL *stop) { - attr.willAnimateCellsOnAppearance = self.styleManager.willAnimateCellsOnAppearance; - attr.animateCellsOnAppearanceDuration = self.styleManager.animateCellsOnAppearanceDuration; + attr.willAnimateCellsOnAppearance = self.styler.willAnimateCellsOnAppearance; + attr.animateCellsOnAppearanceDuration = self.styler.animateCellsOnAppearanceDuration; attr.animateCellsOnAppearanceDelay = (attributeCount > 0) ? ((CGFloat)idx / attributeCount) * duration : 0; - if (self.styleManager.willAnimateCellsOnAppearance) { + if (self.styler.willAnimateCellsOnAppearance) { CGRect frame = attr.frame; - frame.origin.y += self.styleManager.animateCellsOnAppearancePadding; + frame.origin.y += self.styler.animateCellsOnAppearancePadding; attr.frame = frame; } }]; // Call asynchronously to allow the current layout cycle to complete before issuing animations. - if (self.styleManager.willAnimateCellsOnAppearance) { + if (self.styler.willAnimateCellsOnAppearance) { dispatch_async(dispatch_get_main_queue(), ^{ - [self.styleManager beginCellAppearanceAnimation]; + [self.styler beginCellAppearanceAnimation]; }); } } diff --git a/components/Collections/src/MDCCollectionViewStyleManager.h b/components/Collections/src/MDCCollectionViewStyling.h similarity index 92% rename from components/Collections/src/MDCCollectionViewStyleManager.h rename to components/Collections/src/MDCCollectionViewStyling.h index 9964a78cd9d..a5448c47290 100644 --- a/components/Collections/src/MDCCollectionViewStyleManager.h +++ b/components/Collections/src/MDCCollectionViewStyling.h @@ -16,7 +16,7 @@ #import -@protocol MDCCollectionViewStyleManagerDelegate; +@protocol MDCCollectionViewStylingDelegate; @class MDCCollectionViewLayoutAttributes; /** The default section insets. Should be an even number to allow even division. */ @@ -50,16 +50,16 @@ typedef NS_ENUM(NSUInteger, MDCCollectionViewCellLayoutType) { }; /** - MDCCollectionViewStyleManager provides a default implementation for a UICollectionView to set - its style properties. + The MDCCollectionViewStyling protocol defines the stylable properties for a Material collection + view. */ -@interface MDCCollectionViewStyleManager : NSObject +@protocol MDCCollectionViewStyling /** The associated collection view. */ @property(nonatomic, readonly, weak, nullable) UICollectionView *collectionView; /** The delegate is sent messages when styles change. */ -@property(nonatomic, weak, nullable) id delegate; +@property(nonatomic, weak, nullable) id delegate; /** Indicates whether the collection view layout should be invalidated. */ @property(nonatomic, assign) BOOL shouldInvalidateLayout; @@ -262,19 +262,4 @@ typedef NS_ENUM(NSUInteger, MDCCollectionViewCellLayoutType) { */ - (void)beginCellAppearanceAnimation; -#pragma mark - Initializers - -/** Unavailable superclass initializer. */ -- (nonnull instancetype)init NS_UNAVAILABLE; - -/** - Initializes and returns a newly allocated style manager object with the specified collection view. - - Designated initializer. - - @param collectionView The controller's collection view. - */ -- (nonnull instancetype)initWithCollectionView: - (nonnull UICollectionView *)collectionView NS_DESIGNATED_INITIALIZER; - @end diff --git a/components/Collections/src/MDCCollectionViewStyleManagerDelegate.h b/components/Collections/src/MDCCollectionViewStylingDelegate.h similarity index 98% rename from components/Collections/src/MDCCollectionViewStyleManagerDelegate.h rename to components/Collections/src/MDCCollectionViewStylingDelegate.h index 6a6367610a5..b013b77baef 100644 --- a/components/Collections/src/MDCCollectionViewStyleManagerDelegate.h +++ b/components/Collections/src/MDCCollectionViewStylingDelegate.h @@ -16,13 +16,13 @@ #import -#import "MDCCollectionViewStyleManager.h" +#import "MDCCollectionViewStyling.h" @class MDCInkTouchController; @class MDCInkView; /** A delegate protocol which allows setting collection view cell styles. */ -@protocol MDCCollectionViewStyleManagerDelegate +@protocol MDCCollectionViewStylingDelegate @optional #pragma mark - Styling diff --git a/components/Collections/src/MaterialCollections.h b/components/Collections/src/MaterialCollections.h index bf47b7438c8..a1455611957 100644 --- a/components/Collections/src/MaterialCollections.h +++ b/components/Collections/src/MaterialCollections.h @@ -18,6 +18,6 @@ #import "MDCCollectionViewEditing.h" #import "MDCCollectionViewEditingDelegate.h" #import "MDCCollectionViewFlowLayout.h" -#import "MDCCollectionViewStyleManager.h" -#import "MDCCollectionViewStyleManagerDelegate.h" +#import "MDCCollectionViewStyling.h" +#import "MDCCollectionViewStylingDelegate.h" #import "MaterialCollectionCells.h" diff --git a/components/Collections/src/private/MDCCollectionViewStyler.h b/components/Collections/src/private/MDCCollectionViewStyler.h new file mode 100644 index 00000000000..ece6adb9036 --- /dev/null +++ b/components/Collections/src/private/MDCCollectionViewStyler.h @@ -0,0 +1,37 @@ +/* + 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 "MDCCollectionViewStyling.h" + +/** + The MDCCollectionViewStyler class provides a default implementation for a UICollectionView to set + its style properties. + */ +@interface MDCCollectionViewStyler : NSObject + +- (nonnull instancetype)init NS_UNAVAILABLE; + +/** + Initializes and returns a newly allocated style manager object with the specified collection view. + + Designated initializer. + + @param collectionView The controller's collection view. + */ +- (nonnull instancetype)initWithCollectionView: + (nonnull UICollectionView *)collectionView NS_DESIGNATED_INITIALIZER; + +@end diff --git a/components/Collections/src/MDCCollectionViewStyleManager.m b/components/Collections/src/private/MDCCollectionViewStyler.m similarity index 95% rename from components/Collections/src/MDCCollectionViewStyleManager.m rename to components/Collections/src/private/MDCCollectionViewStyler.m index 997df96d5e6..55960d90494 100644 --- a/components/Collections/src/MDCCollectionViewStyleManager.m +++ b/components/Collections/src/private/MDCCollectionViewStyler.m @@ -18,9 +18,9 @@ #error "This file requires ARC support." #endif -#import "MDCCollectionViewStyleManager.h" +#import "MDCCollectionViewStyler.h" -#import "MDCCollectionViewStyleManagerDelegate.h" +#import "MDCCollectionViewStylingDelegate.h" #import "MaterialCollectionLayoutAttributes.h" #import @@ -85,7 +85,7 @@ NS_INLINE CGRect RectShift(CGRect rect, CGFloat dx, CGFloat dy) { return CGRectOffset(RectContract(rect, dx, dy), dx, dy); } -@interface MDCCollectionViewStyleManager () +@interface MDCCollectionViewStyler () /** Convenience property defining the exact color that the collection view background should be. */ @property(nonatomic, readonly) UIColor *collectionViewBackgroundColor; @@ -102,10 +102,29 @@ @interface MDCCollectionViewStyleManager () @end -@implementation MDCCollectionViewStyleManager { +@implementation MDCCollectionViewStyler { UIColor *_collectionViewBackgroundColor; } +@synthesize collectionView = _collectionView; +@synthesize delegate = _delegate; +@synthesize shouldInvalidateLayout = _shouldInvalidateLayout; +@synthesize cellBackgroundColor = _cellBackgroundColor; +@synthesize cellLayoutType = _cellLayoutType; +@synthesize gridColumnCount = _gridColumnCount; +@synthesize gridPadding = _gridPadding; +@synthesize cellStyle = _cellStyle; +@synthesize separatorColor = _separatorColor; +@synthesize separatorInset = _separatorInset; +@synthesize separatorLineHeight = _separatorLineHeight; +@synthesize shouldHideSeparators = _shouldHideSeparators; +@synthesize allowsItemInlay = _allowsItemInlay; +@synthesize allowsMultipleItemInlays = _allowsMultipleItemInlays; +@synthesize shouldAnimateCellsOnAppearance = _shouldAnimateCellsOnAppearance; +@synthesize willAnimateCellsOnAppearance = _willAnimateCellsOnAppearance; +@synthesize animateCellsOnAppearancePadding = _animateCellsOnAppearancePadding; +@synthesize animateCellsOnAppearanceDuration = _animateCellsOnAppearanceDuration; + - (instancetype)initWithCollectionView:(UICollectionView *)collectionView { self = [super init]; if (self) { From c8b22b1b344d211ecf04974bcd212a45e4673165 Mon Sep 17 00:00:00 2001 From: keefertaylor Date: Fri, 22 Apr 2016 14:58:09 -0400 Subject: [PATCH 107/129] [FlexibleHeader] Shift status bar with header Summary: Shift status bar on and off screen when client applications request the header to be shown or hidden. Fixes issue: https://github.com/google/material-components-ios/issues/426 --- Closes https://github.com/google/material-components-ios/pull/434 GitHub author: keefertaylor !![[https://github.com/google/material-components-ios/pull/434 | This is a child of GitHub pull request #434]]!!. Reviewers: github-bot, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D734 --- components/FlexibleHeader/src/MDCFlexibleHeaderView.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m index 82c8b6dd809..11a258f954d 100644 --- a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m +++ b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m @@ -532,6 +532,8 @@ - (void)fhv_shiftDisplayLinkDidFire:(CADisplayLink *)displayLink { _shiftOffscreenAccumulator += kAttachmentCoefficient * distanceToDestination * duration; _shiftOffscreenAccumulator = MAX(0, MIN([self fhv_accumulatorMax], _shiftOffscreenAccumulator)); + [_statusBarShifter setOffset:_shiftOffscreenAccumulator]; + // Have we reached our destination? if (fabs(destination - _shiftOffscreenAccumulator) <= kShiftEpsilon) { _shiftOffscreenAccumulator = destination; @@ -732,6 +734,8 @@ - (void)fhv_commitAccumulatorToFrame { [self fhv_accumulatorDidChange]; [self fhv_recalculatePhase]; + [_statusBarShifter setOffset:_shiftOffscreenAccumulator]; + [self.delegate flexibleHeaderViewFrameDidChange:self]; } From a576d6e7ae209a1d3308f1fe6312ae0ef4c5e54d Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Fri, 22 Apr 2016 13:48:51 -0400 Subject: [PATCH 108/129] [Catalog] Title row name should correspond to type style Reviewers: ajsecord, #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D732 --- components/Typography/examples/TypographyFontListExample.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/Typography/examples/TypographyFontListExample.swift b/components/Typography/examples/TypographyFontListExample.swift index 1564e70242f..13b16d6c3b8 100644 --- a/components/Typography/examples/TypographyFontListExample.swift +++ b/components/Typography/examples/TypographyFontListExample.swift @@ -53,7 +53,7 @@ class TypographyFontListExampleViewController: UITableViewController { cell!.textLabel!.text = "MDC" } - cell!.detailTextLabel!.text = fontStyleNames[indexPath.section] + cell!.detailTextLabel!.text = fontStyleNames[indexPath.row] cell!.detailTextLabel!.font = MDCTypography.captionFont() cell!.detailTextLabel!.alpha = MDCTypography.captionFontOpacity() cell!.selectionStyle = .None From e88dc4ff3fffd72b29b6b98d2479b4c4334165a4 Mon Sep 17 00:00:00 2001 From: Chris Cox Date: Fri, 22 Apr 2016 15:46:14 -0400 Subject: [PATCH 109/129] [Collections] Adds swift example. Reviewers: #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D737 --- .../examples/CollectionsSimpleSwiftDemo.swift | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 components/Collections/examples/CollectionsSimpleSwiftDemo.swift diff --git a/components/Collections/examples/CollectionsSimpleSwiftDemo.swift b/components/Collections/examples/CollectionsSimpleSwiftDemo.swift new file mode 100644 index 00000000000..854551711dd --- /dev/null +++ b/components/Collections/examples/CollectionsSimpleSwiftDemo.swift @@ -0,0 +1,61 @@ +/* + 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 UIKit + +import MaterialComponents.MaterialCollections + +class CollectionsSimpleSwiftDemo: MDCCollectionViewController { + + let reusableIdentifierItem = "itemCellIdentifier" + let colors = [ "red", "blue", "green", "black", "yellow", "purple" ] + + override func viewDidLoad() { + super.viewDidLoad() + + // Register cell class. + self.collectionView?.registerClass(MDCCollectionViewTextCell.self, + forCellWithReuseIdentifier: reusableIdentifierItem) + + // Customize collection view settings. + self.styler.cellStyle = .Card + } + + // MARK: UICollectionViewDataSource + + override func collectionView(collectionView: UICollectionView, + numberOfItemsInSection section: Int) -> Int { + return colors.count + } + + override func collectionView(collectionView: UICollectionView, + cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + var cell = collectionView.dequeueReusableCellWithReuseIdentifier(reusableIdentifierItem, + forIndexPath: indexPath) + if let cell = cell as? MDCCollectionViewTextCell { + cell.textLabel?.text = colors[indexPath.item] + } + + return cell + } +} + +// MARK: Catalog by convention +extension CollectionsSimpleSwiftDemo { + class func catalogBreadcrumbs() -> Array { + return [ "Collections", "Simple Swift Demo"] + } +} From 78bb0454ae0e036870f184b55eedc30fde9a4a42 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Fri, 22 Apr 2016 16:11:43 -0400 Subject: [PATCH 110/129] [Catalog] Add scaled iPad Pro icon to remove warning Reviewers: ajsecord, #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D741 --- .../AppIcon.appiconset/Contents.json | 3 ++- .../logo_mdc_ios_color_83_5pt_2x.png | Bin 0 -> 9215 bytes 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_83_5pt_2x.png diff --git a/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/Contents.json b/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/Contents.json index eb396b35fc2..921cbc26e3a 100644 --- a/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -73,8 +73,9 @@ "scale" : "2x" }, { - "idiom" : "ipad", "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "logo_mdc_ios_color_83_5pt_2x.png", "scale" : "2x" } ], diff --git a/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_83_5pt_2x.png b/catalog/MDCCatalog/Assets.xcassets/AppIcon.appiconset/logo_mdc_ios_color_83_5pt_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..eea65e2c72a68c7c276144262f38d050cafe1200 GIT binary patch literal 9215 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 R$=LHuT^XoUq3|i_{{a5qGNJ$g literal 0 HcmV?d00001 From 4ddbae5d6342b3552914e1c4148e4dad11a70d88 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Fri, 22 Apr 2016 15:19:04 -0400 Subject: [PATCH 111/129] [FlexibleHeader]! No longer remove insets from tracking scroll views during dealloc. Summary: Before this change the Flexible Header would remove its injected insets from the tracking scroll view during dealloc. This would lead to race condition crashes on pre-iOS 9 devices due to the fact that UIScrollView's delegate property was not a weak property. This change removes the code that removed the injected insets. In general this should not be a problem because the App Bar often lives roughly as long as its trackingScrollView. This change introduces behavioral changes if the trackingScrollView lives beyond the lifetime of the App Bar. I am not aware of any practical instances of this scenario. If such a scenario exists, then it will be up to the client after this change to update the scroll view's content insets to a new reasonable value. Closes https://github.com/google/material-components-ios/issues/437. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D738 --- components/FlexibleHeader/src/MDCFlexibleHeaderView.m | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m index 11a258f954d..ff24508f9f3 100644 --- a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m +++ b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m @@ -148,13 +148,12 @@ @implementation MDCFlexibleHeaderView { @synthesize sharedWithManyScrollViews = _sharedWithManyScrollViews; @synthesize visibleShadowOpacity = _visibleShadowOpacity; -- (void)dealloc { #if DEBUG +- (void)dealloc { [_trackingScrollView.panGestureRecognizer removeTarget:self action:@selector(fhv_scrollViewDidPan:)]; -#endif - [self fhv_removeInsetsFromScrollView:_trackingScrollView]; } +#endif - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; @@ -532,7 +531,7 @@ - (void)fhv_shiftDisplayLinkDidFire:(CADisplayLink *)displayLink { _shiftOffscreenAccumulator += kAttachmentCoefficient * distanceToDestination * duration; _shiftOffscreenAccumulator = MAX(0, MIN([self fhv_accumulatorMax], _shiftOffscreenAccumulator)); - [_statusBarShifter setOffset:_shiftOffscreenAccumulator]; + [_statusBarShifter setOffset:_shiftOffscreenAccumulator]; // Have we reached our destination? if (fabs(destination - _shiftOffscreenAccumulator) <= kShiftEpsilon) { @@ -734,7 +733,7 @@ - (void)fhv_commitAccumulatorToFrame { [self fhv_accumulatorDidChange]; [self fhv_recalculatePhase]; - [_statusBarShifter setOffset:_shiftOffscreenAccumulator]; + [_statusBarShifter setOffset:_shiftOffscreenAccumulator]; [self.delegate flexibleHeaderViewFrameDidChange:self]; } From 89f5075dcdc2ddc20e22756ee408db53c8ffa967 Mon Sep 17 00:00:00 2001 From: Chris Cox Date: Fri, 22 Apr 2016 16:29:07 -0400 Subject: [PATCH 112/129] [Collections] Updates to readme. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D743 --- components/Collections/README.md | 123 ++++++++++++------ .../docs/assets/collections_screenshot.png | Bin 0 -> 102821 bytes 2 files changed, 85 insertions(+), 38 deletions(-) create mode 100644 components/Collections/docs/assets/collections_screenshot.png diff --git a/components/Collections/README.md b/components/Collections/README.md index 9deda017239..b68b150d680 100644 --- a/components/Collections/README.md +++ b/components/Collections/README.md @@ -6,7 +6,7 @@ excerpt: "Collection view classes that adhere to Material design layout and anim --- # Collections -![Collections](docs/assets/appbar_screenshot.png) +![Collections](docs/assets/collections_screenshot.png) Collection view classes that adhere to Material design layout and animation styling. @@ -22,11 +22,11 @@ Collection view classes that adhere to Material design layout and animation styl - - - @@ -62,7 +62,6 @@ Before using Collections, you'll need to import it: #### Objective-C - ~~~ objc #import "MaterialCollections.h" ~~~ @@ -82,7 +81,6 @@ Step 1: **Subclass `MDCCollectionViewController` in your view controller interfa #### Objective-C - ~~~ objc #import "MaterialCollections.h" @@ -92,6 +90,8 @@ Step 1: **Subclass `MDCCollectionViewController` in your view controller interfa #### Swift ~~~ swift +class MyCollectionsExample: MDCCollectionViewController { +} ~~~ @@ -99,13 +99,13 @@ Step 2: **Setup your data**. #### Objective-C - ~~~ objc -_colors = @[ @"red", @"blue", @"green", @"black", @"yellow", @"purple" ]; +colors = @[ @"red", @"blue", @"green", @"black", @"yellow", @"purple" ]; ~~~ #### Swift ~~~ swift +let colors = [ "red", "blue", "green", "black", "yellow", "purple" ] ~~~ @@ -113,7 +113,6 @@ Step 3: **Register a cell class**. #### Objective-C - ~~~ objc [self.collectionView registerClass:[MDCCollectionViewTextCell class] forCellWithReuseIdentifier:kReusableIdentifierItem]; @@ -121,6 +120,8 @@ Step 3: **Register a cell class**. #### Swift ~~~ swift +self.collectionView?.registerClass(MDCCollectionViewTextCell.self, + forCellWithReuseIdentifier: reusableIdentifierItem) ~~~ @@ -128,11 +129,10 @@ Step 4: **Override `UICollectionViewDataSource` protocol required methods**. #### Objective-C - ~~~ objc - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return _colors.count; + return colors.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView @@ -140,13 +140,28 @@ Step 4: **Override `UICollectionViewDataSource` protocol required methods**. MDCCollectionViewTextCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem forIndexPath:indexPath]; - cell.textLabel.text = _colors[indexPath.item]; + cell.textLabel.text = colors[indexPath.item]; return cell; } ~~~ #### Swift ~~~ swift +override func collectionView(collectionView: UICollectionView, + numberOfItemsInSection section: Int) -> Int { + return colors.count +} + +override func collectionView(collectionView: UICollectionView, + cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + var cell = collectionView.dequeueReusableCellWithReuseIdentifier(reusableIdentifierItem, + forIndexPath: indexPath) + if let cell = cell as? MDCCollectionViewTextCell { + cell.textLabel?.text = colors[indexPath.item] + } + + return cell +} ~~~ @@ -154,25 +169,24 @@ Step 4: **Override `UICollectionViewDataSource` protocol required methods**. ### Styling the collection view -The `MDCCollectionViewStyleManager` class provides methods and properties for styling the -collection view. Styling can be set for the entire collection view, or by using the -`MDCCollectionViewStyleManagerDelegate` protocol methods to define styles at specific -sections and rows. +The collection view controller provides a `styler` property that conforms to the +`MDCCollectionViewStyling` protocol. By using this property, styling can be easily set for the +collection view items/sections. In addition, by overriding `MDCCollectionViewStyleDelegate` +protocol methods in a collection view subclass, specific cells/sections can be styled differently. ### Cell Styles -The style manager allows setting the cell style as Default, Grouped, or Card Style. Choose to -either set the style manager `cellStyle` property directly, or use the protocol method +The styler allows setting the cell style as Default, Grouped, or Card Style. Choose to +either set the styler `cellStyle` property directly, or use the protocol method `collectionView:cellStyleForSection:` to style per section. #### Objective-C - ~~~ objc // Set for entire collection view. -self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; +self.styler.cellStyle = MDCCollectionViewCellStyleCard; + -// Or set for specific sections. - (MDCCollectionViewCellStyle)collectionView:(UICollectionView *)collectionView cellStyleForSection:(NSInteger)section { if (section == 2) { @@ -184,16 +198,26 @@ self.styleManager.cellStyle = MDCCollectionViewCellStyleCard; #### Swift ~~~ swift +// Set for entire collection view. +self.styler.cellStyle = .Card + +// Or set for specific sections. +override func collectionView(collectionView: UICollectionView, + cellStyleForSection section: Int) -> MDCCollectionViewCellStyle { + if section == 2 { + return .Card + } + return .Grouped +} ~~~ ### Cell Height -The style manager delegate protocol can be used to override the default cell height of 48.0f. +The styling delegate protocol can be used to override the default cell height of 48.0f. #### Objective-C - ~~~ objc - (CGFloat)collectionView:(UICollectionView *)collectionView cellHeightAtIndexPath:(NSIndexPath *)indexPath { @@ -206,56 +230,79 @@ The style manager delegate protocol can be used to override the default cell hei #### Swift ~~~ swift +override func collectionView(collectionView: UICollectionView, + cellHeightAtIndexPath indexPath: NSIndexPath) -> CGFloat { + if indexPath.item == 0 { + return 80.0 + } + return 48.0 +} ~~~ ### Cell Layout -The style manager allows setting the cell layout as List, Grid, or Custom. +The styler allows setting the cell layout as List, Grid, or Custom. #### Objective-C - ~~~ objc // Set as list layout. -self.styleManager.cellLayoutType = MDCCollectionViewCellLayoutTypeList; +self.styler.cellLayoutType = MDCCollectionViewCellLayoutTypeList; // Or set as grid layout. -self.styleManager.cellLayoutType = MDCCollectionViewCellLayoutTypeGrid; -self.styleManager.gridPadding = 8; -self.styleManager.gridColumnCount = 2; +self.styler.cellLayoutType = MDCCollectionViewCellLayoutTypeGrid; +self.styler.gridPadding = 8; +self.styler.gridColumnCount = 2; ~~~ #### Swift ~~~ swift +// Set as list layout. +self.styler.cellLayoutType = .List + +// Or set as grid layout. +self.styler.cellLayoutType = .Grid +self.styler.gridPadding = 8 +self.styler.gridColumnCount = 2 ~~~ ### Cell Separators -The style manager allows customizing cell separators for the entire collection view. Individual -cell customization can is available by using a cell subclassed from MDCCollectionViewCell. Learn -more by reading the section on [Cell Separators](../CollectionCells/#cell-separators) in the +The styler allows customizing cell separators for the entire collection view. Individual +cell customization is also available by using a cell subclassed from `MDCCollectionViewCell`. +Learn more by reading the section on [Cell Separators](../CollectionCells/#cell-separators) in the [CollectionCells](../CollectionCells) component. #### Objective-C - ~~~ objc // Set separator color. -self.styleManager.separatorColor = [UIColor redColor]; +self.styler.separatorColor = [UIColor redColor]; // Set separator insets. -self.styleManager.separatorInset = UIEdgeInsetsMake(0, 16, 0, 16); +self.styler.separatorInset = UIEdgeInsetsMake(0, 16, 0, 16); // Set separator line height. -self.styleManager.separatorLineHeight = 1.0f; +self.styler.separatorLineHeight = 1.0f; // Whether to hide separators. -self.styleManager.shouldHideSeparators = NO; +self.styler.shouldHideSeparators = NO; ~~~ #### Swift ~~~ swift +// Set separator color. +self.styler.separatorColor = UIColor.redColor() + +// Set separator insets. +self.styler.separatorInset = UIEdgeInsetsMake(0, 16, 0, 16) + +// Set separator line height. +self.styler.separatorLineHeight = 1.0 + +// Whether to hide separators. +self.styler.shouldHideSeparators = false ~~~ diff --git a/components/Collections/docs/assets/collections_screenshot.png b/components/Collections/docs/assets/collections_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..2cf6d537aa169887107552ba14a046770e87254c GIT binary patch literal 102821 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%0Aa+}6X{ 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 Q2jC+oqbyx1X%hT@0Er!vvH$=8 literal 0 HcmV?d00001 From 08e7333c0a85cfc2f525f55faf5808c5fd52aff5 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Fri, 22 Apr 2016 16:37:11 -0400 Subject: [PATCH 113/129] [Catalog] Add icon tile for collections Reviewers: ajsecord, featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D745 --- catalog/MDCCatalog.xcodeproj/project.pbxproj | 6 + .../MDCCatalogTileDataCollections.h | 25 ++++ .../MDCCatalogTileDataCollections.m | 131 ++++++++++++++++++ catalog/MDCCatalog/MDCCatalogTileView.swift | 2 + catalog/MDCCatalog/MDCCatalogTiles.h | 1 + 5 files changed, 165 insertions(+) create mode 100644 catalog/MDCCatalog/MDCCatalogTileDataCollections.h create mode 100644 catalog/MDCCatalog/MDCCatalogTileDataCollections.m diff --git a/catalog/MDCCatalog.xcodeproj/project.pbxproj b/catalog/MDCCatalog.xcodeproj/project.pbxproj index 70e332359e1..417ecf1c128 100644 --- a/catalog/MDCCatalog.xcodeproj/project.pbxproj +++ b/catalog/MDCCatalog.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ DE1944931CBD9E40009E0321 /* MDCCatalogTileDataSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = DE1944821CBD9E40009E0321 /* MDCCatalogTileDataSwitch.m */; }; DE1944941CBD9E40009E0321 /* MDCCatalogTileDataTypography.m in Sources */ = {isa = PBXBuildFile; fileRef = DE1944841CBD9E40009E0321 /* MDCCatalogTileDataTypography.m */; }; DE309CF31C8DEB8400E73247 /* MDCCatalogComponentsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */; }; + DE7A17C51CCAC1EA00B66230 /* MDCCatalogTileDataCollections.m in Sources */ = {isa = PBXBuildFile; fileRef = DE7A17C41CCAC1EA00B66230 /* MDCCatalogTileDataCollections.m */; }; DEF64EA41C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF64EA31C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift */; }; /* End PBXBuildFile section */ @@ -134,6 +135,8 @@ DE1944841CBD9E40009E0321 /* MDCCatalogTileDataTypography.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MDCCatalogTileDataTypography.m; sourceTree = ""; }; DE1944851CBD9E40009E0321 /* MDCCatalogTiles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDCCatalogTiles.h; sourceTree = ""; }; DE309CF21C8DEB8400E73247 /* MDCCatalogComponentsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogComponentsController.swift; sourceTree = ""; }; + DE7A17C41CCAC1EA00B66230 /* MDCCatalogTileDataCollections.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MDCCatalogTileDataCollections.m; sourceTree = ""; }; + DE7A17C61CCAC1F700B66230 /* MDCCatalogTileDataCollections.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MDCCatalogTileDataCollections.h; sourceTree = ""; }; DEF64EA31C8DEE83007C4EA0 /* MDCCatalogCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDCCatalogCollectionViewCell.swift; sourceTree = ""; }; DEFF1C057348FFB1866CD023 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; FBDDFF6A49F084B665BA398A /* Pods-MDCCatalog-MDCCatalog.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MDCCatalog-MDCCatalog.release.xcconfig"; path = "Pods/Target Support Files/Pods-MDCCatalog-MDCCatalog/Pods-MDCCatalog-MDCCatalog.release.xcconfig"; sourceTree = ""; }; @@ -257,6 +260,8 @@ DE19446C1CBD9E40009E0321 /* MDCCatalogTileDataButtonBar.m */, DE19446D1CBD9E40009E0321 /* MDCCatalogTileDataButtons.h */, DE19446E1CBD9E40009E0321 /* MDCCatalogTileDataButtons.m */, + DE7A17C61CCAC1F700B66230 /* MDCCatalogTileDataCollections.h */, + DE7A17C41CCAC1EA00B66230 /* MDCCatalogTileDataCollections.m */, DE19446F1CBD9E40009E0321 /* MDCCatalogTileDataFlexibleHeader.h */, DE1944701CBD9E40009E0321 /* MDCCatalogTileDataFlexibleHeader.m */, DE1944711CBD9E40009E0321 /* MDCCatalogTileDataHeaderStackView.h */, @@ -458,6 +463,7 @@ DE19448E1CBD9E40009E0321 /* MDCCatalogTileDataNavigationBar.m in Sources */, DE19448F1CBD9E40009E0321 /* MDCCatalogTileDataPageControl.m in Sources */, DE1944931CBD9E40009E0321 /* MDCCatalogTileDataSwitch.m in Sources */, + DE7A17C51CCAC1EA00B66230 /* MDCCatalogTileDataCollections.m in Sources */, DE1944871CBD9E40009E0321 /* MDCCatalogTileDataAppBar.m in Sources */, DE309CF31C8DEB8400E73247 /* MDCCatalogComponentsController.swift in Sources */, 6681FDFD1CC586660013A0C7 /* MDCCatalogTileView.swift in Sources */, diff --git a/catalog/MDCCatalog/MDCCatalogTileDataCollections.h b/catalog/MDCCatalog/MDCCatalogTileDataCollections.h new file mode 100644 index 00000000000..0fc06b8237d --- /dev/null +++ b/catalog/MDCCatalog/MDCCatalogTileDataCollections.h @@ -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 + +#import "MDCCatalogTileData.h" + +@interface MDCCatalogTileDataCollections : MDCCatalogTileData + ++ (UIImage *)drawTileImage:(CGRect)frame; + +@end diff --git a/catalog/MDCCatalog/MDCCatalogTileDataCollections.m b/catalog/MDCCatalog/MDCCatalogTileDataCollections.m new file mode 100644 index 00000000000..6ef8bf731ec --- /dev/null +++ b/catalog/MDCCatalog/MDCCatalogTileDataCollections.m @@ -0,0 +1,131 @@ +/* + 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 "MDCCatalogTileDataCollections.h" + +@implementation MDCCatalogTileDataCollections + ++ (UIImage*)drawTileImage:(CGRect)frame { + void (^completionBlock)() = ^() { + [self draw:CGRectMake(0, 0, frame.size.width, frame.size.height)]; + }; + return [self drawImageWithFrame:frame completionBlock:completionBlock]; +} + +/* Auto-generated code using PaintCode and formatted with clang-format. */ ++ (void)draw:(CGRect)frame { + CGContextRef context = UIGraphicsGetCurrentContext(); + + UIColor* fillColor = [UIColor colorWithRed:0.012 green:0.663 blue:0.957 alpha:1]; + UIColor* white40 = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.4]; + UIColor* blue60 = [UIColor colorWithRed:0.012 green:0.663 blue:0.957 alpha:0.6]; + UIColor* white60 = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.7]; + UIColor* white80 = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8]; + UIColor* white30 = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.3]; + + CGRect group2 = + CGRectMake(CGRectGetMinX(frame) + floor(CGRectGetWidth(frame) * 0.13021 + 0.02) + 0.48, + CGRectGetMinY(frame) + floor(CGRectGetHeight(frame) * 0.15484 - 0.38) + 0.88, + floor(CGRectGetWidth(frame) * 0.86984 - 0.03) - + floor(CGRectGetWidth(frame) * 0.13021 + 0.02) + 0.05, + floor(CGRectGetHeight(frame) * 0.67177 + 0.5) - + floor(CGRectGetHeight(frame) * 0.15484 - 0.38) - 0.88); + + { + UIBezierPath* collectionsPath = [UIBezierPath + bezierPathWithRect:CGRectMake(CGRectGetMinX(group2) + + floor(CGRectGetWidth(group2) * 0.00018 + 0.48) + 0.02, + CGRectGetMinY(group2) + + floor(CGRectGetHeight(group2) * 0.00000 + 0.48) + 0.02, + floor(CGRectGetWidth(group2) * 0.99982 + 0.47) - + floor(CGRectGetWidth(group2) * 0.00018 + 0.48), + floor(CGRectGetHeight(group2) * 0.99969 + 0.38) - + floor(CGRectGetHeight(group2) * 0.00000 + 0.48) + 0.1)]; + [fillColor setFill]; + [collectionsPath fill]; + + { + CGContextSaveGState(context); + CGContextSetAlpha(context, 0.6); + CGContextBeginTransparencyLayer(context, NULL); + + UIBezierPath* rectangle2Path = [UIBezierPath + bezierPathWithRect:CGRectMake(CGRectGetMinX(group2) + + floor(CGRectGetWidth(group2) * 0.00036 + 0.45) + 0.05, + CGRectGetMinY(group2) + + floor(CGRectGetHeight(group2) * 0.00000 + 0.48) + 0.02, + floor(CGRectGetWidth(group2) * 0.66667 - 0.2) - + floor(CGRectGetWidth(group2) * 0.00036 + 0.45) + 0.65, + floor(CGRectGetHeight(group2) * 0.99969 + 0.38) - + floor(CGRectGetHeight(group2) * 0.00000 + 0.48) + 0.1)]; + [white60 setFill]; + [rectangle2Path fill]; + + CGContextEndTransparencyLayer(context); + CGContextRestoreGState(context); + } + + UIBezierPath* rectangle3Path = [UIBezierPath + bezierPathWithRect:CGRectMake(CGRectGetMinX(group2) + + floor(CGRectGetWidth(group2) * 0.00000 + 0.5), + CGRectGetMinY(group2) + + floor(CGRectGetHeight(group2) * 0.00031 + 0.45) + 0.05, + floor(CGRectGetWidth(group2) * 0.33333 + 0.15) - + floor(CGRectGetWidth(group2) * 0.00000 + 0.5) + 0.35, + floor(CGRectGetHeight(group2) * 0.50016 + 0.4) - + floor(CGRectGetHeight(group2) * 0.00031 + 0.45) + 0.05)]; + [white80 setFill]; + [rectangle3Path fill]; + + UIBezierPath* rectangle4Path = [UIBezierPath + bezierPathWithRect:CGRectMake(CGRectGetMinX(group2) + + floor(CGRectGetWidth(group2) * 0.33333 + 0.15) + 0.35, + CGRectGetMinY(group2) + + floor(CGRectGetHeight(group2) * 0.50016 + 0.5), + floor(CGRectGetWidth(group2) * 0.66667 - 0.2) - + floor(CGRectGetWidth(group2) * 0.33333 + 0.15) + 0.35, + floor(CGRectGetHeight(group2) * 1.00000 + 0.45) - + floor(CGRectGetHeight(group2) * 0.50016 + 0.5) + 0.05)]; + [blue60 setFill]; + [rectangle4Path fill]; + + UIBezierPath* rectangle5Path = [UIBezierPath + bezierPathWithRect:CGRectMake(CGRectGetMinX(group2) + + floor(CGRectGetWidth(group2) * 0.66667 - 0.2) + 0.7, + CGRectGetMinY(group2) + + floor(CGRectGetHeight(group2) * 0.00031 + 0.45) + 0.05, + floor(CGRectGetWidth(group2) * 1.00000 + 0.45) - + floor(CGRectGetWidth(group2) * 0.66667 - 0.2) - 0.65, + floor(CGRectGetHeight(group2) * 0.50016 + 0.4) - + floor(CGRectGetHeight(group2) * 0.00031 + 0.45) + 0.05)]; + [white30 setFill]; + [rectangle5Path fill]; + + UIBezierPath* rectangle6Path = [UIBezierPath + bezierPathWithRect:CGRectMake(CGRectGetMinX(group2) + + floor(CGRectGetWidth(group2) * 0.16667 + 0.32) + 0.18, + CGRectGetMinY(group2) + + floor(CGRectGetHeight(group2) * 0.24992 + 0.45) + 0.05, + floor(CGRectGetWidth(group2) * 0.85203 + 0.02) - + floor(CGRectGetWidth(group2) * 0.16667 + 0.32) + 0.3, + floor(CGRectGetHeight(group2) * 0.74977 + 0.4) - + floor(CGRectGetHeight(group2) * 0.24992 + 0.45) + 0.05)]; + [white40 setFill]; + [rectangle6Path fill]; + } +} + +@end diff --git a/catalog/MDCCatalog/MDCCatalogTileView.swift b/catalog/MDCCatalog/MDCCatalogTileView.swift index 3ddb5e9a582..964e4dfb266 100644 --- a/catalog/MDCCatalog/MDCCatalogTileView.swift +++ b/catalog/MDCCatalog/MDCCatalogTileView.swift @@ -78,6 +78,8 @@ class MDCCatalogTileView: UIView { newImage = MDCCatalogTileDataButtonBar.drawTileImage(centeredFrame) case "Buttons": newImage = MDCCatalogTileDataButtons.drawTileImage(centeredFrame) + case "Collections": + newImage = MDCCatalogTileDataCollections.drawTileImage(centeredFrame) case "Flexible Header": newImage = MDCCatalogTileDataFlexibleHeader.drawTileImage(centeredFrame) case "Header Stack View": diff --git a/catalog/MDCCatalog/MDCCatalogTiles.h b/catalog/MDCCatalog/MDCCatalogTiles.h index 3ee7696ecd5..7432fd5f4b0 100644 --- a/catalog/MDCCatalog/MDCCatalogTiles.h +++ b/catalog/MDCCatalog/MDCCatalogTiles.h @@ -18,6 +18,7 @@ #import "MDCCatalogTileDataAppBar.h" #import "MDCCatalogTileDataButtonBar.h" #import "MDCCatalogTileDataButtons.h" +#import "MDCCatalogTileDataCollections.h" #import "MDCCatalogTileDataFlexibleHeader.h" #import "MDCCatalogTileDataHeaderStackView.h" #import "MDCCatalogTileDataInk.h" From 142bd2fb7e8cb846fdb449d58873fd5f75419bd1 Mon Sep 17 00:00:00 2001 From: Adrian Secord Date: Fri, 22 Apr 2016 17:01:49 -0400 Subject: [PATCH 114/129] Added unit tests for MDCCollectionLayoutAttributes. Reviewers: #mdc_ios_owners, cjcox Reviewed By: #mdc_ios_owners, cjcox Subscribers: cjcox Projects: #material_components_ios Differential Revision: http://codereview.cc/D747 --- .../src/MDCCollectionViewLayoutAttributes.m | 4 +- .../unit/CollectionLayoutAttributesTests.m | 92 +++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 components/CollectionLayoutAttributes/tests/unit/CollectionLayoutAttributesTests.m diff --git a/components/CollectionLayoutAttributes/src/MDCCollectionViewLayoutAttributes.m b/components/CollectionLayoutAttributes/src/MDCCollectionViewLayoutAttributes.m index 8b14287b05e..71d3fdd2015 100644 --- a/components/CollectionLayoutAttributes/src/MDCCollectionViewLayoutAttributes.m +++ b/components/CollectionLayoutAttributes/src/MDCCollectionViewLayoutAttributes.m @@ -57,9 +57,9 @@ - (BOOL)isEqual:(id)object { (otherAttrs.shouldShowSelectorStateMask != self.shouldShowSelectorStateMask) || (otherAttrs.shouldShowGridBackground != self.shouldShowGridBackground) || (otherAttrs.sectionOrdinalPosition != self.sectionOrdinalPosition) || - (otherAttrs.backgroundImage != self.backgroundImage) || + ![otherAttrs.backgroundImage isEqual:self.backgroundImage] || (otherAttrs.isGridLayout != self.isGridLayout) || - (otherAttrs.separatorColor != self.separatorColor) || + ![otherAttrs.separatorColor isEqual:self.separatorColor] || (!UIEdgeInsetsEqualToEdgeInsets(otherAttrs.separatorInset, self.separatorInset)) || (otherAttrs.separatorLineHeight != self.separatorLineHeight) || (otherAttrs.shouldHideSeparators != self.shouldHideSeparators) || diff --git a/components/CollectionLayoutAttributes/tests/unit/CollectionLayoutAttributesTests.m b/components/CollectionLayoutAttributes/tests/unit/CollectionLayoutAttributesTests.m new file mode 100644 index 00000000000..23b313614e1 --- /dev/null +++ b/components/CollectionLayoutAttributes/tests/unit/CollectionLayoutAttributesTests.m @@ -0,0 +1,92 @@ +/* + 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 "MaterialCollectionLayoutAttributes.h" + +@interface CollectionLayoutAttributesTests : XCTestCase { + MDCCollectionViewLayoutAttributes *_attributes; +} +@end + +@implementation CollectionLayoutAttributesTests + +- (void)setUp { + NSIndexPath *indexPath = [NSIndexPath indexPathWithIndex:0]; + _attributes = + [MDCCollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; + _attributes.editing = YES; + _attributes.shouldShowReorderStateMask = YES; + _attributes.shouldShowSelectorStateMask = YES; + _attributes.shouldShowGridBackground = YES; + _attributes.backgroundImage = [[UIImage alloc] init]; + _attributes.isGridLayout = YES; + _attributes.sectionOrdinalPosition = (MDCCollectionViewOrdinalPosition)NSUIntegerMax; + _attributes.separatorColor = [UIColor purpleColor]; + _attributes.separatorInset = UIEdgeInsetsMake(3, 1, 4, 5); + _attributes.separatorLineHeight = 42; + _attributes.shouldHideSeparators = YES; + _attributes.willAnimateCellsOnAppearance = YES; + _attributes.animateCellsOnAppearanceDuration = 3.145926; + _attributes.animateCellsOnAppearanceDelay = 42; +} + +- (void)testCopying { + // Given + + // When + MDCCollectionViewLayoutAttributes *copy = [_attributes copy]; + + // Then + XCTAssertEqual(_attributes.editing, copy.editing); + XCTAssertEqual(_attributes.shouldShowReorderStateMask, copy.shouldShowReorderStateMask); + XCTAssertEqual(_attributes.shouldShowSelectorStateMask, copy.shouldShowSelectorStateMask); + XCTAssertEqual(_attributes.shouldShowGridBackground, copy.shouldShowGridBackground); + XCTAssertEqualObjects(_attributes.backgroundImage, copy.backgroundImage); + XCTAssertEqual(_attributes.isGridLayout, copy.isGridLayout); + XCTAssertEqual(_attributes.sectionOrdinalPosition, copy.sectionOrdinalPosition); + XCTAssertEqualObjects(_attributes.separatorColor, copy.separatorColor); + XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(_attributes.separatorInset, copy.separatorInset)); + XCTAssertEqual(_attributes.separatorLineHeight, copy.separatorLineHeight); + XCTAssertEqual(_attributes.shouldHideSeparators, copy.shouldHideSeparators); + XCTAssertEqual(_attributes.willAnimateCellsOnAppearance, copy.willAnimateCellsOnAppearance); + XCTAssertEqual(_attributes.animateCellsOnAppearanceDuration, + copy.animateCellsOnAppearanceDuration); + XCTAssertEqual(_attributes.animateCellsOnAppearanceDelay, copy.animateCellsOnAppearanceDelay); +} + +- (void)testEqualAfterCopying { + // Given + + // When + MDCCollectionViewLayoutAttributes *copy = [_attributes copy]; + + // Then + XCTAssertEqualObjects(_attributes, copy); +} + +- (void)testEqualHashesAfterCopying { + // Given + + // When + MDCCollectionViewLayoutAttributes *copy = [_attributes copy]; + + // Then + XCTAssertEqual(_attributes.hash, copy.hash); +} + +@end From 24972cb03d7a9ad30c635fd865ffe428788bda95 Mon Sep 17 00:00:00 2001 From: Chris Cox Date: Fri, 22 Apr 2016 16:54:50 -0400 Subject: [PATCH 115/129] [Collections] Updates to readme. Reviewers: iangordon, featherless, #mdc_ios_owners Reviewed By: iangordon, featherless, #mdc_ios_owners Subscribers: featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D746 --- components/Collections/README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/components/Collections/README.md b/components/Collections/README.md index b68b150d680..86e0a9fc971 100644 --- a/components/Collections/README.md +++ b/components/Collections/README.md @@ -2,14 +2,14 @@ title: "Collections" layout: detail section: components -excerpt: "Collection view classes that adhere to Material design layout and animation styling." +excerpt: "Collection view classes that adhere to Material design layout and styling." --- # Collections ![Collections](docs/assets/collections_screenshot.png) -Collection view classes that adhere to Material design layout and animation styling. +Collection view classes that adhere to Material design layout and styling. ### Material Design Specifications @@ -68,14 +68,14 @@ Before using Collections, you'll need to import it: #### Swift ~~~ swift -import MaterialCollections +import MaterialComponents.MaterialCollections ~~~ ### Use `MDCCollectionViewController` as a view controller -The following four steps will allow you to get a basic example to get a MDCCollectionViewController -up and running. +The following four steps will allow you to get a basic example of a `MDCCollectionViewController` +subclass up and running. Step 1: **Subclass `MDCCollectionViewController` in your view controller interface**. @@ -90,6 +90,8 @@ Step 1: **Subclass `MDCCollectionViewController` in your view controller interfa #### Swift ~~~ swift +import MaterialComponents.MaterialCollections + class MyCollectionsExample: MDCCollectionViewController { } ~~~ @@ -172,7 +174,8 @@ override func collectionView(collectionView: UICollectionView, The collection view controller provides a `styler` property that conforms to the `MDCCollectionViewStyling` protocol. By using this property, styling can be easily set for the collection view items/sections. In addition, by overriding `MDCCollectionViewStyleDelegate` -protocol methods in a collection view subclass, specific cells/sections can be styled differently. +protocol methods in a collection view controller subclass, specific cells/sections can be styled +differently. ### Cell Styles @@ -186,7 +189,7 @@ either set the styler `cellStyle` property directly, or use the protocol method // Set for entire collection view. self.styler.cellStyle = MDCCollectionViewCellStyleCard; - +// Or set for specific sections. - (MDCCollectionViewCellStyle)collectionView:(UICollectionView *)collectionView cellStyleForSection:(NSInteger)section { if (section == 2) { @@ -214,7 +217,7 @@ override func collectionView(collectionView: UICollectionView, ### Cell Height -The styling delegate protocol can be used to override the default cell height of 48.0f. +The styling delegate protocol can be used to override the default cell height of `48.0f`. #### Objective-C @@ -271,7 +274,7 @@ self.styler.gridColumnCount = 2 ### Cell Separators The styler allows customizing cell separators for the entire collection view. Individual -cell customization is also available by using a cell subclassed from `MDCCollectionViewCell`. +cell customization is also available by using an `MDCCollectionViewCell` cell or a subclass of it. Learn more by reading the section on [Cell Separators](../CollectionCells/#cell-separators) in the [CollectionCells](../CollectionCells) component. From 32251b39036e21c4a356d4c88c8ca6e32a8516ee Mon Sep 17 00:00:00 2001 From: Yiran Mao Date: Fri, 22 Apr 2016 17:16:41 -0400 Subject: [PATCH 116/129] Make build site script work in forked repositories. Designated GITHUB_REMOTE always to the main repository. As a consequence, the community is able to update the content but not styling the site, unless they modify the script. --- scripts/build-site.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/build-site.sh b/scripts/build-site.sh index bf773c1f885..85302602f79 100755 --- a/scripts/build-site.sh +++ b/scripts/build-site.sh @@ -63,7 +63,11 @@ fi # Checking pre-requsits for folders # Getting the link for github repository -GITHUB_REMOTE=`git remote -v | grep "fetch" | sed -e 's/origin.//' | sed -e 's/.(fetch)//' | grep -v "\t"` +if [ `git remote -v | grep "https://" | wc -l` -gt 0 ]; then + GITHUB_REMOTE=https://github.com/google/material-components-ios.git +else + GITHUB_REMOTE=git@github.com:google/material-components-ios.git +fi SITE_SOURCE_BRANCH="site-source" SITE_SOURCE_FOLDER=$SITE_SOURCE_BRANCH From 0859d4b2b2ba8e7e9d91fbd757ac4b0a006384fc Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Fri, 22 Apr 2016 16:30:25 -0400 Subject: [PATCH 117/129] [Typography] Resolve iOS 8.4 crash in the Typography hero demo. Summary: Due to missing initializer. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D744 --- .../examples/TypographyFontListExample.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/components/Typography/examples/TypographyFontListExample.swift b/components/Typography/examples/TypographyFontListExample.swift index 13b16d6c3b8..2cb86750922 100644 --- a/components/Typography/examples/TypographyFontListExample.swift +++ b/components/Typography/examples/TypographyFontListExample.swift @@ -61,12 +61,20 @@ class TypographyFontListExampleViewController: UITableViewController { return cell! } + convenience init() { + self.init(style: .Plain) + } + override init(style: UITableViewStyle) { - super.init(style: .Plain) + super.init(style: style) self.title = "Font list" } + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + } + required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } From ed9c65c3d22e1318aac4b06b8442e6f8e7fa3428 Mon Sep 17 00:00:00 2001 From: Junius Gunaratne Date: Fri, 22 Apr 2016 17:41:43 -0400 Subject: [PATCH 118/129] [Catalog] Fix color change issue in header stack view demo Summary: Addresses https://github.com/google/material-components-ios/issues/383 Reviewers: ajsecord, featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D748 --- .../HeaderStackViewTypicalUseSupplemental.h | 1 - .../HeaderStackViewTypicalUseSupplemental.m | 42 +++++++++++-------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.h b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.h index 44b5ed390ac..7dc4ca50ae5 100644 --- a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.h +++ b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.h @@ -30,7 +30,6 @@ limitations under the License. @property(nonatomic) ExampleInstructionsViewHeaderStackViewTypicalUse *_Nullable exampleView; @property(nonatomic) MDCHeaderStackView *_Nullable stackView; @property(nonatomic) MDCNavigationBar *_Nullable navBar; -@property(nonatomic) UIBarButtonItem *_Nullable moreButton; @property(nonatomic) UIView *_Nullable topView; @end diff --git a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m index 3dccc9ac18d..564a743ffa4 100644 --- a/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m +++ b/components/HeaderStackView/examples/supplemental/HeaderStackViewTypicalUseSupplemental.m @@ -59,11 +59,21 @@ - (void)setupExampleViews { // Light blue 500 [self.navBar setBackgroundColor:[UIColor colorWithRed:0.012 green:0.663 blue:0.957 alpha:1]]; - self.moreButton = [[UIBarButtonItem alloc] initWithTitle:@"More" - style:UIBarButtonItemStylePlain - target:self - action:@selector(didTapToggleButton:)]; - self.navBar.rightBarButtonItems = @[ self.moreButton ]; + UIBarButtonItem *moreButton = + [[UIBarButtonItem alloc] initWithTitle:@"More" + style:UIBarButtonItemStylePlain + target:self + action:@selector(didTapToggleButton:)]; + + // Set the title text attributes before assigning to buttonBar.items + // because of https://github.com/google/material-components-ios/issues/277 + [moreButton setTitleTextAttributes:[self itemTitleTextAttributes] forState:UIControlStateNormal]; + self.navBar.rightBarButtonItems = @[ moreButton ]; +} + +- (NSDictionary *)itemTitleTextAttributes { + UIColor *textColor = [UIColor whiteColor]; + return @{NSForegroundColorAttributeName : textColor}; } - (void)didTapToggleButton:(id)sender { @@ -74,10 +84,8 @@ - (void)didTapToggleButton:(id)sender { CGRect frame = self.stackView.frame; if (self.toggled) { frame.size.height = 200; - self.moreButton.title = @"Less"; } else { frame.size = [self.stackView sizeThatFits:self.stackView.bounds.size]; - self.moreButton.title = @"More"; } self.stackView.frame = frame; [self.stackView layoutIfNeeded]; @@ -155,19 +163,19 @@ - (NSAttributedString *)instructionsString { NSDictionary *instructionAttributes1 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSDictionary *instructionAttributes2 = @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 - green:0.459 - blue:0.459 - alpha:0.87f], - NSParagraphStyleAttributeName : style}; + NSForegroundColorAttributeName : [UIColor colorWithRed:0.459 + green:0.459 + blue:0.459 + alpha:0.87f], + NSParagraphStyleAttributeName : style}; NSString *instructionText = @"SWIPE RIGHT\n\n\n\nto go back\n\n\n\n\n\n"; NSMutableAttributedString *instructionsAttributedString = [[NSMutableAttributedString alloc] From a858523cb27c6b7f891d5cfec4722172deb71b5a Mon Sep 17 00:00:00 2001 From: Chris Cox Date: Fri, 22 Apr 2016 18:09:51 -0400 Subject: [PATCH 119/129] [Collections] Updates readmes. Reviewers: featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Subscribers: featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D749 --- components/CollectionCells/README.md | 40 ++++++++-- .../docs/assets/collections_screenshot.png | Bin 0 -> 101247 bytes .../CollectionLayoutAttributes/README.md | 70 ++++++++++++++++-- components/Collections/README.md | 4 +- .../docs/assets/collections_screenshot.png | Bin 102821 -> 101247 bytes 5 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 components/CollectionCells/docs/assets/collections_screenshot.png diff --git a/components/CollectionCells/README.md b/components/CollectionCells/README.md index c9922922682..5b416be28f1 100644 --- a/components/CollectionCells/README.md +++ b/components/CollectionCells/README.md @@ -2,10 +2,31 @@ title: "Collection Cells" layout: detail section: components -excerpt: "" +excerpt: "Collection view cell classes that adhere to Material design layout and styling." --- # Collection Cells +![Collections](docs/assets/collections_screenshot.png) + + +Collection view cell classes that adhere to Material design layout and styling. + + +### Material Design Specifications + + + +### API Documentation + + + +- - - + ## Installation ### Requirements @@ -29,11 +50,20 @@ $ pod install - - - - ## Usage -```objectivec +### Importing -// Example usage +Before using Collection Cells, you'll need to import it: -``` + +#### Objective-C +~~~ objc +#import "MaterialCollectionCells.h" +~~~ + +#### Swift +~~~ swift +import MaterialComponents.MaterialCollectionCells +~~~ + diff --git a/components/CollectionCells/docs/assets/collections_screenshot.png b/components/CollectionCells/docs/assets/collections_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..2c0b1d8a6f7a18c9b0305d3a098020824bdc9311 GIT binary patch literal 101247 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
}P_+U!28Y(pe67 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 J*-Y! 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 literal 0 HcmV?d00001 diff --git a/components/CollectionLayoutAttributes/README.md b/components/CollectionLayoutAttributes/README.md index 92dca5bea02..f2128136d3b 100644 --- a/components/CollectionLayoutAttributes/README.md +++ b/components/CollectionLayoutAttributes/README.md @@ -2,10 +2,24 @@ title: "Collection Layout Attributes" layout: detail section: components -excerpt: "" +excerpt: "Allows passing layout attributes to the cells and supplementary views." --- # Collection Layout Attributes +![Collections](docs/assets/collections_screenshot.png) + + +Allows passing layout attributes to the cells and supplementary views. + + +### API Documentation + +

+ +- - - + ## Installation ### Requirements @@ -18,7 +32,7 @@ excerpt: "" To add this component to your Xcode project using CocoaPods, add the following to your `Podfile`: ~~~ -pod 'MaterialComponents/CollectionViewLayouts' +pod 'MaterialComponents/CollectionLayoutAttributes' ~~~ Then, run the following command: @@ -29,11 +43,55 @@ $ pod install - - - - ## Usage -```objectivec +### Importing -// Example usage +Before using Collection Layout Attributes, you'll need to import it: + + +#### Objective-C +~~~ objc +#import "MaterialCollectionLayoutAttributes.h" +~~~ -``` +#### Swift +~~~ swift +import MaterialComponents.MaterialCollectionLayoutAttributes +~~~ + + +The `MDCCollectionViewLayoutAttributes` class allows passing properties to a cell from a collection +view layout. Override the `-applyLayoutAttributes` method of any `UICollectionReusableView` or +`UICollectionViewCell` subclasses, then apply any of the properties of the attributes class. + + +#### Objective-C +~~~ objc +- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes { + [super applyLayoutAttributes:layoutAttributes]; + if ([layoutAttributes isKindOfClass:[MDCCollectionViewLayoutAttributes class]]) { + MDCCollectionViewLayoutAttributes *attr = (MDCCollectionViewLayoutAttributes *)layoutAttributes; + if (attr.representedElementCategory == UICollectionElementCategoryCell) { + + // Example to set a background image to the cell background view. + self.backgroundView = [[UIImageView alloc] initWithImage:attr.backgroundImage]; + } + } +} +~~~ + +#### Swift +~~~ swift +override func applyLayoutAttributes(layoutAttributes: UICollectionViewLayoutAttributes) { + super.applyLayoutAttributes(layoutAttributes) + if let attr = layoutAttributes as? MDCCollectionViewLayoutAttributes { + if (attr.representedElementCategory == .Cell) { + + // Example to set a background image to the cell background view. + self.backgroundView = UIImageView(image: attr.backgroundImage) + } + } +} +~~~ + diff --git a/components/Collections/README.md b/components/Collections/README.md index 86e0a9fc971..5c4cdbfefde 100644 --- a/components/Collections/README.md +++ b/components/Collections/README.md @@ -22,10 +22,10 @@ Collection view classes that adhere to Material design layout and styling. diff --git a/components/Collections/docs/assets/collections_screenshot.png b/components/Collections/docs/assets/collections_screenshot.png index 2cf6d537aa169887107552ba14a046770e87254c..2c0b1d8a6f7a18c9b0305d3a098020824bdc9311 100644 GIT binary patch delta 87744 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

4fd?iyey0SO5S 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 delta 89303 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

    FahS2D#@0Aa&q^S3^8~8nH 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�+%MPk<(qq4FWW|>Od28 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 Date: Fri, 22 Apr 2016 18:28:37 -0400 Subject: [PATCH 120/129] Removes broken image. Reviewers: featherless, #mdc_ios_owners Reviewed By: featherless, #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D750 --- components/CollectionLayoutAttributes/README.md | 3 --- .../Collections/examples/CollectionsSimpleSwiftDemo.swift | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/components/CollectionLayoutAttributes/README.md b/components/CollectionLayoutAttributes/README.md index f2128136d3b..54d4c2e9b14 100644 --- a/components/CollectionLayoutAttributes/README.md +++ b/components/CollectionLayoutAttributes/README.md @@ -6,9 +6,6 @@ excerpt: "Allows passing layout attributes to the cells and supplementary views. --- # Collection Layout Attributes -![Collections](docs/assets/collections_screenshot.png) - - Allows passing layout attributes to the cells and supplementary views. diff --git a/components/Collections/examples/CollectionsSimpleSwiftDemo.swift b/components/Collections/examples/CollectionsSimpleSwiftDemo.swift index 854551711dd..53ab1e4de61 100644 --- a/components/Collections/examples/CollectionsSimpleSwiftDemo.swift +++ b/components/Collections/examples/CollectionsSimpleSwiftDemo.swift @@ -43,7 +43,7 @@ class CollectionsSimpleSwiftDemo: MDCCollectionViewController { override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { - var cell = collectionView.dequeueReusableCellWithReuseIdentifier(reusableIdentifierItem, + let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reusableIdentifierItem, forIndexPath: indexPath) if let cell = cell as? MDCCollectionViewTextCell { cell.textLabel?.text = colors[indexPath.item] From 3ef520462a07f8b015e96b3f5497ec11ff6caf28 Mon Sep 17 00:00:00 2001 From: Louis Romero Date: Mon, 25 Apr 2016 14:27:57 +0200 Subject: [PATCH 121/129] Cells divider is 1 pixel. Summary: The divider between the cells is now 1 pixels for all devices. Prior to this diff, it was 1 point. Test Plan: Check that the divider is 1 pixel on all configurations. Reviewers: #mdc_ios_owners, cjcox Reviewed By: #mdc_ios_owners, cjcox Projects: #material_components_ios Differential Revision: http://codereview.cc/D753 --- components/Collections/src/private/MDCCollectionViewStyler.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/Collections/src/private/MDCCollectionViewStyler.m b/components/Collections/src/private/MDCCollectionViewStyler.m index 55960d90494..0e42c353c10 100644 --- a/components/Collections/src/private/MDCCollectionViewStyler.m +++ b/components/Collections/src/private/MDCCollectionViewStyler.m @@ -47,7 +47,7 @@ typedef NS_OPTIONS(NSUInteger, BackgroundCacheKey) { static const UIEdgeInsets kCollectionViewCellContentInsets = {1, 2, 1, 2}; /** Default cell separator style settings */ -static const CGFloat kCollectionViewCellSeparatorDefaultHeight = 1.0f; +static const CGFloat kCollectionViewCellSeparatorDefaultHeightInPixels = 1.0f; /** Grid layout defaults */ static const NSInteger kCollectionViewGridDefaultColumnCount = 2; @@ -139,7 +139,8 @@ - (instancetype)initWithCollectionView:(UICollectionView *)collectionView { // Cell separator defaults. _separatorColor = RGBCOLOR(224, 224, 224); _separatorInset = UIEdgeInsetsZero; - _separatorLineHeight = kCollectionViewCellSeparatorDefaultHeight; + _separatorLineHeight = + kCollectionViewCellSeparatorDefaultHeightInPixels / [[UIScreen mainScreen] scale]; _shouldHideSeparators = NO; // Grid defaults. From f822350621b6297d301a72166e89cba4229c3ec5 Mon Sep 17 00:00:00 2001 From: Ian Gordon Date: Fri, 22 Apr 2016 13:31:13 -0400 Subject: [PATCH 122/129] [Catalog] Bump deployment target to 9.0 until we have audited our catalog for 8.x compatibility Reviewers: featherless, #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Subscribers: ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D731 --- catalog/MDCCatalog.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/catalog/MDCCatalog.xcodeproj/project.pbxproj b/catalog/MDCCatalog.xcodeproj/project.pbxproj index 417ecf1c128..aa740000ab8 100644 --- a/catalog/MDCCatalog.xcodeproj/project.pbxproj +++ b/catalog/MDCCatalog.xcodeproj/project.pbxproj @@ -575,7 +575,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -614,7 +614,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; From a48e31fb2e9aa814198ad3c510b9e653a0f91e72 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Mon, 25 Apr 2016 11:27:37 -0400 Subject: [PATCH 123/129] [CollectionCells] Update README to indicate its present state. Summary: Collection Cells don't currently have a clear story for their standalone use. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D755 --- components/CollectionCells/README.md | 33 ++-------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/components/CollectionCells/README.md b/components/CollectionCells/README.md index 5b416be28f1..40c08773afe 100644 --- a/components/CollectionCells/README.md +++ b/components/CollectionCells/README.md @@ -34,36 +34,7 @@ Collection view cell classes that adhere to Material design layout and styling. - Xcode 7.0 or higher. - iOS SDK version 7.0 or higher. -### Installation with CocoaPods - -To add this component to your Xcode project using CocoaPods, add the following to your `Podfile`: - -~~~ -pod 'MaterialComponents/CollectionCells' -~~~ - -Then, run the following command: - -~~~ bash -$ pod install -~~~ - -- - - - ## Usage -### Importing - -Before using Collection Cells, you'll need to import it: - - -#### Objective-C -~~~ objc -#import "MaterialCollectionCells.h" -~~~ - -#### Swift -~~~ swift -import MaterialComponents.MaterialCollectionCells -~~~ - +Please see the [Collections](../Collections) component for more information about using Collection +Cells. We do not presently support using Collection Cells as a standalone component. From a0690af49c5c92b1659950863ba8df7d21ff4428 Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Thu, 21 Apr 2016 18:27:58 -0400 Subject: [PATCH 124/129] [FlexibleHeader] Convert typical use example to use Interface Builder + auto layout. Summary: Now handles landscape mode and does not crash on iOS 8. Closes https://github.com/google/material-components-ios/issues/321. Closes https://github.com/google/material-components-ios/issues/406. Reviewers: randallli, #mdc_ios_owners Reviewed By: randallli, #mdc_ios_owners Subscribers: randallli Projects: #material_components_ios Differential Revision: http://codereview.cc/D716 --- .../FlexibleHeaderTypicalUseExample.m | 5 +- ...exibleHeaderTypicalUseInstructionsView.xib | 89 ++++++++++++ .../examples/resources/ic_swap_vert.png | Bin 0 -> 128 bytes .../examples/resources/ic_swap_vert@2x.png | Bin 0 -> 186 bytes .../examples/resources/ic_swap_vert@3x.png | Bin 0 -> 230 bytes ...FlexibleHeaderTypicalUseInstructionsView.h | 25 ---- ...FlexibleHeaderTypicalUseInstructionsView.m | 129 ------------------ .../FlexibleHeaderTypicalUseSupplemental.h | 6 +- .../FlexibleHeaderTypicalUseSupplemental.m | 64 ++------- 9 files changed, 108 insertions(+), 210 deletions(-) create mode 100644 components/FlexibleHeader/examples/resources/FlexibleHeaderTypicalUseInstructionsView.xib create mode 100644 components/FlexibleHeader/examples/resources/ic_swap_vert.png create mode 100644 components/FlexibleHeader/examples/resources/ic_swap_vert@2x.png create mode 100644 components/FlexibleHeader/examples/resources/ic_swap_vert@3x.png delete mode 100644 components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.h delete mode 100644 components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.m diff --git a/components/FlexibleHeader/examples/FlexibleHeaderTypicalUseExample.m b/components/FlexibleHeader/examples/FlexibleHeaderTypicalUseExample.m index 7b1c20042f4..64e2d205159 100644 --- a/components/FlexibleHeader/examples/FlexibleHeaderTypicalUseExample.m +++ b/components/FlexibleHeader/examples/FlexibleHeaderTypicalUseExample.m @@ -60,7 +60,8 @@ - (void)commonMDCFlexibleHeaderViewControllerInit { - (void)viewDidLoad { [super viewDidLoad]; - self.scrollView = [[UIScrollView alloc] init]; + self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; + self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self.view addSubview:self.scrollView]; // If a tableView was being used instead of a scrollView, you would set the trackingScrollView @@ -71,9 +72,7 @@ - (void)viewDidLoad { self.fhvc.headerView.trackingScrollView = self.scrollView; self.fhvc.view.frame = self.view.bounds; - [self.view addSubview:self.fhvc.view]; - [self.fhvc didMoveToParentViewController:self]; // Light blue 500 diff --git a/components/FlexibleHeader/examples/resources/FlexibleHeaderTypicalUseInstructionsView.xib b/components/FlexibleHeader/examples/resources/FlexibleHeaderTypicalUseInstructionsView.xib new file mode 100644 index 00000000000..c024d153fe9 --- /dev/null +++ b/components/FlexibleHeader/examples/resources/FlexibleHeaderTypicalUseInstructionsView.xib @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/FlexibleHeader/examples/resources/ic_swap_vert.png b/components/FlexibleHeader/examples/resources/ic_swap_vert.png new file mode 100644 index 0000000000000000000000000000000000000000..90f8c4567e2aacf7295f7aa61568b65a4d5bb76c GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1cTX3`kP61DXASv|C&qdQ$w!t&+ca*7L?3JECyIq2Ed` ldC}x0>hr#L@-2N*&Fq-{?t4VOtU1sz44$rjF6*2UngC=SOwIrR literal 0 HcmV?d00001 diff --git a/components/FlexibleHeader/examples/resources/ic_swap_vert@3x.png b/components/FlexibleHeader/examples/resources/ic_swap_vert@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..78e865dfae18994c1e7c0f40fdc9a57294a00fc0 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhaw)_S@)hEy=Vy=lwGWXQw%u>7;d zP5E^`jVV%HQ(HM$i*!srpE8M;?e6HPn|Ly0h2He*io8Fw_lhlN$(-EItD7@@|IM(* z;D(YD_Br82%WF0*7E_;EGP!h**57GSAkRVsh3`PXDmxSYTv%pI=5wm-9sJqnOrFogIiPnmusjg bEmB{gCV4q2c*1p{!x=nX{an^LB{Ts56RcS| literal 0 HcmV?d00001 diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.h b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.h deleted file mode 100644 index 4c16df1ec1e..00000000000 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - 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. - */ - -/** 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 - -@interface FlexibleHeaderTypicalUseInstructionsView : UIView -@end diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.m deleted file mode 100644 index 81983370168..00000000000 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseInstructionsView.m +++ /dev/null @@ -1,129 +0,0 @@ -/* - 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. - */ - -/** 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 "FlexibleHeaderTypicalUseInstructionsView.h" - -@implementation FlexibleHeaderTypicalUseInstructionsView - -- (void)drawRect:(CGRect)rect { - [[UIColor whiteColor] setFill]; - [[UIBezierPath bezierPathWithRect:rect] fill]; - - CGSize textSize = [self textSizeForRect:rect]; - CGRect rectForText = CGRectMake(rect.origin.x + rect.size.width / 2.f - textSize.width / 2.f, - rect.origin.y + rect.size.height / 2.f - textSize.height / 2.f, - textSize.width, textSize.height); - [[self instructionsString] drawInRect:rectForText]; - [self drawArrowWithFrame:CGRectMake(rect.size.width / 2.f - 12.f, - rect.size.height / 2.f - 58.f - 12.f, 24.f, 24.f)]; -} - -- (CGSize)textSizeForRect:(CGRect)frame { - return [[self instructionsString] - boundingRectWithSize:frame.size - options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading - context:nil] - .size; -} - -- (NSAttributedString *)instructionsString { - NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; - [style setAlignment:NSTextAlignmentCenter]; - [style setLineBreakMode:NSLineBreakByWordWrapping]; - - NSDictionary *instructionAttributes1 = - @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:(CGFloat)0.459 - green:(CGFloat)0.459 - blue:(CGFloat)0.459 - alpha:(CGFloat)0.87], - NSParagraphStyleAttributeName : style}; - NSDictionary *instructionAttributes2 = - @{NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline], - NSForegroundColorAttributeName : [UIColor colorWithRed:(CGFloat)0.459 - green:(CGFloat)0.459 - blue:(CGFloat)0.459 - alpha:(CGFloat)0.87], - NSParagraphStyleAttributeName : style}; - - NSString *instructionText1 = @"PULL DOWN\n\nMDCFlexibleHeaderViewController\nallows the\ - blue area to stretch\nwhen scrolled down.\n\n\n\n\n\n"; - NSMutableAttributedString *instructionsAttributedString1 = [[NSMutableAttributedString alloc] - initWithString:instructionText1]; - [instructionsAttributedString1 setAttributes:instructionAttributes1 - range:NSMakeRange(0, 9)]; - [instructionsAttributedString1 setAttributes:instructionAttributes2 - range:NSMakeRange(9, instructionText1.length - 9)]; - - NSString *instructionText2 = @"PUSH UP\n\nIt also adds a shadow\nwhen scrolled up.\n\n\n\n\n\n\n"; - NSMutableAttributedString *instructionsAttributedString2 = [[NSMutableAttributedString alloc] - initWithString:instructionText2]; - [instructionsAttributedString2 setAttributes:instructionAttributes1 - range:NSMakeRange(0, 7)]; - [instructionsAttributedString2 setAttributes:instructionAttributes2 - range:NSMakeRange(7, instructionText2.length - 7)]; - - [instructionsAttributedString1 appendAttributedString:instructionsAttributedString2]; - - return instructionsAttributedString1; -} - -- (void)drawArrowWithFrame:(CGRect)frame { - UIBezierPath *bezierPath = [UIBezierPath bezierPath]; - [bezierPath moveToPoint:CGPointMake(CGRectGetMinX(frame) + 16, - CGRectGetMinY(frame) + (CGFloat)17.01)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 16, CGRectGetMinY(frame) + 10)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 14, CGRectGetMinY(frame) + 10)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 14, - CGRectGetMinY(frame) + (CGFloat)17.01)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 11, - CGRectGetMinY(frame) + (CGFloat)17.01)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 15, CGRectGetMinY(frame) + 21)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 19, - CGRectGetMinY(frame) + (CGFloat)17.01)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 16, - CGRectGetMinY(frame) + (CGFloat)17.01)]; - [bezierPath closePath]; - [bezierPath moveToPoint:CGPointMake(CGRectGetMinX(frame) + 9, CGRectGetMinY(frame) + 3)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 5, - CGRectGetMinY(frame) + (CGFloat)6.99)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 8, - CGRectGetMinY(frame) + (CGFloat)6.99)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 8, CGRectGetMinY(frame) + 14)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 10, - CGRectGetMinY(frame) + 14)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 10, - CGRectGetMinY(frame) + (CGFloat)6.99)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 13, - CGRectGetMinY(frame) + (CGFloat)6.99)]; - [bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame) + 9, - CGRectGetMinY(frame) + 3)]; - [bezierPath closePath]; - bezierPath.miterLimit = 4; - - [[UIColor colorWithRed:(CGFloat)0.459 - green:(CGFloat)0.459 - blue:(CGFloat)0.459 - alpha:0.87f] setFill]; - [bezierPath fill]; -} - -@end diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.h b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.h index 4133233c002..ff4ee71bb61 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.h +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.h @@ -25,8 +25,10 @@ @interface FlexibleHeaderTypicalUseViewController : UIViewController -@property(nonatomic) FlexibleHeaderTypicalUseInstructionsView *exampleView; -@property(nonatomic) UIScrollView *scrollView; +@property(nonatomic, strong, nullable) IBOutlet UIView *exampleView; +@property(nonatomic, strong, nullable) UIScrollView *scrollView; + +@property(nonatomic, strong, nullable) IBOutlet UIImageView *imageView; @end diff --git a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m index eda5eb5419d..936894b437a 100644 --- a/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m +++ b/components/FlexibleHeader/examples/supplemental/FlexibleHeaderTypicalUseSupplemental.m @@ -21,7 +21,6 @@ #import -#import "FlexibleHeaderTypicalUseInstructionsView.h" #import "FlexibleHeaderTypicalUseSupplemental.h" @implementation FlexibleHeaderTypicalUseViewController (CatalogByConvention) @@ -45,61 +44,24 @@ + (BOOL)catalogIsPrimaryDemo { @end -@implementation FlexibleHeaderTypicalUseViewController (Rotation) - -- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation - duration:(NSTimeInterval)duration { - [self.exampleView setNeedsDisplay]; -} - -@end - @implementation FlexibleHeaderTypicalUseViewController (Supplemental) - (void)setupExampleViews { - self.scrollView.translatesAutoresizingMaskIntoConstraints = NO; - - NSDictionary *viewBindings = @{ @"scrollView" : self.scrollView }; - NSMutableArray<__kindof NSLayoutConstraint *> *arrayOfConstraints = [NSMutableArray array]; - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"H:|[scrollView]|" - options:0 - metrics:nil - views:viewBindings]]; - [arrayOfConstraints addObjectsFromArray:[NSLayoutConstraint - constraintsWithVisualFormat:@"V:|[scrollView]|" - options:0 - metrics:nil - views:viewBindings]]; - - [self.view addConstraints:arrayOfConstraints]; - - self.exampleView = [[FlexibleHeaderTypicalUseInstructionsView alloc] - initWithFrame:self.scrollView.bounds]; + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + [bundle loadNibNamed:@"FlexibleHeaderTypicalUseInstructionsView" owner:self options:nil]; + + self.imageView.image = [self.imageView.image + imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [self.scrollView addSubview:self.exampleView]; - self.exampleView.translatesAutoresizingMaskIntoConstraints = NO; - - NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:self.exampleView - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeHeight - multiplier:1 - constant:0]; - - NSLayoutConstraint *centerX = [self.exampleView.centerXAnchor - constraintEqualToAnchor:self.view.centerXAnchor]; - NSLayoutConstraint *top = [self.exampleView.topAnchor - constraintEqualToAnchor:self.scrollView.topAnchor]; - NSLayoutConstraint *bottom = [self.exampleView.bottomAnchor - constraintEqualToAnchor:self.scrollView.bottomAnchor]; - NSLayoutConstraint *leading = [self.exampleView.leadingAnchor - constraintEqualToAnchor:self.scrollView.leadingAnchor]; - NSLayoutConstraint *trailing = [self.exampleView.trailingAnchor - constraintEqualToAnchor:self.scrollView.trailingAnchor]; - - [self.view addConstraints:@[ width, centerX, top, bottom, leading, trailing ]]; + self.view.backgroundColor = [UIColor whiteColor]; +} + +- (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + + self.exampleView.frame = self.view.bounds; + self.scrollView.contentSize = self.view.bounds.size; } @end From c71acb58b9d20a917017fbd0e82e33f24f3d870e Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Mon, 25 Apr 2016 12:26:08 -0400 Subject: [PATCH 125/129] [Catalog] Re-add iOS 8 support. Summary: This reverts commit f822350621b6297d301a72166e89cba4229c3ec5. Test Plan: revert-hammer Reviewers: #mdc_ios_owners, iangordon Reviewed By: #mdc_ios_owners, iangordon Projects: #material_components_ios Differential Revision: http://codereview.cc/D756 --- catalog/MDCCatalog.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/catalog/MDCCatalog.xcodeproj/project.pbxproj b/catalog/MDCCatalog.xcodeproj/project.pbxproj index aa740000ab8..417ecf1c128 100644 --- a/catalog/MDCCatalog.xcodeproj/project.pbxproj +++ b/catalog/MDCCatalog.xcodeproj/project.pbxproj @@ -575,7 +575,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -614,7 +614,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; From 7598638db3ecd26e09a82988857a86806bc1dfe5 Mon Sep 17 00:00:00 2001 From: Chris Cox Date: Mon, 25 Apr 2016 12:07:22 -0400 Subject: [PATCH 126/129] [CollectionCells] Updates cells to depend on MDCIcons for editing and accessory icons. Summary: - Updates podspec to use these new dependancies. - Removes private cell resource class. Reviewers: cjcox, #mdc_ios_owners, featherless Reviewed By: #mdc_ios_owners, featherless Projects: #material_components_ios Differential Revision: http://codereview.cc/D751 --- MaterialComponents.podspec | 9 +- catalog/Podfile.lock | 26 ++++- .../src/MDCCollectionViewCell.m | 47 ++++++-- .../src/private/MDCCollectionCellResources.h | 51 --------- .../src/private/MDCCollectionCellResources.m | 102 ------------------ .../ic_check/src/MaterialIcons+ic_check.h | 27 +++++ .../ic_check/src/MaterialIcons+ic_check.m | 33 ++++++ .../ic_check.imageset/Contents.json | 23 ++++ .../ic_check.imageset/ic_check.png | Bin 0 -> 128 bytes .../ic_check.imageset/ic_check@2x.png | Bin 0 -> 188 bytes .../ic_check.imageset/ic_check@3x.png | Bin 0 -> 254 bytes .../src/MaterialIcons+ic_check_circle.h | 27 +++++ .../src/MaterialIcons+ic_check_circle.m | 33 ++++++ .../ic_check_circle.imageset/Contents.json | 23 ++++ .../ic_check_circle.png | Bin 0 -> 260 bytes .../ic_check_circle@2x.png | Bin 0 -> 493 bytes .../ic_check_circle@3x.png | Bin 0 -> 709 bytes .../src/MaterialIcons+ic_chevron_right.h | 27 +++++ .../src/MaterialIcons+ic_chevron_right.m | 33 ++++++ .../ic_chevron_right.imageset/Contents.json | 23 ++++ .../ic_chevron_right.png | Bin 0 -> 109 bytes .../ic_chevron_right@2x.png | Bin 0 -> 138 bytes .../ic_chevron_right@3x.png | Bin 0 -> 200 bytes .../icons/ic_info/src/MaterialIcons+ic_info.h | 27 +++++ .../icons/ic_info/src/MaterialIcons+ic_info.m | 33 ++++++ .../ic_info.imageset/Contents.json | 23 ++++ .../ic_info.imageset/ic_info.png | Bin 0 -> 222 bytes .../ic_info.imageset/ic_info@2x.png | Bin 0 -> 412 bytes .../ic_info.imageset/ic_info@3x.png | Bin 0 -> 579 bytes .../MaterialIcons+ic_radio_button_unchecked.h | 27 +++++ .../MaterialIcons+ic_radio_button_unchecked.m | 33 ++++++ .../Contents.json | 23 ++++ .../ic_radio_button_unchecked.png | Bin 0 -> 307 bytes .../ic_radio_button_unchecked@2x.png | Bin 0 -> 621 bytes .../ic_radio_button_unchecked@3x.png | Bin 0 -> 915 bytes .../ic_reorder/src/MaterialIcons+ic_reorder.h | 27 +++++ .../ic_reorder/src/MaterialIcons+ic_reorder.m | 33 ++++++ .../ic_reorder.imageset/Contents.json | 23 ++++ .../ic_reorder.imageset/ic_reorder.png | Bin 0 -> 82 bytes .../ic_reorder.imageset/ic_reorder@2x.png | Bin 0 -> 101 bytes .../ic_reorder.imageset/ic_reorder@3x.png | Bin 0 -> 113 bytes demos/Pesto/Podfile.lock | 26 ++++- demos/Shrine/Podfile.lock | 26 ++++- scripts/generated/icons.rb | 78 ++++++++++++++ 44 files changed, 693 insertions(+), 170 deletions(-) delete mode 100644 components/CollectionCells/src/private/MDCCollectionCellResources.h delete mode 100644 components/CollectionCells/src/private/MDCCollectionCellResources.m create mode 100644 components/private/Icons/icons/ic_check/src/MaterialIcons+ic_check.h create mode 100644 components/private/Icons/icons/ic_check/src/MaterialIcons+ic_check.m create mode 100644 components/private/Icons/icons/ic_check/src/MaterialIcons_ic_check.bundle/ic_check.xcassets/ic_check.imageset/Contents.json create mode 100644 components/private/Icons/icons/ic_check/src/MaterialIcons_ic_check.bundle/ic_check.xcassets/ic_check.imageset/ic_check.png create mode 100644 components/private/Icons/icons/ic_check/src/MaterialIcons_ic_check.bundle/ic_check.xcassets/ic_check.imageset/ic_check@2x.png create mode 100644 components/private/Icons/icons/ic_check/src/MaterialIcons_ic_check.bundle/ic_check.xcassets/ic_check.imageset/ic_check@3x.png create mode 100644 components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h create mode 100644 components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m create mode 100644 components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/Contents.json create mode 100644 components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle.png create mode 100644 components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@2x.png create mode 100644 components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@3x.png create mode 100644 components/private/Icons/icons/ic_chevron_right/src/MaterialIcons+ic_chevron_right.h create mode 100644 components/private/Icons/icons/ic_chevron_right/src/MaterialIcons+ic_chevron_right.m create mode 100644 components/private/Icons/icons/ic_chevron_right/src/MaterialIcons_ic_chevron_right.bundle/ic_chevron_right.xcassets/ic_chevron_right.imageset/Contents.json create mode 100644 components/private/Icons/icons/ic_chevron_right/src/MaterialIcons_ic_chevron_right.bundle/ic_chevron_right.xcassets/ic_chevron_right.imageset/ic_chevron_right.png create mode 100644 components/private/Icons/icons/ic_chevron_right/src/MaterialIcons_ic_chevron_right.bundle/ic_chevron_right.xcassets/ic_chevron_right.imageset/ic_chevron_right@2x.png create mode 100644 components/private/Icons/icons/ic_chevron_right/src/MaterialIcons_ic_chevron_right.bundle/ic_chevron_right.xcassets/ic_chevron_right.imageset/ic_chevron_right@3x.png create mode 100644 components/private/Icons/icons/ic_info/src/MaterialIcons+ic_info.h create mode 100644 components/private/Icons/icons/ic_info/src/MaterialIcons+ic_info.m create mode 100644 components/private/Icons/icons/ic_info/src/MaterialIcons_ic_info.bundle/ic_info.xcassets/ic_info.imageset/Contents.json create mode 100644 components/private/Icons/icons/ic_info/src/MaterialIcons_ic_info.bundle/ic_info.xcassets/ic_info.imageset/ic_info.png create mode 100644 components/private/Icons/icons/ic_info/src/MaterialIcons_ic_info.bundle/ic_info.xcassets/ic_info.imageset/ic_info@2x.png create mode 100644 components/private/Icons/icons/ic_info/src/MaterialIcons_ic_info.bundle/ic_info.xcassets/ic_info.imageset/ic_info@3x.png create mode 100644 components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons+ic_radio_button_unchecked.h create mode 100644 components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons+ic_radio_button_unchecked.m create mode 100644 components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/Contents.json create mode 100644 components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked.png create mode 100644 components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked@2x.png create mode 100644 components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked@3x.png create mode 100644 components/private/Icons/icons/ic_reorder/src/MaterialIcons+ic_reorder.h create mode 100644 components/private/Icons/icons/ic_reorder/src/MaterialIcons+ic_reorder.m create mode 100644 components/private/Icons/icons/ic_reorder/src/MaterialIcons_ic_reorder.bundle/ic_reorder.xcassets/ic_reorder.imageset/Contents.json create mode 100644 components/private/Icons/icons/ic_reorder/src/MaterialIcons_ic_reorder.bundle/ic_reorder.xcassets/ic_reorder.imageset/ic_reorder.png create mode 100644 components/private/Icons/icons/ic_reorder/src/MaterialIcons_ic_reorder.bundle/ic_reorder.xcassets/ic_reorder.imageset/ic_reorder@2x.png create mode 100644 components/private/Icons/icons/ic_reorder/src/MaterialIcons_ic_reorder.bundle/ic_reorder.xcassets/ic_reorder.imageset/ic_reorder@3x.png diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec index 8cd56f6a993..fd3545ce280 100644 --- a/MaterialComponents.podspec +++ b/MaterialComponents.podspec @@ -79,13 +79,16 @@ Pod::Spec.new do |s| 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.resource_bundles = { - "Material#{ss.base_name}" => ["components/#{ss.base_name}/src/**/*.{png}"] - } ss.dependency "MaterialComponents/CollectionLayoutAttributes" ss.dependency "MaterialComponents/Ink" ss.dependency "MaterialComponents/Typography" + ss.dependency "MaterialComponents/private/Icons/ic_check" + ss.dependency "MaterialComponents/private/Icons/ic_check_circle" + ss.dependency "MaterialComponents/private/Icons/ic_chevron_right" + ss.dependency "MaterialComponents/private/Icons/ic_info" + ss.dependency "MaterialComponents/private/Icons/ic_radio_button_unchecked" + ss.dependency "MaterialComponents/private/Icons/ic_reorder" end s.subspec "CollectionLayoutAttributes" do |ss| diff --git a/catalog/Podfile.lock b/catalog/Podfile.lock index 3f062ca7493..f05362312db 100644 --- a/catalog/Podfile.lock +++ b/catalog/Podfile.lock @@ -38,6 +38,12 @@ PODS: - MaterialComponents/CollectionCells (4.0.1): - MaterialComponents/CollectionLayoutAttributes - MaterialComponents/Ink + - MaterialComponents/private/Icons/ic_check + - MaterialComponents/private/Icons/ic_check_circle + - MaterialComponents/private/Icons/ic_chevron_right + - MaterialComponents/private/Icons/ic_info + - MaterialComponents/private/Icons/ic_radio_button_unchecked + - MaterialComponents/private/Icons/ic_reorder - MaterialComponents/Typography - MaterialComponents/CollectionLayoutAttributes (4.0.1) - MaterialComponents/Collections (4.0.1): @@ -63,9 +69,27 @@ PODS: - MaterialComponents/private/Icons (4.0.1): - MaterialComponents/private/Icons/Base (= 4.0.1) - MaterialComponents/private/Icons/ic_arrow_back (= 4.0.1) + - MaterialComponents/private/Icons/ic_check (= 4.0.1) + - MaterialComponents/private/Icons/ic_check_circle (= 4.0.1) + - MaterialComponents/private/Icons/ic_chevron_right (= 4.0.1) + - MaterialComponents/private/Icons/ic_info (= 4.0.1) + - MaterialComponents/private/Icons/ic_radio_button_unchecked (= 4.0.1) + - MaterialComponents/private/Icons/ic_reorder (= 4.0.1) - MaterialComponents/private/Icons/Base (4.0.1) - MaterialComponents/private/Icons/ic_arrow_back (4.0.1): - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_check (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_check_circle (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_chevron_right (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_info (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_radio_button_unchecked (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_reorder (4.0.1): + - MaterialComponents/private/Icons/Base - MaterialComponents/private/ThumbTrack (4.0.1): - MaterialComponents/Ink - MaterialComponents/private/Color @@ -101,7 +125,7 @@ EXTERNAL SOURCES: :path: ../ SPEC CHECKSUMS: - MaterialComponents: e5e22c3f3bef3695355326d2c595c0d1e85d053f + MaterialComponents: 63514a0e6f6fb1d29604ded592b91e6b84238505 MaterialComponentsCatalog: 4769fcabbbdb4b40373eb428f329dbc4c2b53dac MaterialComponentsUnitTests: 8fa9520a8dae5ee0b7962ce449619b5be13445e6 diff --git a/components/CollectionCells/src/MDCCollectionViewCell.m b/components/CollectionCells/src/MDCCollectionViewCell.m index d9018772da1..f4fd9127757 100644 --- a/components/CollectionCells/src/MDCCollectionViewCell.m +++ b/components/CollectionCells/src/MDCCollectionViewCell.m @@ -21,13 +21,25 @@ #import "MDCCollectionViewCell.h" #import "MaterialCollectionLayoutAttributes.h" -#import "private/MDCCollectionCellResources.h" +#import "MaterialIcons+ic_check.h" +#import "MaterialIcons+ic_check_circle.h" +#import "MaterialIcons+ic_chevron_right.h" +#import "MaterialIcons+ic_info.h" +#import "MaterialIcons+ic_radio_button_unchecked.h" +#import "MaterialIcons+ic_reorder.h" + +#define RGBCOLOR(r, g, b) [UIColor colorWithRed:(r) / 255.0f green:(g) / 255.0f blue:(b) / 255.0f alpha:1] +#define HEXCOLOR(hex) RGBCOLOR((((hex) >> 16) & 0xFF), (((hex) >> 8) & 0xFF), ((hex)&0xFF)) static CGFloat kEditingControlAppearanceOffset = 16.0f; -// Default accesory insets. +// Default accessory insets. static const UIEdgeInsets kAccessoryInsetDefault = {0, 16.0f, 0, 16.0f}; +// Default editing icon colors. +static const uint32_t kCellGrayColor = 0x626262; +static const uint32_t kCellRedColor = 0xF44336; + @implementation MDCCollectionViewCell { MDCCollectionViewLayoutAttributes *_attr; BOOL _usesCellSeparatorHiddenOverride; @@ -150,17 +162,17 @@ - (void)setAccessoryType:(MDCCollectionViewCellAccessoryType)accessoryType { switch (_accessoryType) { case MDCCollectionViewCellAccessoryDisclosureIndicator: { - UIImage *image = MDCCollectionCellResources(imageForCellAccessoryChevronRight); - accessoryImageView.image = image; + accessoryImageView.image = + [UIImage imageWithContentsOfFile:[MDCIcons pathFor_ic_chevron_right]]; break; } case MDCCollectionViewCellAccessoryCheckmark: { - UIImage *image = MDCCollectionCellResources(imageForCellAccessoryCheck); + UIImage *image = [UIImage imageWithContentsOfFile:[MDCIcons pathFor_ic_check]]; accessoryImageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; break; } case MDCCollectionViewCellAccessoryDetailButton: { - UIImage *image = MDCCollectionCellResources(imageForCellAccessoryInfo); + UIImage *image = [UIImage imageWithContentsOfFile:[MDCIcons pathFor_ic_info]]; accessoryImageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; break; } @@ -253,8 +265,10 @@ - (void)updateInterfaceForEditing { // Create reorder editing controls. if (_attr.shouldShowReorderStateMask && !_editingReorderImageView) { - UIImage *reorderImage = MDCCollectionCellResources(imageForCellEditingReorder); + UIImage *reorderImage = [UIImage imageWithContentsOfFile:[MDCIcons pathFor_ic_reorder]]; + reorderImage = [reorderImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; _editingReorderImageView = [[UIImageView alloc] initWithImage:reorderImage]; + _editingReorderImageView.tintColor = HEXCOLOR(kCellGrayColor); _editingReorderImageView.alpha = 0.0f; _editingReorderImageView.frame = CGRectMake(0, @@ -266,8 +280,11 @@ - (void)updateInterfaceForEditing { // Create selector editing controls. if (_attr.shouldShowSelectorStateMask && !_editingSelectorImageView) { - UIImage *selectorImage = MDCCollectionCellResources(imageForCellEditingUnselected); + UIImage *selectorImage = + [UIImage imageWithContentsOfFile:[MDCIcons pathFor_ic_radio_button_unchecked]]; + selectorImage = [selectorImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; _editingSelectorImageView = [[UIImageView alloc] initWithImage:selectorImage]; + _editingSelectorImageView.tintColor = HEXCOLOR(kCellGrayColor); _editingSelectorImageView.alpha = 0.0f; _editingSelectorImageView.frame = CGRectMake(CGRectGetWidth(self.bounds) - selectorImage.size.width, @@ -291,9 +308,17 @@ - (void)updateInterfaceForEditing { - (void)setSelected:(BOOL)selected { [super setSelected:selected]; - _editingSelectorImageView.image = selected - ? MDCCollectionCellResources(imageForCellEditingSelected) - : MDCCollectionCellResources(imageForCellEditingUnselected); + if (selected) { + _editingSelectorImageView.image = + [UIImage imageWithContentsOfFile:[MDCIcons pathFor_ic_check_circle]]; + _editingSelectorImageView.tintColor = HEXCOLOR(kCellRedColor); + } else { + _editingSelectorImageView.image = + [UIImage imageWithContentsOfFile:[MDCIcons pathFor_ic_radio_button_unchecked]]; + _editingSelectorImageView.tintColor = HEXCOLOR(kCellGrayColor); + } + _editingSelectorImageView.image = + [_editingSelectorImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; } #pragma mark - Cell Appearance Animation diff --git a/components/CollectionCells/src/private/MDCCollectionCellResources.h b/components/CollectionCells/src/private/MDCCollectionCellResources.h deleted file mode 100644 index 452e8851c69..00000000000 --- a/components/CollectionCells/src/private/MDCCollectionCellResources.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - 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 - -/** - Shorthand for returning a resource of any type from MDCCollectionCellResources's singleton. - */ -#define MDCCollectionCellResources(sel) [[MDCCollectionCellResources sharedInstance] sel] - -/** Resources that are used for collection views. */ -@interface MDCCollectionCellResources : NSObject - -/** Returns the shared resources singleton instance. */ -+ (nonnull instancetype)sharedInstance; - -/** Returns cell accessory check image. */ -- (nonnull UIImage *)imageForCellAccessoryCheck; - -/** Returns cell accessory chevron right image. */ -- (nonnull UIImage *)imageForCellAccessoryChevronRight; - -/** Returns cell accessory info image. */ -- (nonnull UIImage *)imageForCellAccessoryInfo; - -/** Returns cell editing delete image. */ -- (nonnull UIImage *)imageForCellEditingDelete; - -/** Returns cell editing reorder image. */ -- (nonnull UIImage *)imageForCellEditingReorder; - -/** Returns cell editing selected image. */ -- (nonnull UIImage *)imageForCellEditingSelected; - -/** Returns cell editing unselected image. */ -- (nonnull UIImage *)imageForCellEditingUnselected; - -@end diff --git a/components/CollectionCells/src/private/MDCCollectionCellResources.m b/components/CollectionCells/src/private/MDCCollectionCellResources.m deleted file mode 100644 index f222264b929..00000000000 --- a/components/CollectionCells/src/private/MDCCollectionCellResources.m +++ /dev/null @@ -1,102 +0,0 @@ -/* - 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. - */ - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "MDCCollectionCellResources.h" - -// The Bundle for string and icon resources. -static NSString *const kBundleName = @"MaterialCollectionCells.bundle"; - -static NSString *const kCellCheckImageName = @"mdc_cell_check"; -static NSString *const kCellChevronRightImageName = @"mdc_cell_chevron_right"; -static NSString *const kCellInfoImageName = @"mdc_cell_info"; -static NSString *const kCellDeleteImageName = @"mdc_cell_delete"; -static NSString *const kCellReorderImageName = @"mdc_cell_reorder"; -static NSString *const kCellSelectedImageName = @"mdc_cell_selected"; -static NSString *const kCellUnselectedImageName = @"mdc_cell_unselected"; - -@implementation MDCCollectionCellResources - -+ (instancetype)sharedInstance { - static MDCCollectionCellResources *sharedInstance; - @synchronized(self) { - if (sharedInstance == nil) { - sharedInstance = [[MDCCollectionCellResources alloc] init]; - } - } - return sharedInstance; -} - -#pragma mark - Resource bundle - -+ (NSBundle *)bundle { - static NSBundle *bundle = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - bundle = [NSBundle bundleWithPath:[self bundlePathWithName:kBundleName]]; - }); - - return bundle; -} - -+ (NSString *)bundlePathWithName:(NSString *)bundleName { - // In iOS 8+, we could be included by way of a dynamic framework, and our resource bundles may - // not be in the main .app bundle, but rather in a nested framework, so figure out where we live - // and use that as the search location. - NSBundle *bundle = [NSBundle bundleForClass:[self class]]; - NSString *resourcePath = [(nil == bundle ? [NSBundle mainBundle] : bundle)resourcePath]; - return [resourcePath stringByAppendingPathComponent:bundleName]; -} - -- (UIImage *)loadImageWithName:(NSString *)imageName { - NSBundle *bundle = [[self class] bundle]; - return [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil]; -} - -#pragma mark - Images - -- (UIImage *)imageForCellAccessoryCheck { - return [self loadImageWithName:kCellCheckImageName]; -} - -- (UIImage *)imageForCellAccessoryChevronRight { - return [self loadImageWithName:kCellChevronRightImageName]; -} - -- (UIImage *)imageForCellAccessoryInfo { - return [self loadImageWithName:kCellInfoImageName]; -} - -- (UIImage *)imageForCellEditingDelete { - return [self loadImageWithName:kCellDeleteImageName]; -} - -- (UIImage *)imageForCellEditingReorder { - return [self loadImageWithName:kCellReorderImageName]; -} - -- (UIImage *)imageForCellEditingSelected { - return [self loadImageWithName:kCellSelectedImageName]; -} - -- (UIImage *)imageForCellEditingUnselected { - return [self loadImageWithName:kCellUnselectedImageName]; -} - -@end diff --git a/components/private/Icons/icons/ic_check/src/MaterialIcons+ic_check.h b/components/private/Icons/icons/ic_check/src/MaterialIcons+ic_check.h new file mode 100644 index 00000000000..1091fdd77a3 --- /dev/null +++ b/components/private/Icons/icons/ic_check/src/MaterialIcons+ic_check.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 "MaterialIcons.h" + +// This file was automatically generated by running scripts/sync_icons.sh +// Do not modify directly. + +@interface MDCIcons (ic_check) + +/** Returns the path for the ic_check image contained in MaterialIcons_ic_check.bundle. */ ++ (nonnull NSString *)pathFor_ic_check; + +@end diff --git a/components/private/Icons/icons/ic_check/src/MaterialIcons+ic_check.m b/components/private/Icons/icons/ic_check/src/MaterialIcons+ic_check.m new file mode 100644 index 00000000000..f218c6bab06 --- /dev/null +++ b/components/private/Icons/icons/ic_check/src/MaterialIcons+ic_check.m @@ -0,0 +1,33 @@ +/* + 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. + */ + +// This file was automatically generated by running scripts/sync_icons.sh +// Do not modify directly. + +#import "MaterialIcons+ic_check.h" + +#import "MDCIcons+BundleLoader.h" + +static NSString *const kBundleName = @"MaterialIcons_ic_check"; +static NSString *const kIconName = @"ic_check"; + +@implementation MDCIcons (ic_check) + ++ (nonnull NSString *)pathFor_ic_check { + return [self pathForIconName:kIconName withBundleName:kBundleName]; +} + +@end diff --git a/components/private/Icons/icons/ic_check/src/MaterialIcons_ic_check.bundle/ic_check.xcassets/ic_check.imageset/Contents.json b/components/private/Icons/icons/ic_check/src/MaterialIcons_ic_check.bundle/ic_check.xcassets/ic_check.imageset/Contents.json new file mode 100644 index 00000000000..44f0095d9ac --- /dev/null +++ b/components/private/Icons/icons/ic_check/src/MaterialIcons_ic_check.bundle/ic_check.xcassets/ic_check.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images": [ + { + "filename": "ic_check.png", + "idiom": "universal", + "scale": "1x" + }, + { + "filename": "ic_check@2x.png", + "idiom": "universal", + "scale": "2x" + }, + { + "filename": "ic_check@3x.png", + "idiom": "universal", + "scale": "3x" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/components/private/Icons/icons/ic_check/src/MaterialIcons_ic_check.bundle/ic_check.xcassets/ic_check.imageset/ic_check.png b/components/private/Icons/icons/ic_check/src/MaterialIcons_ic_check.bundle/ic_check.xcassets/ic_check.imageset/ic_check.png new file mode 100644 index 0000000000000000000000000000000000000000..1c14c9c44592e95983dec13ca705ab99a6c54f21 GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1cTX3`kP61+1H5jHdYPvdOa3!3 znXv4Sz4PG@!RHQbn4&mG+#ttr57iP(&yMOzaz berIh-Q-qr4!cP|^dng9R*07*qoM6N<$ Eg0vTEBme*a literal 0 HcmV?d00001 diff --git a/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h b/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h new file mode 100644 index 00000000000..729f8f35152 --- /dev/null +++ b/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.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 "MaterialIcons.h" + +// This file was automatically generated by running scripts/sync_icons.sh +// Do not modify directly. + +@interface MDCIcons (ic_check_circle) + +/** Returns the path for the ic_check_circle image contained in MaterialIcons_ic_check_circle.bundle. */ ++ (nonnull NSString *)pathFor_ic_check_circle; + +@end diff --git a/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m b/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m new file mode 100644 index 00000000000..153fb97050e --- /dev/null +++ b/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m @@ -0,0 +1,33 @@ +/* + 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. + */ + +// This file was automatically generated by running scripts/sync_icons.sh +// Do not modify directly. + +#import "MaterialIcons+ic_check_circle.h" + +#import "MDCIcons+BundleLoader.h" + +static NSString *const kBundleName = @"MaterialIcons_ic_check_circle"; +static NSString *const kIconName = @"ic_check_circle"; + +@implementation MDCIcons (ic_check_circle) + ++ (nonnull NSString *)pathFor_ic_check_circle { + return [self pathForIconName:kIconName withBundleName:kBundleName]; +} + +@end diff --git a/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/Contents.json b/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/Contents.json new file mode 100644 index 00000000000..de6673864e8 --- /dev/null +++ b/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images": [ + { + "filename": "ic_check_circle.png", + "idiom": "universal", + "scale": "1x" + }, + { + "filename": "ic_check_circle@2x.png", + "idiom": "universal", + "scale": "2x" + }, + { + "filename": "ic_check_circle@3x.png", + "idiom": "universal", + "scale": "3x" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle.png b/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..a2caa18db2b2f00f890d119183e78093314e7a64 GIT binary patch literal 260 zcmV+f0sH=mP)ayb&5+IG8{X5M=Wfq|%hM?=*;Bc- zRdy^W5Hsb1x}BNelK4Qu)+BmN4-)z_vHXh=V+;zHQ2*qG72-s;V0l;PKM->Z_K$VM z)`DtpLtPv#xFN)A6MjNnT>D@JP5Sn93+$35U#>0=7VOX<@pQ4VV1WjSr;C{dh4Ucs z^hyR}f*x!{$NH}RP7f(gcq8F5=Hz@I5kqFqmLu1;0~=;!p3EOL#1#p?D_&0k0000< KMNUMnLSTXkp==BQ literal 0 HcmV?d00001 diff --git a/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@2x.png b/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..86bf38e98fd6cd4917a9a05f1627b4cd00bc95d4 GIT binary patch literal 493 zcmVM}5-5a(K1E3IIb76@&*2A@3Ka?2H4rk8_6a0`E`;ve`T*h!RCH0s zX}8X7{@uyIb^g5{ZiJlQMmOg$&b_i1ybw{R$1^iha&l5;JkjNvh+y7w%MvNsf*UUI z<_(YJh(0o6PVu0{AH;GinmBRD7sPQfRV;bMH^gIOYFKcn>-B`qQ+!4O_J9s8ByjKa zgZbA>`kmqCbHWLwJbLP11O3hS`|?|xd+RAA)||7I+aP`M8HSh;v2@n|*s2%iR4}Lq z>x;X>ptnQ4(81t&hk9Xz!3?3ek4zN(k*XJ_7^FyH?|~yGo_Zm{pm+y90GJ7Dy^!gF zK9nSWYI;48B6`9>FC_X)HWP~8_9Z_dYQj#h9MdkMCS3GF8-r^^O}OZVOAI0wdcr~f ziwc;eA(g)CGAuU6=+00000NkvXXu0mjf+)>#> literal 0 HcmV?d00001 diff --git a/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@3x.png b/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..1c2ddcd5f3474304cb73cf39be7d6f5deff012b9 GIT binary patch literal 709 zcmV;$0y_PPP)E9`6SZ1GRcK*-cVL!j)!@?JaXJ)yvnwpx@j4@A+V@j0q@hMZ{m>lzr z(oTXh%uv8b>a4;6)7-(iA-1_jELUZVEY1vbh%~SQgQ)A_3ld}Nbff4QS4hlVGJ%R# zJ|Ie4CPhr0wa@A~C3<;|1ojb2!%lJlULb*Gv2y4r2Vfy^vX6x!hdBU?fzttuww>m{ zQs8_OgACUR<_J|h>Y8z#Q?n`RDkPjE>`_M=Kj9qVH*JI(bD0ym5kk$Ae2y?js2mcq zD`z>vDxr=;e)5us{B)Ef91*I7P`d>H9&qj?M<^1ijMVHE|MYS0AV;_$RPCi-v41w>Gw*DX)$G zB>wQ9HI3uaR+mZZRJ%%aFAC^R$&jdcYSOcVO<@T~4->ouBorxH~AC;R6%DZ24h(Pqs3k;S8RxelF{r5}E*KA2A~U literal 0 HcmV?d00001 diff --git a/components/private/Icons/icons/ic_chevron_right/src/MaterialIcons_ic_chevron_right.bundle/ic_chevron_right.xcassets/ic_chevron_right.imageset/ic_chevron_right@3x.png b/components/private/Icons/icons/ic_chevron_right/src/MaterialIcons_ic_chevron_right.bundle/ic_chevron_right.xcassets/ic_chevron_right.imageset/ic_chevron_right@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f97c51b8ed7f4716341898f25e522f49336c7ec7 GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhaw`aNA7Ln;{G-Z;z2WFX-3aHRt4 zkI50QR)zi&f2%TS@^&4LLk;g&Kagdzb^)qBaK6RKtxrnI&U5+9$CFFuY1(Pg~FoNpT>L!PC{xWt~$(69CpdO(*~W literal 0 HcmV?d00001 diff --git a/components/private/Icons/icons/ic_info/src/MaterialIcons+ic_info.h b/components/private/Icons/icons/ic_info/src/MaterialIcons+ic_info.h new file mode 100644 index 00000000000..9a9157565bd --- /dev/null +++ b/components/private/Icons/icons/ic_info/src/MaterialIcons+ic_info.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 "MaterialIcons.h" + +// This file was automatically generated by running scripts/sync_icons.sh +// Do not modify directly. + +@interface MDCIcons (ic_info) + +/** Returns the path for the ic_info image contained in MaterialIcons_ic_info.bundle. */ ++ (nonnull NSString *)pathFor_ic_info; + +@end diff --git a/components/private/Icons/icons/ic_info/src/MaterialIcons+ic_info.m b/components/private/Icons/icons/ic_info/src/MaterialIcons+ic_info.m new file mode 100644 index 00000000000..3712e75fceb --- /dev/null +++ b/components/private/Icons/icons/ic_info/src/MaterialIcons+ic_info.m @@ -0,0 +1,33 @@ +/* + 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. + */ + +// This file was automatically generated by running scripts/sync_icons.sh +// Do not modify directly. + +#import "MaterialIcons+ic_info.h" + +#import "MDCIcons+BundleLoader.h" + +static NSString *const kBundleName = @"MaterialIcons_ic_info"; +static NSString *const kIconName = @"ic_info"; + +@implementation MDCIcons (ic_info) + ++ (nonnull NSString *)pathFor_ic_info { + return [self pathForIconName:kIconName withBundleName:kBundleName]; +} + +@end diff --git a/components/private/Icons/icons/ic_info/src/MaterialIcons_ic_info.bundle/ic_info.xcassets/ic_info.imageset/Contents.json b/components/private/Icons/icons/ic_info/src/MaterialIcons_ic_info.bundle/ic_info.xcassets/ic_info.imageset/Contents.json new file mode 100644 index 00000000000..710d76aafc4 --- /dev/null +++ b/components/private/Icons/icons/ic_info/src/MaterialIcons_ic_info.bundle/ic_info.xcassets/ic_info.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images": [ + { + "filename": "ic_info.png", + "idiom": "universal", + "scale": "1x" + }, + { + "filename": "ic_info@2x.png", + "idiom": "universal", + "scale": "2x" + }, + { + "filename": "ic_info@3x.png", + "idiom": "universal", + "scale": "3x" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/components/private/Icons/icons/ic_info/src/MaterialIcons_ic_info.bundle/ic_info.xcassets/ic_info.imageset/ic_info.png b/components/private/Icons/icons/ic_info/src/MaterialIcons_ic_info.bundle/ic_info.xcassets/ic_info.imageset/ic_info.png new file mode 100644 index 0000000000000000000000000000000000000000..5ef3dc0809e2030ac573605e4d50a1b15be283f3 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_+irJgR1Ar*{!&pBo_7znsMbQe4M zmu2aqie+Lg_Z%-OYdThQ-1WK3yF`&wq~^cgljetCWtg?+{hpb=@c6_R&GITYJe^Gr zn+wS+w{7@Qu3Fze@l2LUOog?>hoS^g9uF~&xlN*)H`W{wZ;U+{TXN|u|AX!W%Mvu1 zCO7Tl*uo`~E>@u<#XBj|^}>ul|6C>19PJEUR$ER;nAhbpt#9Vs*Ef&3bU%Gs$9S4s WW8+=f3r;`>GkCiCxvXk7>D5($61se024W16Y9PdLry5vdn5qWG7(8}rf#0;i^i3`Bz)3@LmB1SV3Ir;F zXA1R55y$YDX|Txx7F9T)MSz5C z%me$p#<(@Ec}8Mx!WA`)S?3W+u@6bG_Q+CN{iWZ6iFpgn8im7?Un+GeL)QF6AA4ghWDNXmU%KBG@?RmKM)K1 zMojBSqWw=2twt=5)`ISdVGXesBvgrXZ8hkU2o;{K22Jn?b%0n667~qyLM-o&5Jdv( zL4TP?X$x_%4U`&Af{svXIte;OspBN5jZz=6{@DvXl!i`%1}Ftig2q!9G`&wY{XgoC zlb|+AO(#JoC^eh}9ip`9BT! zt3hvwp=B+|Cze%9K`*Qj(>Y>cXT-L|$au~Wj|A$*gSJWNv(cQ7Bv#Y(JK#w*j9P*Yqa=F};`Uf%+Z6V(b RSUCUy002ovPDHLkV1jt6`k4R# literal 0 HcmV?d00001 diff --git a/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons+ic_radio_button_unchecked.h b/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons+ic_radio_button_unchecked.h new file mode 100644 index 00000000000..efd5bc51521 --- /dev/null +++ b/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons+ic_radio_button_unchecked.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 "MaterialIcons.h" + +// This file was automatically generated by running scripts/sync_icons.sh +// Do not modify directly. + +@interface MDCIcons (ic_radio_button_unchecked) + +/** Returns the path for the ic_radio_button_unchecked image contained in MaterialIcons_ic_radio_button_unchecked.bundle. */ ++ (nonnull NSString *)pathFor_ic_radio_button_unchecked; + +@end diff --git a/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons+ic_radio_button_unchecked.m b/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons+ic_radio_button_unchecked.m new file mode 100644 index 00000000000..57cadcbc4a0 --- /dev/null +++ b/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons+ic_radio_button_unchecked.m @@ -0,0 +1,33 @@ +/* + 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. + */ + +// This file was automatically generated by running scripts/sync_icons.sh +// Do not modify directly. + +#import "MaterialIcons+ic_radio_button_unchecked.h" + +#import "MDCIcons+BundleLoader.h" + +static NSString *const kBundleName = @"MaterialIcons_ic_radio_button_unchecked"; +static NSString *const kIconName = @"ic_radio_button_unchecked"; + +@implementation MDCIcons (ic_radio_button_unchecked) + ++ (nonnull NSString *)pathFor_ic_radio_button_unchecked { + return [self pathForIconName:kIconName withBundleName:kBundleName]; +} + +@end diff --git a/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/Contents.json b/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/Contents.json new file mode 100644 index 00000000000..1da06359b98 --- /dev/null +++ b/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images": [ + { + "filename": "ic_radio_button_unchecked.png", + "idiom": "universal", + "scale": "1x" + }, + { + "filename": "ic_radio_button_unchecked@2x.png", + "idiom": "universal", + "scale": "2x" + }, + { + "filename": "ic_radio_button_unchecked@3x.png", + "idiom": "universal", + "scale": "3x" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked.png b/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked.png new file mode 100644 index 0000000000000000000000000000000000000000..4c0688f03a10206bdc503f90176172fef5b669db GIT binary patch literal 307 zcmV-30nGl1P)6WZz3WhB05KU)Xv>|wY2o&&Y9Xj`v#hYaGNQmkre;{002ovPDHLk FV1g7GgZuyh literal 0 HcmV?d00001 diff --git a/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked@2x.png b/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..daa867f1375f9a76e2bea9f19ce546148301e849 GIT binary patch literal 621 zcmV-z0+RiSP)5jiVeB6 zrh;NG#tp*)-D3T{jk|a6u6xpS7tEPfQ zKxsbWXnbLoltFajMGF1g^w96{lqXq z*&OP*SN9kG)W3e1Fl>E`3$l3HmZCq55!SeVi!INs)wNo{#FDPwQ|`2(7t_K-LdUd@ z8*%;3nZ;C5I-C;2Wm1yANMS0&rF5CVWmQr;M6oAbR&c3Fsz?+i=~Bg|A*no36r@WX zm$sy`L}5#p7A~>_B#B~Bcj3Q3Ku478&xrc}k^6s5%aYn9ihb#_?EP{b(wNHd+k4P) z&Lla9sd?#eLJZvANcxBbCX#%U4r|1zE41_&UH zJVM^1NXBEW=8=mj?NbC~A%l{*WRDUBYzA+_ra*~(WNURe1B5co1x~;?X9@#Zw&?jb z@3P4d#-6dxsb}*O))*nOI1{W;rA~_;JzCVMvP_;B55S{;0?xuy#KlHA00000NkvXX Hu0mjfuURFA literal 0 HcmV?d00001 diff --git a/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked@3x.png b/components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/ic_radio_button_unchecked.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..d8c86365f9d8b5cbc8b92ae61b4fe854479dc99f GIT binary patch literal 915 zcmV;E18n?>P)gZm5+%rTxLDO%tt-tFNg zlA&34SUzONmzq8Ur|aZVdWfeA-WpXiwc13_;^8zMV(V=x0X{jIVM5-y&8K zZH^sOjrD7c5GITjuHyc~8*~vuflc>cdI8Zc63S&ndqNYDF#`t(9|2iw-D_#i3SmEjc_{n z2hqMJQNbAegM@O}b^U;6h&Dwi(}*@tQ>PGZlL8?`)ZDj;|f0ff}l~Of;&bK>qxnbPscota3bZuyV_k!q}9zFV)P>)EYBmv z=oR}@PS$J0=pnmEp~MTsDCRs7?x4s88i>&!M5I*FBWbKMK>-&L-?N6;$DvFHB_1Po zY;u`HbdaQj!%R~{?085-MtLqF+qTY4#u=uMGM(=$)5kF5+|u>S?gw_^#RywS#%i3v zqbk24se8wDDtI@*QzS+6^b<@!b2JdgV(xK-;3|yqggVlsx6Ct2nUAQ8kch)?FB=LnFmNz${AJE7 yXCby@2~%AbQ%$h9@C^13%#42=80-ZY^s?9ex4rLXcYi8SAA_f>pUXO@geCw|yc$0M literal 0 HcmV?d00001 diff --git a/components/private/Icons/icons/ic_reorder/src/MaterialIcons_ic_reorder.bundle/ic_reorder.xcassets/ic_reorder.imageset/ic_reorder@3x.png b/components/private/Icons/icons/ic_reorder/src/MaterialIcons_ic_reorder.bundle/ic_reorder.xcassets/ic_reorder.imageset/ic_reorder@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..0a66529bfeac1f43a44606a11b23344a7864c141 GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xf3?%cF6R(1{pgYCW9j{U8QwIH3Iu6{1-oD!M< D>%<$U literal 0 HcmV?d00001 diff --git a/demos/Pesto/Podfile.lock b/demos/Pesto/Podfile.lock index 2ac40b909f9..ce21cb30a90 100644 --- a/demos/Pesto/Podfile.lock +++ b/demos/Pesto/Podfile.lock @@ -38,6 +38,12 @@ PODS: - MaterialComponents/CollectionCells (4.0.1): - MaterialComponents/CollectionLayoutAttributes - MaterialComponents/Ink + - MaterialComponents/private/Icons/ic_check + - MaterialComponents/private/Icons/ic_check_circle + - MaterialComponents/private/Icons/ic_chevron_right + - MaterialComponents/private/Icons/ic_info + - MaterialComponents/private/Icons/ic_radio_button_unchecked + - MaterialComponents/private/Icons/ic_reorder - MaterialComponents/Typography - MaterialComponents/CollectionLayoutAttributes (4.0.1) - MaterialComponents/Collections (4.0.1): @@ -63,9 +69,27 @@ PODS: - MaterialComponents/private/Icons (4.0.1): - MaterialComponents/private/Icons/Base (= 4.0.1) - MaterialComponents/private/Icons/ic_arrow_back (= 4.0.1) + - MaterialComponents/private/Icons/ic_check (= 4.0.1) + - MaterialComponents/private/Icons/ic_check_circle (= 4.0.1) + - MaterialComponents/private/Icons/ic_chevron_right (= 4.0.1) + - MaterialComponents/private/Icons/ic_info (= 4.0.1) + - MaterialComponents/private/Icons/ic_radio_button_unchecked (= 4.0.1) + - MaterialComponents/private/Icons/ic_reorder (= 4.0.1) - MaterialComponents/private/Icons/Base (4.0.1) - MaterialComponents/private/Icons/ic_arrow_back (4.0.1): - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_check (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_check_circle (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_chevron_right (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_info (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_radio_button_unchecked (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_reorder (4.0.1): + - MaterialComponents/private/Icons/Base - MaterialComponents/private/ThumbTrack (4.0.1): - MaterialComponents/Ink - MaterialComponents/private/Color @@ -91,6 +115,6 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - MaterialComponents: e5e22c3f3bef3695355326d2c595c0d1e85d053f + MaterialComponents: 63514a0e6f6fb1d29604ded592b91e6b84238505 COCOAPODS: 0.39.0 diff --git a/demos/Shrine/Podfile.lock b/demos/Shrine/Podfile.lock index 2ac40b909f9..ce21cb30a90 100644 --- a/demos/Shrine/Podfile.lock +++ b/demos/Shrine/Podfile.lock @@ -38,6 +38,12 @@ PODS: - MaterialComponents/CollectionCells (4.0.1): - MaterialComponents/CollectionLayoutAttributes - MaterialComponents/Ink + - MaterialComponents/private/Icons/ic_check + - MaterialComponents/private/Icons/ic_check_circle + - MaterialComponents/private/Icons/ic_chevron_right + - MaterialComponents/private/Icons/ic_info + - MaterialComponents/private/Icons/ic_radio_button_unchecked + - MaterialComponents/private/Icons/ic_reorder - MaterialComponents/Typography - MaterialComponents/CollectionLayoutAttributes (4.0.1) - MaterialComponents/Collections (4.0.1): @@ -63,9 +69,27 @@ PODS: - MaterialComponents/private/Icons (4.0.1): - MaterialComponents/private/Icons/Base (= 4.0.1) - MaterialComponents/private/Icons/ic_arrow_back (= 4.0.1) + - MaterialComponents/private/Icons/ic_check (= 4.0.1) + - MaterialComponents/private/Icons/ic_check_circle (= 4.0.1) + - MaterialComponents/private/Icons/ic_chevron_right (= 4.0.1) + - MaterialComponents/private/Icons/ic_info (= 4.0.1) + - MaterialComponents/private/Icons/ic_radio_button_unchecked (= 4.0.1) + - MaterialComponents/private/Icons/ic_reorder (= 4.0.1) - MaterialComponents/private/Icons/Base (4.0.1) - MaterialComponents/private/Icons/ic_arrow_back (4.0.1): - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_check (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_check_circle (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_chevron_right (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_info (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_radio_button_unchecked (4.0.1): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_reorder (4.0.1): + - MaterialComponents/private/Icons/Base - MaterialComponents/private/ThumbTrack (4.0.1): - MaterialComponents/Ink - MaterialComponents/private/Color @@ -91,6 +115,6 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - MaterialComponents: e5e22c3f3bef3695355326d2c595c0d1e85d053f + MaterialComponents: 63514a0e6f6fb1d29604ded592b91e6b84238505 COCOAPODS: 0.39.0 diff --git a/scripts/generated/icons.rb b/scripts/generated/icons.rb index c8da3333196..c91a2fcc657 100644 --- a/scripts/generated/icons.rb +++ b/scripts/generated/icons.rb @@ -21,5 +21,83 @@ def registerIcons(s) } ss.dependency "#{Pathname.new(ss.name).dirname}/Base" end + + iss.subspec "ic_check" do |ss| + ss.public_header_files = "components/private/Icons/icons/ic_check/src/*.h" + ss.source_files = "components/private/Icons/icons/ic_check/src/*.{h,m}" + ss.header_mappings_dir = "components/private/Icons/icons/ic_check/src/*" + ss.resource_bundles = { + "MaterialIcons_ic_check" => [ + "components/private/Icons/icons/ic_check/src/MaterialIcons_ic_check.bundle/**/*.png", + "components/private/Icons/icons/ic_check/src/MaterialIcons_ic_check.bundle/*.xcassets" + ] + } + ss.dependency "#{Pathname.new(ss.name).dirname}/Base" + end + + iss.subspec "ic_check_circle" do |ss| + ss.public_header_files = "components/private/Icons/icons/ic_check_circle/src/*.h" + ss.source_files = "components/private/Icons/icons/ic_check_circle/src/*.{h,m}" + ss.header_mappings_dir = "components/private/Icons/icons/ic_check_circle/src/*" + ss.resource_bundles = { + "MaterialIcons_ic_check_circle" => [ + "components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/**/*.png", + "components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.bundle/*.xcassets" + ] + } + ss.dependency "#{Pathname.new(ss.name).dirname}/Base" + end + + iss.subspec "ic_chevron_right" do |ss| + ss.public_header_files = "components/private/Icons/icons/ic_chevron_right/src/*.h" + ss.source_files = "components/private/Icons/icons/ic_chevron_right/src/*.{h,m}" + ss.header_mappings_dir = "components/private/Icons/icons/ic_chevron_right/src/*" + ss.resource_bundles = { + "MaterialIcons_ic_chevron_right" => [ + "components/private/Icons/icons/ic_chevron_right/src/MaterialIcons_ic_chevron_right.bundle/**/*.png", + "components/private/Icons/icons/ic_chevron_right/src/MaterialIcons_ic_chevron_right.bundle/*.xcassets" + ] + } + ss.dependency "#{Pathname.new(ss.name).dirname}/Base" + end + + iss.subspec "ic_info" do |ss| + ss.public_header_files = "components/private/Icons/icons/ic_info/src/*.h" + ss.source_files = "components/private/Icons/icons/ic_info/src/*.{h,m}" + ss.header_mappings_dir = "components/private/Icons/icons/ic_info/src/*" + ss.resource_bundles = { + "MaterialIcons_ic_info" => [ + "components/private/Icons/icons/ic_info/src/MaterialIcons_ic_info.bundle/**/*.png", + "components/private/Icons/icons/ic_info/src/MaterialIcons_ic_info.bundle/*.xcassets" + ] + } + ss.dependency "#{Pathname.new(ss.name).dirname}/Base" + end + + iss.subspec "ic_radio_button_unchecked" do |ss| + ss.public_header_files = "components/private/Icons/icons/ic_radio_button_unchecked/src/*.h" + ss.source_files = "components/private/Icons/icons/ic_radio_button_unchecked/src/*.{h,m}" + ss.header_mappings_dir = "components/private/Icons/icons/ic_radio_button_unchecked/src/*" + ss.resource_bundles = { + "MaterialIcons_ic_radio_button_unchecked" => [ + "components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/**/*.png", + "components/private/Icons/icons/ic_radio_button_unchecked/src/MaterialIcons_ic_radio_button_unchecked.bundle/*.xcassets" + ] + } + ss.dependency "#{Pathname.new(ss.name).dirname}/Base" + end + + iss.subspec "ic_reorder" do |ss| + ss.public_header_files = "components/private/Icons/icons/ic_reorder/src/*.h" + ss.source_files = "components/private/Icons/icons/ic_reorder/src/*.{h,m}" + ss.header_mappings_dir = "components/private/Icons/icons/ic_reorder/src/*" + ss.resource_bundles = { + "MaterialIcons_ic_reorder" => [ + "components/private/Icons/icons/ic_reorder/src/MaterialIcons_ic_reorder.bundle/**/*.png", + "components/private/Icons/icons/ic_reorder/src/MaterialIcons_ic_reorder.bundle/*.xcassets" + ] + } + ss.dependency "#{Pathname.new(ss.name).dirname}/Base" + end end end From cbaa1e9f2e3a24d437477692b1a66d7eca8b049a Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Tue, 26 Apr 2016 13:43:16 -0400 Subject: [PATCH 127/129] Revert "[FlexibleHeader] Resolve iOS 8.4 unit test failure of issue176 tests." Summary: This reverts commit fe107a900cde37fd4f4321d8cf333a20e040669f and 271572706e98e93edde01a30a14daf129f639306. This change is causing regressions in our April 25, 2016 release-candidate. Rolling back the changes until we can find a better path forward. Reviewers: #mdc_ios_owners, ajsecord Reviewed By: #mdc_ios_owners, ajsecord Projects: #material_components_ios Differential Revision: http://codereview.cc/D762 --- .../FlexibleHeader/src/MDCFlexibleHeaderView.m | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m index ff24508f9f3..1b8430b101e 100644 --- a/components/FlexibleHeader/src/MDCFlexibleHeaderView.m +++ b/components/FlexibleHeader/src/MDCFlexibleHeaderView.m @@ -336,29 +336,11 @@ - (MDCFlexibleHeaderScrollViewInfo *)fhv_addInsetsToScrollView:(UIScrollView *)s } if (!info.hasInjectedTopContentInset) { - _contentInsetsAreChanging = YES; UIEdgeInsets insets = scrollView.contentInset; insets.top += _maximumHeight; info.injectedTopContentInset = _maximumHeight; info.hasInjectedTopContentInset = YES; - CGPoint oldContentOffset = scrollView.contentOffset; scrollView.contentInset = insets; - - // The private API -[UIScrollView(UIScrollViewInternal) _adjustContentOffsetIfNecessary] - // will automatically adjust the contentOffset when adjusting contentInset. Exactly *when* this - // happens is not documented. Some OS versions appear to have different behaviors. - // Notably: iOS 8.4 does not appear to change the contentOffset when first initializing the - // scroll view. - // - // The logic below is intentionally conservative because we want to lean on - // _adjustContentOffsetIfNecessary doing the right thing most of the time. - - if (oldContentOffset.y == 0 && CGPointEqualToPoint(oldContentOffset, scrollView.contentOffset)) { - CGPoint contentOffset = scrollView.contentOffset; - contentOffset.y = -_maximumHeight; - scrollView.contentOffset = contentOffset; - } - _contentInsetsAreChanging = NO; } // The scroll indicator insets are updated by fhv_accumulatorDidChange and change dynamically with From a02eea231fe2b5d6cff7ca8e92471c0d7a175ea5 Mon Sep 17 00:00:00 2001 From: Ian Gordon Date: Tue, 26 Apr 2016 16:24:05 -0400 Subject: [PATCH 128/129] Added API diff to CHANGELOG.md. --- CHANGELOG.md | 288 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3c53c9004e..d8178ec57d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,291 @@ +# 5.0.0 + +## API diffs + +Auto-generated by running: + + scripts/api_diff -o 55afa3aaef67799bdb8a94881f31c5c3b242e9a6 -n fe1ac2f14b7ad4179c84b01590df9c93289f2e36 + +### CollectionCells + +**New component.** + +### CollectionLayoutAttributes + +**New component.** + +### Collections + +**New component.** + +### FlexibleHeader + +- [new] [`MDCFlexibleHeaderView.statusBarHintCanOverlapHeader`](https://github.com/google/material-components-ios/blob/fe1ac2f14b7ad4179c84b01590df9c93289f2e36/components/FlexibleHeader/src/MDCFlexibleHeaderView.h#L344) + +### PageControl + +- [protocols changed] [`MDCPageControl`](https://github.com/google/material-components-ios/blob/fe1ac2f14b7ad4179c84b01590df9c93289f2e36/components/PageControl/src/MDCPageControl.h#L41). +Added *UIScrollViewDelegate*. + +## Component changes + +### AppBar + +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Remove mention of deprecated API.](https://github.com/google/material-components-ios/commit/1b35fe48b4f411c049689b398711682513b954d1) (Louis Romero) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Remove the internal MDCAppBarContainerViewController contentViewController setter.](https://github.com/google/material-components-ios/commit/8100c088133fd5799eb9f692ab76f463acaab715) (Jeff Verkoeyen) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Revert replace + with _ in icon names](https://github.com/google/material-components-ios/commit/552d66f3fb2f258446f1b41951457ee494e7bc06) (Junius Gunaratne) +* [Typical Use Example moving logic from init into viewDidLoad](https://github.com/google/material-components-ios/commit/0dc8870271222083a2a4dbbf55dd249a5b5cd4f5) (randallli) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[AppBar]! NSLog warning to NSAssert for incorrect parentViewController behavior.](https://github.com/google/material-components-ios/commit/8f3c3f8607ff295b8594ea1edbe8dadb244eaaaa) (randallli) +* [[AppBar]? Added NSLog to ensure that addChildViewController: is called before addSubviewsToParent](https://github.com/google/material-components-ios/commit/c7a3891fe9754962cbbb14157cd5595ca5b0abec) (randallli) +* [[Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic.](https://github.com/google/material-components-ios/commit/2ab08f41337d1ef1392bec46f534fe95fb2ac79e) (Will Larche) +* [[Catalog] Example view controllers must implement init.](https://github.com/google/material-components-ios/commit/ea405734e97bf96bff2d97f46b28322a0aa2b753) (Jeff Verkoeyen) +* [[Catalog] Fixing Swift example view controller initializers.](https://github.com/google/material-components-ios/commit/8e733e163a4b308186345e03d90056e4040c693b) (Jeff Verkoeyen) +* [[Catalog] Make example titles consistent, use Component Name](https://github.com/google/material-components-ios/commit/ebd1fbd14c6d2b0c052e28a9bc3cf59a6e01e75e) (Junius Gunaratne) +* [[Catalog] Update AppBar demo design, table view should not have text](https://github.com/google/material-components-ios/commit/a147d7b359d1d99fca81aa97594d93263a3cedee) (Junius Gunaratne) +* [[Icons] Replace + with _ in icon names](https://github.com/google/material-components-ios/commit/c0bd1de8e5520102f59e9b92d8a1085b285be38e) (Junius Gunaratne) + +### ButtonBar + +* [Check UIBarButtonItem global appearance configuration when creating the buttons.](https://github.com/google/material-components-ios/commit/e3a56fe9624ab978850665ab4f06479ebe5e13bf) (Jeff Verkoeyen) +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Update tests to reflect that titleTextAttributes appearance only works on iOS 9.](https://github.com/google/material-components-ios/commit/916866a5259d3972ff359f0d57bce67bdb982ed2) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic.](https://github.com/google/material-components-ios/commit/2ab08f41337d1ef1392bec46f534fe95fb2ac79e) (Will Larche) +* [[Catalog] Example view controllers must implement init.](https://github.com/google/material-components-ios/commit/ea405734e97bf96bff2d97f46b28322a0aa2b753) (Jeff Verkoeyen) +* [[Catalog] Fixing Swift example view controller initializers.](https://github.com/google/material-components-ios/commit/8e733e163a4b308186345e03d90056e4040c693b) (Jeff Verkoeyen) +* [[Catalog] Make example titles consistent, use Component Name](https://github.com/google/material-components-ios/commit/ebd1fbd14c6d2b0c052e28a9bc3cf59a6e01e75e) (Junius Gunaratne) + +### Buttons + +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Resolve deprecation warnings.](https://github.com/google/material-components-ios/commit/13bb72bed147c249d9db1e8a31e3f7d9a62ecc46) (Jeff Verkoeyen) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic.](https://github.com/google/material-components-ios/commit/2ab08f41337d1ef1392bec46f534fe95fb2ac79e) (Will Larche) +* [[Catalog] Adjust layout for button example in landscape mode, move layout to supplemental](https://github.com/google/material-components-ios/commit/28e1904f7884de7c18ff646b722678a044497506) (Junius Gunaratne) +* [[Catalog] Fixing Swift example view controller initializers.](https://github.com/google/material-components-ios/commit/8e733e163a4b308186345e03d90056e4040c693b) (Jeff Verkoeyen) + +### CollectionCells + +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Rename all images with @2x/@3x.](https://github.com/google/material-components-ios/commit/2df5344ee2a5a35d2e2c6d7d33ac79ecbdc1afba) (Louis Romero) +* [Update README to indicate its present state.](https://github.com/google/material-components-ios/commit/a48e31fb2e9aa814198ad3c510b9e653a0f91e72) (Jeff Verkoeyen) +* [Updates cells to depend on MDCIcons for editing and accessory icons.](https://github.com/google/material-components-ios/commit/7598638db3ecd26e09a82988857a86806bc1dfe5) (Chris Cox) +* [[Collections] Merge Collections, CollectionCells, and CollectionLayoutAttributes components.](https://github.com/google/material-components-ios/commit/f15f6d5db6bcb0ddc6a750683daa649775f1e049) (Chris Cox) +* [[Collections] Replace EditingManager with an Editing protocol.](https://github.com/google/material-components-ios/commit/60ffaa55909fd931d976ecbd55f91b5d71394ce4) (Jeff Verkoeyen) +* [[Collections] Replace StyleManager with a Styling protocol.](https://github.com/google/material-components-ios/commit/8b925d001c8de48e226de4c83556ad5c440d9301) (Jeff Verkoeyen) +* [[Collections] Updates readmes.](https://github.com/google/material-components-ios/commit/a858523cb27c6b7f891d5cfec4722172deb71b5a) (Chris Cox) + +### CollectionLayoutAttributes + +* [Added unit tests for MDCCollectionLayoutAttributes.](https://github.com/google/material-components-ios/commit/142bd2fb7e8cb846fdb449d58873fd5f75419bd1) (Adrian Secord) +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Removes broken image.](https://github.com/google/material-components-ios/commit/ee3daf2fe52d44c8e552f40ffdd039f6937f896b) (Chris Cox) +* [[Collections] Merge Collections, CollectionCells, and CollectionLayoutAttributes components.](https://github.com/google/material-components-ios/commit/f15f6d5db6bcb0ddc6a750683daa649775f1e049) (Chris Cox) +* [[Collections] Updates readmes.](https://github.com/google/material-components-ios/commit/a858523cb27c6b7f891d5cfec4722172deb71b5a) (Chris Cox) + +### Collections + +* [Adds swift example.](https://github.com/google/material-components-ios/commit/e88dc4ff3fffd72b29b6b98d2479b4c4334165a4) (Chris Cox) +* [Cells divider is 1 pixel.](https://github.com/google/material-components-ios/commit/3ef520462a07f8b015e96b3f5497ec11ff6caf28) (Louis Romero) +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Merge Collections, CollectionCells, and CollectionLayoutAttributes components.](https://github.com/google/material-components-ios/commit/f15f6d5db6bcb0ddc6a750683daa649775f1e049) (Chris Cox) +* [Removes broken image.](https://github.com/google/material-components-ios/commit/ee3daf2fe52d44c8e552f40ffdd039f6937f896b) (Chris Cox) +* [Replace EditingManager with an Editing protocol.](https://github.com/google/material-components-ios/commit/60ffaa55909fd931d976ecbd55f91b5d71394ce4) (Jeff Verkoeyen) +* [Replace StyleManager with a Styling protocol.](https://github.com/google/material-components-ios/commit/8b925d001c8de48e226de4c83556ad5c440d9301) (Jeff Verkoeyen) +* [Updates readmes.](https://github.com/google/material-components-ios/commit/a858523cb27c6b7f891d5cfec4722172deb71b5a) (Chris Cox) +* [Updates to readme.](https://github.com/google/material-components-ios/commit/24972cb03d7a9ad30c635fd865ffe428788bda95) (Chris Cox) +* [Updates to readme.](https://github.com/google/material-components-ios/commit/89f5075dcdc2ddc20e22756ee408db53c8ffa967) (Chris Cox) + +### FlexibleHeader + +* [Add horizontal paging example.](https://github.com/google/material-components-ios/commit/bf2c12bd7c9b46eb23858673e6c63d3dfa20edf4) (Jeff Verkoeyen) +* [Add status bar visibility switch to configurator example.](https://github.com/google/material-components-ios/commit/34b656298fd35fe1440a5e0bfcf9e10934199d91) (Jeff Verkoeyen) +* [Add statusBarCanOverlapHeader property to MDCFlexibleHeaderView.](https://github.com/google/material-components-ios/commit/1229fc7b266eb939976a652a695f3d0b201cfea3) (Jeff Verkoeyen) +* [Configurator example is now a table view.](https://github.com/google/material-components-ios/commit/b9b819536bf19eca5298acc9fd471f92333cb555) (Jeff Verkoeyen) +* [Consolidate frame projection logic.](https://github.com/google/material-components-ios/commit/6b38608a33de7227143faa80933df7c2152c509f) (Jeff Verkoeyen) +* [Convert typical use example to use Interface Builder + auto layout.](https://github.com/google/material-components-ios/commit/a0690af49c5c92b1659950863ba8df7d21ff4428) (Jeff Verkoeyen) +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Funnel init through initWithStyle:.](https://github.com/google/material-components-ios/commit/35206a410e0fd5bc365d69fbbc0d955006558cdd) (Jeff Verkoeyen) +* [Hide header contents in the configurator when the header is shifting.](https://github.com/google/material-components-ios/commit/9310ae9de1f5e7a05cfe195f89852eeb9ea7ae33) (Jeff Verkoeyen) +* [Implement the correct designated initializer chain in the Configurator example.](https://github.com/google/material-components-ios/commit/c1777eb1f1e9e095257f20cd30a54d7ebc13fff7) (Jeff Verkoeyen) +* [Pull the instructions view out of the typical use example.](https://github.com/google/material-components-ios/commit/589c2bf3396a9844ca91f1a8e92a96fc92eaace1) (Jeff Verkoeyen) +* [Ran arc lint --everything --apply-patches.](https://github.com/google/material-components-ios/commit/a15bfbe35bdd6e61e17739ff4199b6a3940398a0) (Jeff Verkoeyen) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Remove usage of iOS 9 API in Configurator example.](https://github.com/google/material-components-ios/commit/6951e147d0a140fce0c9df61a5a074765ee0c0e8) (Jeff Verkoeyen) +* [Resolve iOS 8.4 unit test failure of issue176 tests.](https://github.com/google/material-components-ios/commit/271572706e98e93edde01a30a14daf129f639306) (Jeff Verkoeyen) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Revert "Resolve iOS 8.4 unit test failure of issue176 tests."](https://github.com/google/material-components-ios/commit/fe1ac2f14b7ad4179c84b01590df9c93289f2e36) (Jeff Verkoeyen) +* [Shift status bar with header](https://github.com/google/material-components-ios/commit/c8b22b1b344d211ecf04974bcd212a45e4673165) (keefertaylor) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Update README to include Swift examples](https://github.com/google/material-components-ios/commit/e46a7a032171b138b2fd62c74e1d8893fd95b166) (Ian Gordon) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [When changing min/max height, update the opposite property to match the new bounds.](https://github.com/google/material-components-ios/commit/757c0ec76413f0bd92b92ddc13692e803c6e0bff) (Jeff Verkoeyen) +* [When injecting insets, set the contentOffset rather than change it relatively.](https://github.com/google/material-components-ios/commit/fe107a900cde37fd4f4321d8cf333a20e040669f) (Jeff Verkoeyen) +* [[Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic.](https://github.com/google/material-components-ios/commit/2ab08f41337d1ef1392bec46f534fe95fb2ac79e) (Will Larche) +* [[Catalog] Example view controllers must implement init.](https://github.com/google/material-components-ios/commit/ea405734e97bf96bff2d97f46b28322a0aa2b753) (Jeff Verkoeyen) +* [[Catalog] Make example titles consistent, use Component Name](https://github.com/google/material-components-ios/commit/ebd1fbd14c6d2b0c052e28a9bc3cf59a6e01e75e) (Junius Gunaratne) +* [[FlexibleHeader]! No longer remove insets from tracking scroll views during dealloc.](https://github.com/google/material-components-ios/commit/4ddbae5d6342b3552914e1c4148e4dad11a70d88) (Jeff Verkoeyen) + +### FontDiskLoader + +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog and Typography] Group Typography and Font Loader examples into Typography and Fonts](https://github.com/google/material-components-ios/commit/62f57e2b290155d5bfdfafb3f27839eb8e9c1eb5) (Junius Gunaratne) + +### HeaderStackView + +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Ran arc lint --everything --apply-patches.](https://github.com/google/material-components-ios/commit/a15bfbe35bdd6e61e17739ff4199b6a3940398a0) (Jeff Verkoeyen) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Rename mdc_theme.png to header_stack_view_theme.png.](https://github.com/google/material-components-ios/commit/03e0cdb05742eca86b0c2f2ee678d99617827cbe) (Jeff Verkoeyen) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic.](https://github.com/google/material-components-ios/commit/2ab08f41337d1ef1392bec46f534fe95fb2ac79e) (Will Larche) +* [[Catalog] Add autoresize masks to header stack view demo for landscape orientation](https://github.com/google/material-components-ios/commit/6206c48cb0dfb3c23ed228ba31fce6892c3094b8) (Junius Gunaratne) +* [[Catalog] Fix color change issue in header stack view demo](https://github.com/google/material-components-ios/commit/ed9c65c3d22e1318aac4b06b8442e6f8e7fa3428) (Junius Gunaratne) +* [[Catalog] Update AppBar demo design, table view should not have text](https://github.com/google/material-components-ios/commit/a147d7b359d1d99fca81aa97594d93263a3cedee) (Junius Gunaratne) +* [[Catalog] Update Header Stack View demo visuals, move layout code into supplemental](https://github.com/google/material-components-ios/commit/cdc18fb8ccda075e1f8c56fd638c3a66df6f4a93) (Junius Gunaratne) +* [added missing swift code snippet to readme.](https://github.com/google/material-components-ios/commit/06df37a74d2b9e99f620c09a2674cc3853c42b19) (randallli) + +### Ink + +* [Clarified MDCInkTouchControllerDelegate inkTouchController:shouldProcessInkTouchesAtTouchLocation: documentation.](https://github.com/google/material-components-ios/commit/6241d918df44a82218349223285d6b9881d8534b) (Adrian Secord) +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Update README to include Swift examples](https://github.com/google/material-components-ios/commit/6dfac14b518bd81dc62c3398f4416a3cf1fe57e3) (Ian Gordon) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic.](https://github.com/google/material-components-ios/commit/2ab08f41337d1ef1392bec46f534fe95fb2ac79e) (Will Larche) +* [[Catalog] Change ink demo shapes to represent pseudo button/FAB, move layout code into supplemental](https://github.com/google/material-components-ios/commit/d0683a0e0bbe73fb36641c9ea92a5e715c438d19) (Junius Gunaratne) + +### NavigationBar + +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Explain exception for UINavigationBar/MDCNavigationBar comparison.](https://github.com/google/material-components-ios/commit/5187441e7d42f8006372dbd245e0f51ce1803c53) (Jeff Verkoeyen) +* [Ran arc lint --everything --apply-patches.](https://github.com/google/material-components-ios/commit/a15bfbe35bdd6e61e17739ff4199b6a3940398a0) (Jeff Verkoeyen) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [Use UIViewNoIntrinsicMetric to indicate the the NavigationBar has no intrinsic width.](https://github.com/google/material-components-ios/commit/1e84b7cd5173d85bffb77628341e91d08a90ced6) (Jeff Verkoeyen) +* [[Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic.](https://github.com/google/material-components-ios/commit/2ab08f41337d1ef1392bec46f534fe95fb2ac79e) (Will Larche) +* [[Catalog] Make example titles consistent, use Component Name](https://github.com/google/material-components-ios/commit/ebd1fbd14c6d2b0c052e28a9bc3cf59a6e01e75e) (Junius Gunaratne) +* [[Examples] Correcting scope modifier of functions in Swift](https://github.com/google/material-components-ios/commit/6d5406baac2d0bb484fff90882c39309a68bc744) (Will Larche) + +### PageControl + +* [Added MDCPageControl initWithCoder:.](https://github.com/google/material-components-ios/commit/43cd7fea3208134fbcf324d11492a9fd182506b4) (Adrian Secord) +* [Added test for updating the currentPage when the contentOffset changes](https://github.com/google/material-components-ios/commit/ac8642f4a0b9cd90946ff6fa65440a39b934ebbf) (randallli) +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Created swift example for page control and added it to readme](https://github.com/google/material-components-ios/commit/1272a1275b219d252dffcefc7748b0e025e50829) (randallli) +* [Fix crash when scrollView offset is set out of bounds of the numberOfPages](https://github.com/google/material-components-ios/commit/e1f584adad2950a9c46ba7dba2da01461b6ea899) (randallli) +* [Publicized conformance to UIScrollViewDelegate.](https://github.com/google/material-components-ios/commit/e0d8050ef395928ba3eee40045059b9c46c9f21a) (randallli) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic.](https://github.com/google/material-components-ios/commit/2ab08f41337d1ef1392bec46f534fe95fb2ac79e) (Will Larche) + +### RobotoFontLoader + +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic.](https://github.com/google/material-components-ios/commit/2ab08f41337d1ef1392bec46f534fe95fb2ac79e) (Will Larche) +* [[Catalog and Typography] Group Typography and Font Loader examples into Typography and Fonts](https://github.com/google/material-components-ios/commit/62f57e2b290155d5bfdfafb3f27839eb8e9c1eb5) (Junius Gunaratne) + +### ShadowElevations + +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Update documention with Objective C examples](https://github.com/google/material-components-ios/commit/49af52411077d5587ccc4bcb4019ac5ece4a55fe) (Ian Gordon) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic.](https://github.com/google/material-components-ios/commit/2ab08f41337d1ef1392bec46f534fe95fb2ac79e) (Will Larche) +* [[Catalog and Shadow] Group shadow elevations with shadow demos](https://github.com/google/material-components-ios/commit/b8dc78d58c06c45a8616e6798839b37fd065fc05) (Junius Gunaratne) + +### ShadowLayer + +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog and Shadow] Group shadow elevations with shadow demos](https://github.com/google/material-components-ios/commit/b8dc78d58c06c45a8616e6798839b37fd065fc05) (Junius Gunaratne) +* [[Catalog] Make catalogIsPrimaryDemo static method in demos](https://github.com/google/material-components-ios/commit/7bb181a150bc1cf707637fdf112d3a949638c809) (Junius Gunaratne) +* [[Shadow] Add swift examples to the documentation](https://github.com/google/material-components-ios/commit/adba94e106b4e576160f15380f97f720800ec373) (Ian Gordon) + +### Slider + +* [Added swift example to slider readme](https://github.com/google/material-components-ios/commit/835a6d358253253cbd3bea960fd8eebb09ddfaf3) (randallli) +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Fix unit tests by increasing epsilon](https://github.com/google/material-components-ios/commit/8ab280fdc926fe389396a248f2d28dc24fa75c97) (randallli) +* [Fixes MDCSlider example build with required import.](https://github.com/google/material-components-ios/commit/bd6ef35fd82c4ee16d2a4d077e224499a3abe9ed) (Adrian Secord) +* [Import the umbrella header in the typical use example.](https://github.com/google/material-components-ios/commit/917f68f953132ba345fdf70fe79d80c3d68b23b6) (Jeff Verkoeyen) +* [Ran arc lint --everything --apply-patches.](https://github.com/google/material-components-ios/commit/a15bfbe35bdd6e61e17739ff4199b6a3940398a0) (Jeff Verkoeyen) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog] Make catalogIsPrimaryDemo method in slider demo static to match other examples](https://github.com/google/material-components-ios/commit/a9a28c4cde35e476e886c1f90c44b96988c72f73) (Junius Gunaratne) +* [[Catalog] Use slider in variable names in example layout code](https://github.com/google/material-components-ios/commit/784e9a57be01dfa8f93528aa4a92de0b772b4605) (Junius Gunaratne) + +### SpritedAnimationView + +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Rename all images with @2x/@3x.](https://github.com/google/material-components-ios/commit/2df5344ee2a5a35d2e2c6d7d33ac79ecbdc1afba) (Louis Romero) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic.](https://github.com/google/material-components-ios/commit/2ab08f41337d1ef1392bec46f534fe95fb2ac79e) (Will Larche) + +### Switch + +* [Added swift example to catalog and readme.](https://github.com/google/material-components-ios/commit/1a7d800cc9a6b29320bf57d61806a85cde387f15) (randallli) +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Rename label from slider to switch.](https://github.com/google/material-components-ios/commit/c92a9510bc8ed6d18339b4d7928cc470cc2a4f07) (Ian Gordon) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Set the switch's ink's max ripple radius to the spec value.](https://github.com/google/material-components-ios/commit/c15fd0c5c18e442947728472b7b33381cae78f45) (Adrian Secord) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog & Examples] Added navigationBar example in Swift (Supplemental POC) and corrected slight mistake in Catalog by Convention logic.](https://github.com/google/material-components-ios/commit/2ab08f41337d1ef1392bec46f534fe95fb2ac79e) (Will Larche) +* [[Catalog and Switch] Update switch demo, move layout code to supplemental, update switch color](https://github.com/google/material-components-ios/commit/e700bf58ee43e35d34b565b9b04f0af4b03f1288) (Junius Gunaratne) +* [[Catalog] Make example titles consistent, use Component Name](https://github.com/google/material-components-ios/commit/ebd1fbd14c6d2b0c052e28a9bc3cf59a6e01e75e) (Junius Gunaratne) + +### Typography + +* [Correct links for deploy on various deployment environment.](https://github.com/google/material-components-ios/commit/2b6f7f40a75410e57e13c20cab2d18dd09ae2566) (Yiran Mao) +* [Remove obsolete jazzy.yaml files.](https://github.com/google/material-components-ios/commit/fffb75e91e8ab5b979dba7a7fec661d1a058bb11) (Yiran Mao) +* [Resolve iOS 8.4 crash in the Typography hero demo.](https://github.com/google/material-components-ios/commit/0859d4b2b2ba8e7e9d91fbd757ac4b0a006384fc) (Jeff Verkoeyen) +* [Revert "Remove obsolete jazzy.yaml files."](https://github.com/google/material-components-ios/commit/ad228a154455835c3e871109f12670387b9d926d) (Jeff Verkoeyen) +* [Set autoresizing masks on Read Me example.](https://github.com/google/material-components-ios/commit/b54b2c96978fdb15d44c15b565f94ab43e60a22a) (Jeff Verkoeyen) +* [Typography hero demo is now a UITableView.](https://github.com/google/material-components-ios/commit/25c20a9abf9a6c9d30397dc7387646ead5fcafc4) (Jeff Verkoeyen) +* [Update .jazzy.yaml module property.](https://github.com/google/material-components-ios/commit/977626313e702783835f7ad1aba220b99316ba3f) (Jeff Verkoeyen) +* [Updated top-level "Documentation" to "Components".](https://github.com/google/material-components-ios/commit/ac38382e86f058593de41756fb8360dc5a156a10) (Adrian Secord) +* [[Catalog and Typography] Group Typography and Font Loader examples into Typography and Fonts](https://github.com/google/material-components-ios/commit/62f57e2b290155d5bfdfafb3f27839eb8e9c1eb5) (Junius Gunaratne) +* [[Catalog] Fixing Swift example view controller initializers.](https://github.com/google/material-components-ios/commit/8e733e163a4b308186345e03d90056e4040c693b) (Jeff Verkoeyen) +* [[Catalog] Make catalogIsPrimaryDemo static method in demos](https://github.com/google/material-components-ios/commit/7bb181a150bc1cf707637fdf112d3a949638c809) (Junius Gunaratne) +* [[Catalog] Title row name should correspond to type style](https://github.com/google/material-components-ios/commit/a576d6e7ae209a1d3308f1fe6312ae0ef4c5e54d) (Junius Gunaratne) + # 4.0.1 ## API diffs From 54c5ad7bb55eb4d44e1e3c2aca4ab227f66224b0 Mon Sep 17 00:00:00 2001 From: Ian Gordon Date: Tue, 26 Apr 2016 16:31:07 -0400 Subject: [PATCH 129/129] Release candidate. Summary: Checklist: - [ ] Ran `arc unit --everything`. - [ ] Ran `scripts/build_all_pod_projects`. - [ ] Ran `scripts/release/api_diff` and pasted the results into CHANGELOG.md. - [ ] Ran `scripts/release/changes` and pasted the results into CHANGELOG.md. - [ ] Visually inspected the API diff to ensure it accurately reflects the release's changes. - [ ] Ran `scripts/release/diff components/*/src/` and visually inspected the changes. - [ ] Ran `scripts/release/bump` with the new version number. - [ ] Updated CHANGELOG.md's latest section header to match the release's version number. Reviewers: #mdc_ios_owners Projects: #material_components_ios Differential Revision: http://codereview.cc/D767 --- MaterialComponents.podspec | 2 +- MaterialComponentsCatalog.podspec | 2 +- MaterialComponentsUnitTests.podspec | 2 +- catalog/Podfile.lock | 136 ++++++++++++++-------------- demos/Pesto/Podfile.lock | 128 +++++++++++++------------- demos/Shrine/Podfile.lock | 128 +++++++++++++------------- 6 files changed, 199 insertions(+), 199 deletions(-) diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec index fd3545ce280..30caece430c 100644 --- a/MaterialComponents.podspec +++ b/MaterialComponents.podspec @@ -2,7 +2,7 @@ load 'scripts/generated/icons.rb' Pod::Spec.new do |s| s.name = "MaterialComponents" - s.version = "4.0.1" + s.version = "5.0.0" s.authors = { 'Apple platform engineering at Google' => 'appleplatforms@google.com' } s.summary = "A collection of stand-alone production-ready UI libraries focused on design details." s.homepage = "https://github.com/google/material-components-ios" diff --git a/MaterialComponentsCatalog.podspec b/MaterialComponentsCatalog.podspec index 9a69b397615..a670be3bd63 100644 --- a/MaterialComponentsCatalog.podspec +++ b/MaterialComponentsCatalog.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialComponentsCatalog" - s.version = "4.0.1" + s.version = "5.0.0" s.authors = { 'Apple platform engineering at Google' => 'appleplatforms@google.com' } s.summary = "A collection of stand-alone production-ready UI libraries focused on design details." s.homepage = "https://github.com/google/material-components-ios" diff --git a/MaterialComponentsUnitTests.podspec b/MaterialComponentsUnitTests.podspec index b36a2bb8188..36f860d2f1a 100644 --- a/MaterialComponentsUnitTests.podspec +++ b/MaterialComponentsUnitTests.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialComponentsUnitTests" - s.version = "4.0.1" + s.version = "5.0.0" s.authors = { 'Apple platform engineering at Google' => 'appleplatforms@google.com' } s.summary = "A collection of stand-alone production-ready UI libraries focused on design details." s.homepage = "https://github.com/google/material-components-ios" diff --git a/catalog/Podfile.lock b/catalog/Podfile.lock index f05362312db..1bcce02f66a 100644 --- a/catalog/Podfile.lock +++ b/catalog/Podfile.lock @@ -1,26 +1,26 @@ PODS: - - MaterialComponents (4.0.1): - - MaterialComponents/AppBar (= 4.0.1) - - MaterialComponents/ButtonBar (= 4.0.1) - - MaterialComponents/Buttons (= 4.0.1) - - MaterialComponents/CollectionCells (= 4.0.1) - - MaterialComponents/CollectionLayoutAttributes (= 4.0.1) - - MaterialComponents/Collections (= 4.0.1) - - MaterialComponents/FlexibleHeader (= 4.0.1) - - MaterialComponents/FontDiskLoader (= 4.0.1) - - MaterialComponents/HeaderStackView (= 4.0.1) - - MaterialComponents/Ink (= 4.0.1) - - MaterialComponents/NavigationBar (= 4.0.1) - - MaterialComponents/PageControl (= 4.0.1) - - MaterialComponents/private (= 4.0.1) - - MaterialComponents/RobotoFontLoader (= 4.0.1) - - MaterialComponents/ShadowElevations (= 4.0.1) - - MaterialComponents/ShadowLayer (= 4.0.1) - - MaterialComponents/Slider (= 4.0.1) - - MaterialComponents/SpritedAnimationView (= 4.0.1) - - MaterialComponents/Switch (= 4.0.1) - - MaterialComponents/Typography (= 4.0.1) - - MaterialComponents/AppBar (4.0.1): + - MaterialComponents (5.0.0): + - MaterialComponents/AppBar (= 5.0.0) + - MaterialComponents/ButtonBar (= 5.0.0) + - MaterialComponents/Buttons (= 5.0.0) + - MaterialComponents/CollectionCells (= 5.0.0) + - MaterialComponents/CollectionLayoutAttributes (= 5.0.0) + - MaterialComponents/Collections (= 5.0.0) + - MaterialComponents/FlexibleHeader (= 5.0.0) + - MaterialComponents/FontDiskLoader (= 5.0.0) + - MaterialComponents/HeaderStackView (= 5.0.0) + - MaterialComponents/Ink (= 5.0.0) + - MaterialComponents/NavigationBar (= 5.0.0) + - MaterialComponents/PageControl (= 5.0.0) + - MaterialComponents/private (= 5.0.0) + - MaterialComponents/RobotoFontLoader (= 5.0.0) + - MaterialComponents/ShadowElevations (= 5.0.0) + - MaterialComponents/ShadowLayer (= 5.0.0) + - MaterialComponents/Slider (= 5.0.0) + - MaterialComponents/SpritedAnimationView (= 5.0.0) + - MaterialComponents/Switch (= 5.0.0) + - MaterialComponents/Typography (= 5.0.0) + - MaterialComponents/AppBar (5.0.0): - MaterialComponents/FlexibleHeader - MaterialComponents/HeaderStackView - MaterialComponents/NavigationBar @@ -28,14 +28,14 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/ButtonBar (4.0.1): + - MaterialComponents/ButtonBar (5.0.0): - MaterialComponents/Buttons - - MaterialComponents/Buttons (4.0.1): + - MaterialComponents/Buttons (5.0.0): - MaterialComponents/Ink - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/CollectionCells (4.0.1): + - MaterialComponents/CollectionCells (5.0.0): - MaterialComponents/CollectionLayoutAttributes - MaterialComponents/Ink - MaterialComponents/private/Icons/ic_check @@ -45,70 +45,70 @@ PODS: - MaterialComponents/private/Icons/ic_radio_button_unchecked - MaterialComponents/private/Icons/ic_reorder - MaterialComponents/Typography - - MaterialComponents/CollectionLayoutAttributes (4.0.1) - - MaterialComponents/Collections (4.0.1): + - MaterialComponents/CollectionLayoutAttributes (5.0.0) + - MaterialComponents/Collections (5.0.0): - MaterialComponents/CollectionCells - MaterialComponents/CollectionLayoutAttributes - MaterialComponents/Ink - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/FlexibleHeader (4.0.1) - - MaterialComponents/FontDiskLoader (4.0.1) - - MaterialComponents/HeaderStackView (4.0.1) - - MaterialComponents/Ink (4.0.1) - - MaterialComponents/NavigationBar (4.0.1): + - MaterialComponents/FlexibleHeader (5.0.0) + - MaterialComponents/FontDiskLoader (5.0.0) + - MaterialComponents/HeaderStackView (5.0.0) + - MaterialComponents/Ink (5.0.0) + - MaterialComponents/NavigationBar (5.0.0): - MaterialComponents/ButtonBar - MaterialComponents/Typography - - MaterialComponents/PageControl (4.0.1) - - MaterialComponents/private (4.0.1): - - MaterialComponents/private/Color (= 4.0.1) - - MaterialComponents/private/Icons (= 4.0.1) - - MaterialComponents/private/ThumbTrack (= 4.0.1) - - MaterialComponents/private/Color (4.0.1) - - MaterialComponents/private/Icons (4.0.1): - - MaterialComponents/private/Icons/Base (= 4.0.1) - - MaterialComponents/private/Icons/ic_arrow_back (= 4.0.1) - - MaterialComponents/private/Icons/ic_check (= 4.0.1) - - MaterialComponents/private/Icons/ic_check_circle (= 4.0.1) - - MaterialComponents/private/Icons/ic_chevron_right (= 4.0.1) - - MaterialComponents/private/Icons/ic_info (= 4.0.1) - - MaterialComponents/private/Icons/ic_radio_button_unchecked (= 4.0.1) - - MaterialComponents/private/Icons/ic_reorder (= 4.0.1) - - MaterialComponents/private/Icons/Base (4.0.1) - - MaterialComponents/private/Icons/ic_arrow_back (4.0.1): + - MaterialComponents/PageControl (5.0.0) + - MaterialComponents/private (5.0.0): + - MaterialComponents/private/Color (= 5.0.0) + - MaterialComponents/private/Icons (= 5.0.0) + - MaterialComponents/private/ThumbTrack (= 5.0.0) + - MaterialComponents/private/Color (5.0.0) + - MaterialComponents/private/Icons (5.0.0): + - MaterialComponents/private/Icons/Base (= 5.0.0) + - MaterialComponents/private/Icons/ic_arrow_back (= 5.0.0) + - MaterialComponents/private/Icons/ic_check (= 5.0.0) + - MaterialComponents/private/Icons/ic_check_circle (= 5.0.0) + - MaterialComponents/private/Icons/ic_chevron_right (= 5.0.0) + - MaterialComponents/private/Icons/ic_info (= 5.0.0) + - MaterialComponents/private/Icons/ic_radio_button_unchecked (= 5.0.0) + - MaterialComponents/private/Icons/ic_reorder (= 5.0.0) + - MaterialComponents/private/Icons/Base (5.0.0) + - MaterialComponents/private/Icons/ic_arrow_back (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_check (4.0.1): + - MaterialComponents/private/Icons/ic_check (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_check_circle (4.0.1): + - MaterialComponents/private/Icons/ic_check_circle (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_chevron_right (4.0.1): + - MaterialComponents/private/Icons/ic_chevron_right (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_info (4.0.1): + - MaterialComponents/private/Icons/ic_info (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_radio_button_unchecked (4.0.1): + - MaterialComponents/private/Icons/ic_radio_button_unchecked (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_reorder (4.0.1): + - MaterialComponents/private/Icons/ic_reorder (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/ThumbTrack (4.0.1): + - MaterialComponents/private/ThumbTrack (5.0.0): - MaterialComponents/Ink - MaterialComponents/private/Color - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - - MaterialComponents/RobotoFontLoader (4.0.1): + - MaterialComponents/RobotoFontLoader (5.0.0): - MaterialComponents/FontDiskLoader - MaterialComponents/Typography - - MaterialComponents/ShadowElevations (4.0.1) - - MaterialComponents/ShadowLayer (4.0.1) - - MaterialComponents/Slider (4.0.1): + - MaterialComponents/ShadowElevations (5.0.0) + - MaterialComponents/ShadowLayer (5.0.0) + - MaterialComponents/Slider (5.0.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/SpritedAnimationView (4.0.1) - - MaterialComponents/Switch (4.0.1): + - MaterialComponents/SpritedAnimationView (5.0.0) + - MaterialComponents/Switch (5.0.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/Typography (4.0.1) - - MaterialComponentsCatalog (4.0.1): + - MaterialComponents/Typography (5.0.0) + - MaterialComponentsCatalog (5.0.0): - MaterialComponents - - MaterialComponentsUnitTests (4.0.1): + - MaterialComponentsUnitTests (5.0.0): - MaterialComponents DEPENDENCIES: @@ -125,8 +125,8 @@ EXTERNAL SOURCES: :path: ../ SPEC CHECKSUMS: - MaterialComponents: 63514a0e6f6fb1d29604ded592b91e6b84238505 - MaterialComponentsCatalog: 4769fcabbbdb4b40373eb428f329dbc4c2b53dac - MaterialComponentsUnitTests: 8fa9520a8dae5ee0b7962ce449619b5be13445e6 + MaterialComponents: 5de833a54f1cd86c5826c605d8fb00db0d5c4626 + MaterialComponentsCatalog: 3c3164b20890756c6701ec09d1420c3d2114c876 + MaterialComponentsUnitTests: 37830e7944154daea9f6aea9664249635e03ce00 COCOAPODS: 0.39.0 diff --git a/demos/Pesto/Podfile.lock b/demos/Pesto/Podfile.lock index ce21cb30a90..91e20388c24 100644 --- a/demos/Pesto/Podfile.lock +++ b/demos/Pesto/Podfile.lock @@ -1,26 +1,26 @@ PODS: - - MaterialComponents (4.0.1): - - MaterialComponents/AppBar (= 4.0.1) - - MaterialComponents/ButtonBar (= 4.0.1) - - MaterialComponents/Buttons (= 4.0.1) - - MaterialComponents/CollectionCells (= 4.0.1) - - MaterialComponents/CollectionLayoutAttributes (= 4.0.1) - - MaterialComponents/Collections (= 4.0.1) - - MaterialComponents/FlexibleHeader (= 4.0.1) - - MaterialComponents/FontDiskLoader (= 4.0.1) - - MaterialComponents/HeaderStackView (= 4.0.1) - - MaterialComponents/Ink (= 4.0.1) - - MaterialComponents/NavigationBar (= 4.0.1) - - MaterialComponents/PageControl (= 4.0.1) - - MaterialComponents/private (= 4.0.1) - - MaterialComponents/RobotoFontLoader (= 4.0.1) - - MaterialComponents/ShadowElevations (= 4.0.1) - - MaterialComponents/ShadowLayer (= 4.0.1) - - MaterialComponents/Slider (= 4.0.1) - - MaterialComponents/SpritedAnimationView (= 4.0.1) - - MaterialComponents/Switch (= 4.0.1) - - MaterialComponents/Typography (= 4.0.1) - - MaterialComponents/AppBar (4.0.1): + - MaterialComponents (5.0.0): + - MaterialComponents/AppBar (= 5.0.0) + - MaterialComponents/ButtonBar (= 5.0.0) + - MaterialComponents/Buttons (= 5.0.0) + - MaterialComponents/CollectionCells (= 5.0.0) + - MaterialComponents/CollectionLayoutAttributes (= 5.0.0) + - MaterialComponents/Collections (= 5.0.0) + - MaterialComponents/FlexibleHeader (= 5.0.0) + - MaterialComponents/FontDiskLoader (= 5.0.0) + - MaterialComponents/HeaderStackView (= 5.0.0) + - MaterialComponents/Ink (= 5.0.0) + - MaterialComponents/NavigationBar (= 5.0.0) + - MaterialComponents/PageControl (= 5.0.0) + - MaterialComponents/private (= 5.0.0) + - MaterialComponents/RobotoFontLoader (= 5.0.0) + - MaterialComponents/ShadowElevations (= 5.0.0) + - MaterialComponents/ShadowLayer (= 5.0.0) + - MaterialComponents/Slider (= 5.0.0) + - MaterialComponents/SpritedAnimationView (= 5.0.0) + - MaterialComponents/Switch (= 5.0.0) + - MaterialComponents/Typography (= 5.0.0) + - MaterialComponents/AppBar (5.0.0): - MaterialComponents/FlexibleHeader - MaterialComponents/HeaderStackView - MaterialComponents/NavigationBar @@ -28,14 +28,14 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/ButtonBar (4.0.1): + - MaterialComponents/ButtonBar (5.0.0): - MaterialComponents/Buttons - - MaterialComponents/Buttons (4.0.1): + - MaterialComponents/Buttons (5.0.0): - MaterialComponents/Ink - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/CollectionCells (4.0.1): + - MaterialComponents/CollectionCells (5.0.0): - MaterialComponents/CollectionLayoutAttributes - MaterialComponents/Ink - MaterialComponents/private/Icons/ic_check @@ -45,67 +45,67 @@ PODS: - MaterialComponents/private/Icons/ic_radio_button_unchecked - MaterialComponents/private/Icons/ic_reorder - MaterialComponents/Typography - - MaterialComponents/CollectionLayoutAttributes (4.0.1) - - MaterialComponents/Collections (4.0.1): + - MaterialComponents/CollectionLayoutAttributes (5.0.0) + - MaterialComponents/Collections (5.0.0): - MaterialComponents/CollectionCells - MaterialComponents/CollectionLayoutAttributes - MaterialComponents/Ink - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/FlexibleHeader (4.0.1) - - MaterialComponents/FontDiskLoader (4.0.1) - - MaterialComponents/HeaderStackView (4.0.1) - - MaterialComponents/Ink (4.0.1) - - MaterialComponents/NavigationBar (4.0.1): + - MaterialComponents/FlexibleHeader (5.0.0) + - MaterialComponents/FontDiskLoader (5.0.0) + - MaterialComponents/HeaderStackView (5.0.0) + - MaterialComponents/Ink (5.0.0) + - MaterialComponents/NavigationBar (5.0.0): - MaterialComponents/ButtonBar - MaterialComponents/Typography - - MaterialComponents/PageControl (4.0.1) - - MaterialComponents/private (4.0.1): - - MaterialComponents/private/Color (= 4.0.1) - - MaterialComponents/private/Icons (= 4.0.1) - - MaterialComponents/private/ThumbTrack (= 4.0.1) - - MaterialComponents/private/Color (4.0.1) - - MaterialComponents/private/Icons (4.0.1): - - MaterialComponents/private/Icons/Base (= 4.0.1) - - MaterialComponents/private/Icons/ic_arrow_back (= 4.0.1) - - MaterialComponents/private/Icons/ic_check (= 4.0.1) - - MaterialComponents/private/Icons/ic_check_circle (= 4.0.1) - - MaterialComponents/private/Icons/ic_chevron_right (= 4.0.1) - - MaterialComponents/private/Icons/ic_info (= 4.0.1) - - MaterialComponents/private/Icons/ic_radio_button_unchecked (= 4.0.1) - - MaterialComponents/private/Icons/ic_reorder (= 4.0.1) - - MaterialComponents/private/Icons/Base (4.0.1) - - MaterialComponents/private/Icons/ic_arrow_back (4.0.1): + - MaterialComponents/PageControl (5.0.0) + - MaterialComponents/private (5.0.0): + - MaterialComponents/private/Color (= 5.0.0) + - MaterialComponents/private/Icons (= 5.0.0) + - MaterialComponents/private/ThumbTrack (= 5.0.0) + - MaterialComponents/private/Color (5.0.0) + - MaterialComponents/private/Icons (5.0.0): + - MaterialComponents/private/Icons/Base (= 5.0.0) + - MaterialComponents/private/Icons/ic_arrow_back (= 5.0.0) + - MaterialComponents/private/Icons/ic_check (= 5.0.0) + - MaterialComponents/private/Icons/ic_check_circle (= 5.0.0) + - MaterialComponents/private/Icons/ic_chevron_right (= 5.0.0) + - MaterialComponents/private/Icons/ic_info (= 5.0.0) + - MaterialComponents/private/Icons/ic_radio_button_unchecked (= 5.0.0) + - MaterialComponents/private/Icons/ic_reorder (= 5.0.0) + - MaterialComponents/private/Icons/Base (5.0.0) + - MaterialComponents/private/Icons/ic_arrow_back (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_check (4.0.1): + - MaterialComponents/private/Icons/ic_check (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_check_circle (4.0.1): + - MaterialComponents/private/Icons/ic_check_circle (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_chevron_right (4.0.1): + - MaterialComponents/private/Icons/ic_chevron_right (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_info (4.0.1): + - MaterialComponents/private/Icons/ic_info (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_radio_button_unchecked (4.0.1): + - MaterialComponents/private/Icons/ic_radio_button_unchecked (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_reorder (4.0.1): + - MaterialComponents/private/Icons/ic_reorder (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/ThumbTrack (4.0.1): + - MaterialComponents/private/ThumbTrack (5.0.0): - MaterialComponents/Ink - MaterialComponents/private/Color - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - - MaterialComponents/RobotoFontLoader (4.0.1): + - MaterialComponents/RobotoFontLoader (5.0.0): - MaterialComponents/FontDiskLoader - MaterialComponents/Typography - - MaterialComponents/ShadowElevations (4.0.1) - - MaterialComponents/ShadowLayer (4.0.1) - - MaterialComponents/Slider (4.0.1): + - MaterialComponents/ShadowElevations (5.0.0) + - MaterialComponents/ShadowLayer (5.0.0) + - MaterialComponents/Slider (5.0.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/SpritedAnimationView (4.0.1) - - MaterialComponents/Switch (4.0.1): + - MaterialComponents/SpritedAnimationView (5.0.0) + - MaterialComponents/Switch (5.0.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/Typography (4.0.1) + - MaterialComponents/Typography (5.0.0) DEPENDENCIES: - MaterialComponents (from `../../`) @@ -115,6 +115,6 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - MaterialComponents: 63514a0e6f6fb1d29604ded592b91e6b84238505 + MaterialComponents: 5de833a54f1cd86c5826c605d8fb00db0d5c4626 COCOAPODS: 0.39.0 diff --git a/demos/Shrine/Podfile.lock b/demos/Shrine/Podfile.lock index ce21cb30a90..91e20388c24 100644 --- a/demos/Shrine/Podfile.lock +++ b/demos/Shrine/Podfile.lock @@ -1,26 +1,26 @@ PODS: - - MaterialComponents (4.0.1): - - MaterialComponents/AppBar (= 4.0.1) - - MaterialComponents/ButtonBar (= 4.0.1) - - MaterialComponents/Buttons (= 4.0.1) - - MaterialComponents/CollectionCells (= 4.0.1) - - MaterialComponents/CollectionLayoutAttributes (= 4.0.1) - - MaterialComponents/Collections (= 4.0.1) - - MaterialComponents/FlexibleHeader (= 4.0.1) - - MaterialComponents/FontDiskLoader (= 4.0.1) - - MaterialComponents/HeaderStackView (= 4.0.1) - - MaterialComponents/Ink (= 4.0.1) - - MaterialComponents/NavigationBar (= 4.0.1) - - MaterialComponents/PageControl (= 4.0.1) - - MaterialComponents/private (= 4.0.1) - - MaterialComponents/RobotoFontLoader (= 4.0.1) - - MaterialComponents/ShadowElevations (= 4.0.1) - - MaterialComponents/ShadowLayer (= 4.0.1) - - MaterialComponents/Slider (= 4.0.1) - - MaterialComponents/SpritedAnimationView (= 4.0.1) - - MaterialComponents/Switch (= 4.0.1) - - MaterialComponents/Typography (= 4.0.1) - - MaterialComponents/AppBar (4.0.1): + - MaterialComponents (5.0.0): + - MaterialComponents/AppBar (= 5.0.0) + - MaterialComponents/ButtonBar (= 5.0.0) + - MaterialComponents/Buttons (= 5.0.0) + - MaterialComponents/CollectionCells (= 5.0.0) + - MaterialComponents/CollectionLayoutAttributes (= 5.0.0) + - MaterialComponents/Collections (= 5.0.0) + - MaterialComponents/FlexibleHeader (= 5.0.0) + - MaterialComponents/FontDiskLoader (= 5.0.0) + - MaterialComponents/HeaderStackView (= 5.0.0) + - MaterialComponents/Ink (= 5.0.0) + - MaterialComponents/NavigationBar (= 5.0.0) + - MaterialComponents/PageControl (= 5.0.0) + - MaterialComponents/private (= 5.0.0) + - MaterialComponents/RobotoFontLoader (= 5.0.0) + - MaterialComponents/ShadowElevations (= 5.0.0) + - MaterialComponents/ShadowLayer (= 5.0.0) + - MaterialComponents/Slider (= 5.0.0) + - MaterialComponents/SpritedAnimationView (= 5.0.0) + - MaterialComponents/Switch (= 5.0.0) + - MaterialComponents/Typography (= 5.0.0) + - MaterialComponents/AppBar (5.0.0): - MaterialComponents/FlexibleHeader - MaterialComponents/HeaderStackView - MaterialComponents/NavigationBar @@ -28,14 +28,14 @@ PODS: - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/ButtonBar (4.0.1): + - MaterialComponents/ButtonBar (5.0.0): - MaterialComponents/Buttons - - MaterialComponents/Buttons (4.0.1): + - MaterialComponents/Buttons (5.0.0): - MaterialComponents/Ink - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/CollectionCells (4.0.1): + - MaterialComponents/CollectionCells (5.0.0): - MaterialComponents/CollectionLayoutAttributes - MaterialComponents/Ink - MaterialComponents/private/Icons/ic_check @@ -45,67 +45,67 @@ PODS: - MaterialComponents/private/Icons/ic_radio_button_unchecked - MaterialComponents/private/Icons/ic_reorder - MaterialComponents/Typography - - MaterialComponents/CollectionLayoutAttributes (4.0.1) - - MaterialComponents/Collections (4.0.1): + - MaterialComponents/CollectionLayoutAttributes (5.0.0) + - MaterialComponents/Collections (5.0.0): - MaterialComponents/CollectionCells - MaterialComponents/CollectionLayoutAttributes - MaterialComponents/Ink - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - MaterialComponents/Typography - - MaterialComponents/FlexibleHeader (4.0.1) - - MaterialComponents/FontDiskLoader (4.0.1) - - MaterialComponents/HeaderStackView (4.0.1) - - MaterialComponents/Ink (4.0.1) - - MaterialComponents/NavigationBar (4.0.1): + - MaterialComponents/FlexibleHeader (5.0.0) + - MaterialComponents/FontDiskLoader (5.0.0) + - MaterialComponents/HeaderStackView (5.0.0) + - MaterialComponents/Ink (5.0.0) + - MaterialComponents/NavigationBar (5.0.0): - MaterialComponents/ButtonBar - MaterialComponents/Typography - - MaterialComponents/PageControl (4.0.1) - - MaterialComponents/private (4.0.1): - - MaterialComponents/private/Color (= 4.0.1) - - MaterialComponents/private/Icons (= 4.0.1) - - MaterialComponents/private/ThumbTrack (= 4.0.1) - - MaterialComponents/private/Color (4.0.1) - - MaterialComponents/private/Icons (4.0.1): - - MaterialComponents/private/Icons/Base (= 4.0.1) - - MaterialComponents/private/Icons/ic_arrow_back (= 4.0.1) - - MaterialComponents/private/Icons/ic_check (= 4.0.1) - - MaterialComponents/private/Icons/ic_check_circle (= 4.0.1) - - MaterialComponents/private/Icons/ic_chevron_right (= 4.0.1) - - MaterialComponents/private/Icons/ic_info (= 4.0.1) - - MaterialComponents/private/Icons/ic_radio_button_unchecked (= 4.0.1) - - MaterialComponents/private/Icons/ic_reorder (= 4.0.1) - - MaterialComponents/private/Icons/Base (4.0.1) - - MaterialComponents/private/Icons/ic_arrow_back (4.0.1): + - MaterialComponents/PageControl (5.0.0) + - MaterialComponents/private (5.0.0): + - MaterialComponents/private/Color (= 5.0.0) + - MaterialComponents/private/Icons (= 5.0.0) + - MaterialComponents/private/ThumbTrack (= 5.0.0) + - MaterialComponents/private/Color (5.0.0) + - MaterialComponents/private/Icons (5.0.0): + - MaterialComponents/private/Icons/Base (= 5.0.0) + - MaterialComponents/private/Icons/ic_arrow_back (= 5.0.0) + - MaterialComponents/private/Icons/ic_check (= 5.0.0) + - MaterialComponents/private/Icons/ic_check_circle (= 5.0.0) + - MaterialComponents/private/Icons/ic_chevron_right (= 5.0.0) + - MaterialComponents/private/Icons/ic_info (= 5.0.0) + - MaterialComponents/private/Icons/ic_radio_button_unchecked (= 5.0.0) + - MaterialComponents/private/Icons/ic_reorder (= 5.0.0) + - MaterialComponents/private/Icons/Base (5.0.0) + - MaterialComponents/private/Icons/ic_arrow_back (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_check (4.0.1): + - MaterialComponents/private/Icons/ic_check (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_check_circle (4.0.1): + - MaterialComponents/private/Icons/ic_check_circle (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_chevron_right (4.0.1): + - MaterialComponents/private/Icons/ic_chevron_right (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_info (4.0.1): + - MaterialComponents/private/Icons/ic_info (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_radio_button_unchecked (4.0.1): + - MaterialComponents/private/Icons/ic_radio_button_unchecked (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Icons/ic_reorder (4.0.1): + - MaterialComponents/private/Icons/ic_reorder (5.0.0): - MaterialComponents/private/Icons/Base - - MaterialComponents/private/ThumbTrack (4.0.1): + - MaterialComponents/private/ThumbTrack (5.0.0): - MaterialComponents/Ink - MaterialComponents/private/Color - MaterialComponents/ShadowElevations - MaterialComponents/ShadowLayer - - MaterialComponents/RobotoFontLoader (4.0.1): + - MaterialComponents/RobotoFontLoader (5.0.0): - MaterialComponents/FontDiskLoader - MaterialComponents/Typography - - MaterialComponents/ShadowElevations (4.0.1) - - MaterialComponents/ShadowLayer (4.0.1) - - MaterialComponents/Slider (4.0.1): + - MaterialComponents/ShadowElevations (5.0.0) + - MaterialComponents/ShadowLayer (5.0.0) + - MaterialComponents/Slider (5.0.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/SpritedAnimationView (4.0.1) - - MaterialComponents/Switch (4.0.1): + - MaterialComponents/SpritedAnimationView (5.0.0) + - MaterialComponents/Switch (5.0.0): - MaterialComponents/private/ThumbTrack - - MaterialComponents/Typography (4.0.1) + - MaterialComponents/Typography (5.0.0) DEPENDENCIES: - MaterialComponents (from `../../`) @@ -115,6 +115,6 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - MaterialComponents: 63514a0e6f6fb1d29604ded592b91e6b84238505 + MaterialComponents: 5de833a54f1cd86c5826c605d8fb00db0d5c4626 COCOAPODS: 0.39.0