diff --git a/CHANGELOG.md b/CHANGELOG.md index 81d75a72c04..440c57b8758 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,36 @@ +# 124.1.1 + +In this minor release we added a new Shadow Component and removed the Shrine demo. + +## New features + +A new Shadow Component. + +## API changes + +### Shadow + +Use the new Shadow component to create a shadow adjusted to your elevation: + + +```objc +MDCShadow *lowElevationShadow = MDCShadowForElevation(2); +MDCShadow *highElevationShadow = MDCShadowForElevation(24); +``` + +```swift +let lowElevation = MDCShadowForElevation(2) +let highElevation = MDCShadowForElevation(24) +``` + +## Component changes + +### Shadow + +* [New in-development Shadow component (not ready for public use).](https://github.com/material-components/material-components-ios/commit/ff33bab752cb518e46d2a2d4011045d081428b80) (Ben Hamilton) + +--- + # 124.0.1 This patch release fixes a bug in BottomNavigation. diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec index 0b752b8ce0e..100e8d33318 100644 --- a/MaterialComponents.podspec +++ b/MaterialComponents.podspec @@ -2,7 +2,7 @@ load 'scripts/generated/icons.rb' Pod::Spec.new do |mdc| mdc.name = "MaterialComponents" - mdc.version = "124.0.1" + mdc.version = "124.1.1" mdc.authors = "The Material Components authors." mdc.summary = "A collection of stand-alone production-ready UI libraries focused on design details." mdc.homepage = "https://github.com/material-components/material-components-ios" diff --git a/MaterialComponentsEarlGreyTests.podspec b/MaterialComponentsEarlGreyTests.podspec index 3d1c99a65d8..398c1c87fcc 100644 --- a/MaterialComponentsEarlGreyTests.podspec +++ b/MaterialComponentsEarlGreyTests.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialComponentsEarlGreyTests" - s.version = "124.0.1" + s.version = "124.1.1" s.authors = "The Material Components authors." s.summary = "This spec is an aggregate of all the Material Components EarlGrey tests." s.description = "This spec is made for use in the MDC Catalog." diff --git a/MaterialComponentsExamples.podspec b/MaterialComponentsExamples.podspec index 8ea1db233c7..eec56494346 100644 --- a/MaterialComponentsExamples.podspec +++ b/MaterialComponentsExamples.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialComponentsExamples" - s.version = "124.0.1" + s.version = "124.1.1" s.authors = "The Material Components authors." s.summary = "This spec is an aggregate of all the Material Components examples." s.description = "This spec is made for use in the MDC Catalog. Used in conjunction with CatalogByConvention we create our Material Catalog." diff --git a/MaterialComponentsSnapshotTests.podspec b/MaterialComponentsSnapshotTests.podspec index 04b5ec6c15d..0269fa4fcfb 100644 --- a/MaterialComponentsSnapshotTests.podspec +++ b/MaterialComponentsSnapshotTests.podspec @@ -53,7 +53,7 @@ end Pod::Spec.new do |s| s.name = "MaterialComponentsSnapshotTests" - s.version = "124.0.1" + s.version = "124.1.1" s.authors = "The Material Components authors." s.summary = "This spec is an aggregate of all the Material Components snapshot tests." s.homepage = "https://github.com/material-components/material-components-ios" diff --git a/VERSION b/VERSION index 2c7399bd34e..e6afbd05164 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -124.0.1 +124.1.1 diff --git a/catalog/MDCCatalog/Info.plist b/catalog/MDCCatalog/Info.plist index 7110217ce7e..36af393c5b5 100644 --- a/catalog/MDCCatalog/Info.plist +++ b/catalog/MDCCatalog/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 124.0.1 + 124.1.1 CFBundleSignature ???? CFBundleVersion - 124.0.1 + 124.1.1 LSRequiresIPhoneOS UIAppFonts diff --git a/catalog/MDCDragons/Info.plist b/catalog/MDCDragons/Info.plist index d1a660cd153..3dc3c83f70f 100644 --- a/catalog/MDCDragons/Info.plist +++ b/catalog/MDCDragons/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 124.0.1 + 124.1.1 CFBundleVersion - 124.0.1 + 124.1.1 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/catalog/MaterialCatalog/MaterialCatalog.podspec b/catalog/MaterialCatalog/MaterialCatalog.podspec index 11a9296b3f0..666c4d1b069 100644 --- a/catalog/MaterialCatalog/MaterialCatalog.podspec +++ b/catalog/MaterialCatalog/MaterialCatalog.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialCatalog" - s.version = "124.0.1" + s.version = "124.1.1" s.summary = "Helper Objective-C classes for the MDC catalog." s.description = "This spec is made for use in the MDC Catalog." s.homepage = "https://github.com/material-components/material-components-ios" diff --git a/components/LibraryInfo/src/MDCLibraryInfo.m b/components/LibraryInfo/src/MDCLibraryInfo.m index 99bbd379927..b30653b8a64 100644 --- a/components/LibraryInfo/src/MDCLibraryInfo.m +++ b/components/LibraryInfo/src/MDCLibraryInfo.m @@ -19,7 +19,7 @@ // This string is updated automatically as a part of the release process and should not be edited // manually. Do not rename this constant or change the formatting without updating the release // scripts. -static NSString* const kMDCLibraryInfoVersionString = @"124.0.1"; +static NSString* const kMDCLibraryInfoVersionString = @"124.1.1"; @implementation MDCLibraryInfo diff --git a/components/LibraryInfo/tests/unit/LibraryInfoTests.m b/components/LibraryInfo/tests/unit/LibraryInfoTests.m index 748dd4cc13b..e0fc188dce2 100644 --- a/components/LibraryInfo/tests/unit/LibraryInfoTests.m +++ b/components/LibraryInfo/tests/unit/LibraryInfoTests.m @@ -26,7 +26,7 @@ - (void)testVersionFormat { // Given // This regex pattern does the following: - // Accept: "124.0.1", etc. + // Accept: "124.1.1", etc. // Reject: "0.0.0", "1.2", "1", "-1.2.3", "Hi, I'm a version 1.2.3", "1.2.3 is my version", etc. // // Note the major version must be >= 1 since "0.0.0" is used as the version when something goes diff --git a/components/Shadow/src/MDCShadow.h b/components/Shadow/src/MDCShadow.h new file mode 100644 index 00000000000..d4a7fb3aa7f --- /dev/null +++ b/components/Shadow/src/MDCShadow.h @@ -0,0 +1,64 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + Immutable value type holding shadow metrics to apply to a view's layer. Use + `MDCShadowForElevation()` or `MDCShadowBuilder` to create this object. + */ +__attribute__((objc_subclassing_restricted)) @interface MDCShadow : NSObject + +/** CALayer.shadowOpacity */ +@property(nonatomic, readonly) CGFloat opacity; + +/** CALayer.shadowRadius */ +@property(nonatomic, readonly) CGFloat radius; + +/** CALayer.shadowOffset */ +@property(nonatomic, readonly) CGSize offset; + +- (nonnull instancetype)init NS_UNAVAILABLE; + +@end + +/** + Mutable builder to construct immutable `MDCShadow` objects. + */ +__attribute__((objc_subclassing_restricted)) @interface MDCShadowBuilder : NSObject + +/** CALayer.shadowOpacity */ +@property(nonatomic) CGFloat opacity; + +/** CALayer.shadowRadius */ +@property(nonatomic) CGFloat radius; + +/** CALayer.shadowOffset */ +@property(nonatomic) CGSize offset; + +/** Returns an immutable value type containing a snapshot of the values in this object. */ +- (nonnull MDCShadow *)build; + +@end + +/** + Default color for a Material shadow. On iOS >= 13, this is a dynamic color. + */ +FOUNDATION_EXTERN UIColor *_Nonnull MDCShadowColor(void); + +/** + Returns an `MDCShadow` representing the Material shadow metrics for the given elevation (in + points). + */ +FOUNDATION_EXTERN MDCShadow *_Nonnull MDCShadowForElevation(CGFloat elevation); diff --git a/components/Shadow/src/MDCShadow.m b/components/Shadow/src/MDCShadow.m new file mode 100644 index 00000000000..897165f1ee0 --- /dev/null +++ b/components/Shadow/src/MDCShadow.m @@ -0,0 +1,126 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCShadow.h" + +#import "MaterialAvailability.h" +#import "MDCShadow+Internal.h" + +@implementation MDCShadow + +- (instancetype)initWithOpacity:(CGFloat)opacity radius:(CGFloat)radius offset:(CGSize)offset { + self = [super init]; + if (self) { + _opacity = opacity; + _radius = radius; + _offset = offset; + } + return self; +} + +- (BOOL)isEqual:(id)other { + if (self == other) { + return YES; + } + if (![other isKindOfClass:[MDCShadow class]]) { + return NO; + } + MDCShadow *otherShadow = other; + return _opacity == otherShadow.opacity && _radius == otherShadow.radius && + CGSizeEqualToSize(_offset, otherShadow.offset); +} + +- (NSUInteger)hash { + const NSUInteger kPrime = 31; + NSUInteger result = 1; + result = result * kPrime + (NSUInteger)_opacity; + result = result * kPrime + (NSUInteger)_radius; + result = result * kPrime + (NSUInteger)(_offset.width); + result = result * kPrime + (NSUInteger)(_offset.height); + return result; +} + +@end + +@implementation MDCShadowBuilder + +- (MDCShadow *)build { + return [[MDCShadow alloc] initWithOpacity:self.opacity radius:self.radius offset:self.offset]; +} + +@end + +static UIColor *LightStyleShadowColor(void) { + static UIColor *lightStyleShadowColor; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + lightStyleShadowColor = [UIColor colorWithRed:0.235 green:0.251 blue:0.263 alpha:1]; + }); + return lightStyleShadowColor; +} + +UIColor *MDCShadowColor(void) { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(iOS 13.0, *)) { + return [UIColor colorWithDynamicProvider:^(UITraitCollection *traitCollection) { + switch (traitCollection.userInterfaceStyle) { + case UIUserInterfaceStyleUnspecified: + __attribute__((fallthrough)); + case UIUserInterfaceStyleLight: + return LightStyleShadowColor(); + case UIUserInterfaceStyleDark: + return UIColor.blackColor; + } + __builtin_unreachable(); + }]; + } +#endif // MDC_AVAILABLE_SDK_IOS(13_0) + return LightStyleShadowColor(); +} + +static int ShadowElevationToLevel(CGFloat elevation) { + if (elevation < 1) { + return 0; + } + if (elevation < 3) { + return 1; + } + if (elevation < 6) { + return 2; + } + if (elevation < 8) { + return 3; + } + if (elevation < 12) { + return 4; + } + return 5; +} + +MDCShadow *MDCShadowForElevation(CGFloat elevation) { + static NSArray *shadowLevels; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + shadowLevels = @[ + [[MDCShadow alloc] initWithOpacity:0 radius:0 offset:CGSizeMake(0, 0)], + [[MDCShadow alloc] initWithOpacity:0.43 radius:2.5 offset:CGSizeMake(0, 1)], + [[MDCShadow alloc] initWithOpacity:0.4 radius:3.25 offset:CGSizeMake(0, 1.25)], + [[MDCShadow alloc] initWithOpacity:0.34 radius:4.75 offset:CGSizeMake(0, 2.25)], + [[MDCShadow alloc] initWithOpacity:0.42 radius:6 offset:CGSizeMake(0, 3)], + [[MDCShadow alloc] initWithOpacity:0.4 radius:7.25 offset:CGSizeMake(0, 5)], + ]; + }); + int shadowLevel = ShadowElevationToLevel(elevation); + return shadowLevels[shadowLevel]; +} diff --git a/components/Shadow/src/MDCShadowConfiguring.h b/components/Shadow/src/MDCShadowConfiguring.h new file mode 100644 index 00000000000..e1910ee38f3 --- /dev/null +++ b/components/Shadow/src/MDCShadowConfiguring.h @@ -0,0 +1,87 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class MDCShadow; + +/** + Given a view and a shadow color (e.g. `MDCShadowColor()`) along with an elevation, updates + the shadow properties of `view.layer`: + + * shadowColor + * shadowOpacity + * shadowRadius + * shadowOffset + * shadowPath + + `shadowPath` will be set to the current bounds of the given view (including rounded + corners if set on view.layer). + + TODO(b/182581383): maskedCorners, cornerCurve, and cornerCurveExpansionFactor are not + yet supported. + + If `elevation` is < 1, disables the view's shadow. Otherwise, enables the shadow. + + Call this function from your `UIView` subclass's `-layoutSubviews` to update `shadowPath` + whenever the view's bounds change. + */ +FOUNDATION_EXTERN void MDCConfigureShadowForViewWithElevation(UIView *_Nonnull view, + UIColor *_Nonnull shadowColor, + CGFloat elevation) + NS_SWIFT_NAME(MDCConfigureShadow(for:color:elevation:)); + +/** + Given a view and a shadow color (e.g. `MDCShadowColor()`) along with an `MDCShadow` value, updates + the shadow properties of `view.layer`: + + * shadowColor + * shadowOpacity + * shadowRadius + * shadowOffset + * shadowPath + + `shadowPath` will be set to the current bounds of the given view (including rounded + corners if set on view.layer). + + TODO(b/182581383): maskedCorners, cornerCurve, and cornerCurveExpansionFactor are not + yet supported. + + Call this function from your `UIView` subclass's `-layoutSubviews` to update `shadowPath` + whenever the view's bounds change. + */ +FOUNDATION_EXTERN void MDCConfigureShadowForViewWithShadow(UIView *_Nonnull view, + UIColor *_Nonnull shadowColor, + MDCShadow *_Nonnull shadow) + NS_SWIFT_NAME(MDCConfigureShadow(for:color:shadow:)); + +/** + Given a view, a shadow color (e.g. `MDCShadowColor()`), an `MDCShadow` value, and a `path` in the + view's coordinate space representing the shape of the view, updates the shadow properties of + `view.layer`: + + * shadowColor + * shadowOpacity + * shadowRadius + * shadowOffset + * shadowPath + + Call this function from your `UIView` subclass's `-layoutSubviews` to update `shadowPath` + whenever the view's bounds or shape changes. + */ +FOUNDATION_EXTERN void MDCConfigureShadowForViewWithShadowAndPath(UIView *_Nonnull view, + UIColor *_Nonnull shadowColor, + MDCShadow *_Nonnull shadow, + CGPathRef _Nonnull path) + NS_SWIFT_NAME(MDCConfigureShadow(for:color:shadow:path:)); diff --git a/components/Shadow/src/MDCShadowConfiguring.m b/components/Shadow/src/MDCShadowConfiguring.m new file mode 100644 index 00000000000..c2414f19fd9 --- /dev/null +++ b/components/Shadow/src/MDCShadowConfiguring.m @@ -0,0 +1,52 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCShadowConfiguring.h" + +#import "MaterialAvailability.h" +#import "MDCShadow.h" + +void MDCConfigureShadowForViewWithElevation(UIView *view, UIColor *shadowColor, CGFloat elevation) { + MDCShadow *shadow = MDCShadowForElevation(elevation); + if (!shadow) { + view.layer.shadowColor = nil; + view.layer.shadowOpacity = 0; + view.layer.shadowRadius = 0; + view.layer.shadowOffset = CGSizeZero; + view.layer.shadowPath = nil; + return; + } + MDCConfigureShadowForViewWithShadow(view, shadowColor, shadow); +} + +void MDCConfigureShadowForViewWithShadow(UIView *view, UIColor *shadowColor, MDCShadow *shadow) { + // Support views both with and without cornerRadius set. + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:view.bounds + cornerRadius:view.layer.cornerRadius]; + MDCConfigureShadowForViewWithShadowAndPath(view, shadowColor, shadow, path.CGPath); +} + +void MDCConfigureShadowForViewWithShadowAndPath(UIView *view, UIColor *shadowColor, + MDCShadow *shadow, CGPathRef path) { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(ios 13.0, *)) { + shadowColor = [shadowColor resolvedColorWithTraitCollection:view.traitCollection]; + } +#endif // MDC_AVAILABLE_SDK_IOS(13_0) + view.layer.shadowColor = shadowColor.CGColor; + view.layer.shadowOpacity = shadow.opacity; + view.layer.shadowRadius = shadow.radius; + view.layer.shadowOffset = shadow.offset; + view.layer.shadowPath = path; +} diff --git a/components/Shadow/src/MaterialShadow.h b/components/Shadow/src/MaterialShadow.h new file mode 100644 index 00000000000..c4fb1074199 --- /dev/null +++ b/components/Shadow/src/MaterialShadow.h @@ -0,0 +1,17 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights +// Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCShadow.h" +#import "MDCShadowConfiguring.h" diff --git a/components/Shadow/src/private/MDCShadow+Internal.h b/components/Shadow/src/private/MDCShadow+Internal.h new file mode 100644 index 00000000000..8c7225d5d2f --- /dev/null +++ b/components/Shadow/src/private/MDCShadow+Internal.h @@ -0,0 +1,25 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MDCShadow.h" + +@interface MDCShadow () + +- (instancetype)initWithOpacity:(CGFloat)opacity + radius:(CGFloat)radius + offset:(CGSize)offset NS_DESIGNATED_INITIALIZER; + +@end diff --git a/components/Shadow/tests/snapshot/MDCShadowSnapshotTests.m b/components/Shadow/tests/snapshot/MDCShadowSnapshotTests.m new file mode 100644 index 00000000000..13c705e5d5b --- /dev/null +++ b/components/Shadow/tests/snapshot/MDCShadowSnapshotTests.m @@ -0,0 +1,234 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MaterialAvailability.h" +#import "MaterialShadow.h" +#import "MaterialSnapshot.h" + +/** + Returns a dynamic color which is green in light mode and red in dark mode. + */ +static UIColor *MDCTestDynamicShadowColor(void) { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(iOS 13.0, *)) { + return [UIColor colorWithDynamicProvider:^(UITraitCollection *traitCollection) { + switch (traitCollection.userInterfaceStyle) { + case UIUserInterfaceStyleUnspecified: + __attribute__((fallthrough)); + case UIUserInterfaceStyleLight: + return UIColor.greenColor; + case UIUserInterfaceStyleDark: + return UIColor.redColor; + } + __builtin_unreachable(); + }]; + } +#endif // MDC_AVAILABLE_SDK_IOS(13_0) + // Should not be reached (do not invoke this function on iOS < 13). + abort(); +} + +/** + A UIView that supports rendering a shadow and overriding its traitCollection. + */ +@interface MDCShadowTestView : UIView +@property(nonatomic) CGFloat shadowElevation; +@property(nonatomic, strong, nonnull) UIColor *shadowColor; +@property(nonatomic, strong, nullable) CAShapeLayer *shapeLayer; +@property(nonatomic, nullable) CGPathRef shapePath; +@property(nonatomic, strong, nullable) UITraitCollection *traitCollectionOverride; +@end + +@implementation MDCShadowTestView + +@synthesize shapePath = _shapePath; + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + _shadowColor = MDCShadowColor(); + } + return self; +} + +- (UITraitCollection *)traitCollection { + return self.traitCollectionOverride ?: [super traitCollection]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + if (self.shapePath) { + self.backgroundColor = nil; + MDCShadow *shadow = MDCShadowForElevation(self.shadowElevation); + MDCConfigureShadowForViewWithShadowAndPath(self, self.shadowColor, shadow, self.shapePath); + } else { + self.backgroundColor = UIColor.whiteColor; + MDCConfigureShadowForViewWithElevation(self, self.shadowColor, self.shadowElevation); + } +} + +- (void)setShapePath:(CGPathRef)shapePath { + if (_shapeLayer) { + [_shapeLayer removeFromSuperlayer]; + _shapeLayer = nil; + } + if (shapePath) { + // Note we cannot use self.layer.mask here, as it would mask the shadow. + CAShapeLayer *shapeLayer = CAShapeLayer.layer; + shapeLayer.fillColor = UIColor.whiteColor.CGColor; + shapeLayer.path = shapePath; + [self.layer addSublayer:shapeLayer]; + } + _shapePath = shapePath; +} + +@end + +/** + Snapshot tests for MDCShadow functions. + */ +@interface MDCShadowSnapshotTests : MDCSnapshotTestCase + +@property(nonatomic, strong) MDCShadowTestView *view; + +@end + +@implementation MDCShadowSnapshotTests + +- (void)setUp { + [super setUp]; + self.view = [[MDCShadowTestView alloc] initWithFrame:CGRectMake(0, 0, 80, 80)]; +} + +#pragma mark - Helpers + +- (void)generateSnapshotAndVerifyForView:(UIView *)view { + UIView *snapshotView = [view mdc_addToBackgroundView]; + [self snapshotVerifyView:snapshotView]; +} + +- (void)generateSnapshotForIOS13AndVerifyForView:(UIView *)view { + UIView *snapshotView = [view mdc_addToBackgroundView]; + [self snapshotVerifyViewForIOS13:snapshotView]; +} + +#pragma mark - Tests + +- (void)testShadowWithZeroElevationShouldNotRenderShadow { + // Given + self.view.shadowElevation = 0; + + // Then + [self generateSnapshotAndVerifyForView:self.view]; +} + +- (void)testShadowWithLowElevationShouldRenderShadow { + // Given + self.view.shadowElevation = 2; + + // Then + [self generateSnapshotAndVerifyForView:self.view]; +} + +- (void)testShadowWithHighElevationShouldRenderShadow { + // Given + self.view.shadowElevation = 10; + + // Then + [self generateSnapshotAndVerifyForView:self.view]; +} + +- (void)testShadowWithLowElevationShouldUpdateShadowOnBoundsChange { + // Given + self.view.shadowElevation = 2; + + // When + [self.view layoutIfNeeded]; + self.view.bounds = CGRectMake(0, 0, 180, 20); + + // Then + [self generateSnapshotAndVerifyForView:self.view]; +} + +- (void)testShadowWithLowElevationAndCornerRadiusShouldRenderRoundedShadow { + // Given + self.view.shadowElevation = 2; + self.view.layer.cornerRadius = 3; + + // Then + [self generateSnapshotAndVerifyForView:self.view]; +} + +- (void)testShadowWithLowElevationAndCornerRadiusShouldUpdateShadowOnBoundsChange { + // Given + self.view.shadowElevation = 2; + self.view.layer.cornerRadius = 3; + + // When + [self.view layoutIfNeeded]; + self.view.bounds = CGRectMake(0, 0, 180, 20); + + // Then + [self generateSnapshotAndVerifyForView:self.view]; +} + +- (void)testShadowWithLowElevationAndShapeLayerShouldRenderShapedShadow { + // Given + self.view.shadowElevation = 2; + UIBezierPath *triangleBezierPath = UIBezierPath.bezierPath; + [triangleBezierPath moveToPoint:CGPointMake(40, 0)]; + [triangleBezierPath addLineToPoint:CGPointMake(80, 80)]; + [triangleBezierPath addLineToPoint:CGPointMake(0, 80)]; + [triangleBezierPath closePath]; + self.view.shapePath = triangleBezierPath.CGPath; + + // Then + [self generateSnapshotAndVerifyForView:self.view]; +} + +- (void)testCustomShadowColorInLightModeShouldBeGreen { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(iOS 13.0, *)) { + // Given + self.view.shadowElevation = 2; + self.view.shadowColor = MDCTestDynamicShadowColor(); + + // When + self.view.traitCollectionOverride = + [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]; + + // Then + [self generateSnapshotForIOS13AndVerifyForView:self.view]; + } +#endif // MDC_AVAILABLE_SDK_IOS(13_0) +} + +- (void)testCustomShadowColorInDarkModeShouldBeRed { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(iOS 13.0, *)) { + // Given + self.view.shadowElevation = 2; + self.view.shadowColor = MDCTestDynamicShadowColor(); + + // When + self.view.traitCollectionOverride = + [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]; + + // Then + [self generateSnapshotForIOS13AndVerifyForView:self.view]; + } +#endif // MDC_AVAILABLE_SDK_IOS(13_0) +} + +@end diff --git a/components/Shadow/tests/unit/MDCShadowTests.m b/components/Shadow/tests/unit/MDCShadowTests.m new file mode 100644 index 00000000000..68b715250f1 --- /dev/null +++ b/components/Shadow/tests/unit/MDCShadowTests.m @@ -0,0 +1,90 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MDCShadow.h" + +@interface MDCShadowTests : XCTestCase +@end + +@implementation MDCShadowTests + +- (void)testZeroElevationShouldReturnEmptyShadow { + // Given + MDCShadow *shadow = MDCShadowForElevation(0); + + // Then + XCTAssertEqual(shadow.opacity, 0); + XCTAssertEqual(shadow.radius, 0); + XCTAssertEqual(shadow.offset.width + shadow.offset.height, 0); +} + +- (void)testLowElevationShouldReturnShadow { + // Given + MDCShadow *shadow = MDCShadowForElevation(2); + + // Then + XCTAssertGreaterThan(shadow.opacity, 0); + XCTAssertGreaterThan(shadow.radius, 0); + XCTAssertGreaterThan(shadow.offset.width + shadow.offset.height, 0); +} + +- (void)testHighElevationShouldReturnShadow { + // Given + MDCShadow *shadow = MDCShadowForElevation(24); + + // Then + XCTAssertGreaterThan(shadow.opacity, 0); + XCTAssertGreaterThan(shadow.radius, 0); + XCTAssertGreaterThan(shadow.offset.width + shadow.offset.height, 0); +} + +- (void)testSameElevationShouldBeEqualToSelf { + // Given + MDCShadow *lowElevationShadowA = MDCShadowForElevation(2); + MDCShadow *lowElevationShadowB = MDCShadowForElevation(2); + + // Then + XCTAssertEqualObjects(lowElevationShadowA, lowElevationShadowB); +} + +- (void)testLowElevationShouldReturnDifferentShadowFromHighElevation { + // Given + MDCShadow *lowElevationShadow = MDCShadowForElevation(2); + MDCShadow *highElevationShadow = MDCShadowForElevation(24); + + // Then + XCTAssertNotEqualObjects(lowElevationShadow, highElevationShadow); +} + +- (void)testBuilderReturnsExpectedValues { + // Given + MDCShadowBuilder *shadowBuilder = [[MDCShadowBuilder alloc] init]; + + // When + shadowBuilder.opacity = 42; + shadowBuilder.radius = 23; + shadowBuilder.offset = CGSizeMake(1, 2); + + MDCShadow *shadow = [shadowBuilder build]; + + // Then + XCTAssertEqualWithAccuracy(shadow.opacity, 42, 1e-6); + XCTAssertEqualWithAccuracy(shadow.radius, 23, 1e-6); + XCTAssertEqualWithAccuracy(shadow.offset.width, 1, 1e-6); + XCTAssertEqualWithAccuracy(shadow.offset.height, 2, 1e-6); +} + +@end diff --git a/demos/README.md b/demos/README.md index 7eb5b2f8cdb..1fbe0f8a313 100644 --- a/demos/README.md +++ b/demos/README.md @@ -2,7 +2,6 @@ The following demo apps can be found in this directory: -- [Shrine](Shrine/) - a demo shopping app, incorporating a flexible header, custom typography, and collections - [Bare](Bare/) - a skeleton example app used to measure the size of Material Components for iOS ## Trying out Material Components diff --git a/demos/Shrine/.swiftlint.yml b/demos/Shrine/.swiftlint.yml deleted file mode 120000 index e5c8a05550f..00000000000 --- a/demos/Shrine/.swiftlint.yml +++ /dev/null @@ -1 +0,0 @@ -../../.swiftlint.yml \ No newline at end of file diff --git a/demos/Shrine/Podfile b/demos/Shrine/Podfile deleted file mode 100644 index 9c0863bc126..00000000000 --- a/demos/Shrine/Podfile +++ /dev/null @@ -1,8 +0,0 @@ -platform :ios, "10.0" -use_frameworks! - -target 'Shrine' do - pod 'MaterialComponents/AppBar', :path => '../../' - pod 'MaterialComponents/PageControl', :path => '../../' - pod 'RemoteImageServiceForMDCDemos', :path => '../supplemental' -end diff --git a/demos/Shrine/Shrine.xcodeproj/project.pbxproj b/demos/Shrine/Shrine.xcodeproj/project.pbxproj deleted file mode 100644 index 51dcba8c602..00000000000 --- a/demos/Shrine/Shrine.xcodeproj/project.pbxproj +++ /dev/null @@ -1,419 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 6B0C01BA37AC5D6B39F49F16 /* Pods_Shrine.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E79DB3BB3A6DCA8E56C685D /* Pods_Shrine.framework */; }; - CBAF7A311E1577380022A08F /* ShrineHeaderPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBAF7A301E1577380022A08F /* ShrineHeaderPage.swift */; }; - DE064E731C83D20E00B17776 /* ShrineFlexibleHeaderContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE064E721C83D20E00B17776 /* ShrineFlexibleHeaderContainerViewController.swift */; }; - DE34DEBB1C88E90000E04B65 /* ShrineInkOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE34DEBA1C88E90000E04B65 /* ShrineInkOverlay.swift */; }; - DE34DEBF1C8A34B100E04B65 /* ShrineHeaderContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE34DEBE1C8A34B100E04B65 /* ShrineHeaderContentView.swift */; }; - DE3CFD831C84FE0B006BACA7 /* products.json in Resources */ = {isa = PBXBuildFile; fileRef = DE3CFD821C84FE0B006BACA7 /* products.json */; }; - DE3CFD851C873A07006BACA7 /* ShrineDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE3CFD841C873A07006BACA7 /* ShrineDetailViewController.swift */; }; - DE5D2A481C81068100C9C650 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE5D2A471C81068100C9C650 /* AppDelegate.swift */; }; - DE5D2A4F1C81068100C9C650 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DE5D2A4E1C81068100C9C650 /* Assets.xcassets */; }; - DE5D2A521C81068100C9C650 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DE5D2A501C81068100C9C650 /* LaunchScreen.storyboard */; }; - DE5D2A5A1C81092D00C9C650 /* ShrineCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE5D2A591C81092D00C9C650 /* ShrineCollectionViewCell.swift */; }; - DE5D2A5C1C810E6600C9C650 /* ShrineCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE5D2A5B1C810E6600C9C650 /* ShrineCollectionViewController.swift */; }; - DE6887EA1C849C7C0074A8C5 /* AbrilFatface-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DE6887E91C849C7C0074A8C5 /* AbrilFatface-Regular.ttf */; }; - DE6887F31C84FCD40074A8C5 /* ShrineData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6887F21C84FCD40074A8C5 /* ShrineData.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 1C7F50E40E35B48C29C884F8 /* Pods-Shrine.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Shrine.release.xcconfig"; path = "Pods/Target Support Files/Pods-Shrine/Pods-Shrine.release.xcconfig"; sourceTree = ""; }; - 2E79DB3BB3A6DCA8E56C685D /* Pods_Shrine.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Shrine.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 3D22DA7C0F576B707C1CA5BE /* Pods-Shrine.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Shrine.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Shrine/Pods-Shrine.debug.xcconfig"; sourceTree = ""; }; - CBAF7A301E1577380022A08F /* ShrineHeaderPage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShrineHeaderPage.swift; sourceTree = ""; }; - DE064E721C83D20E00B17776 /* ShrineFlexibleHeaderContainerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShrineFlexibleHeaderContainerViewController.swift; sourceTree = ""; }; - DE34DEBA1C88E90000E04B65 /* ShrineInkOverlay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShrineInkOverlay.swift; sourceTree = ""; }; - DE34DEBE1C8A34B100E04B65 /* ShrineHeaderContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShrineHeaderContentView.swift; sourceTree = ""; }; - DE3CFD821C84FE0B006BACA7 /* products.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = products.json; path = Shrine/Resources/products.json; sourceTree = ""; }; - DE3CFD841C873A07006BACA7 /* ShrineDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShrineDetailViewController.swift; sourceTree = ""; }; - DE5D2A441C81068100C9C650 /* Shrine.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Shrine.app; sourceTree = BUILT_PRODUCTS_DIR; }; - DE5D2A471C81068100C9C650 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - DE5D2A4E1C81068100C9C650 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - DE5D2A511C81068100C9C650 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - DE5D2A531C81068100C9C650 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - DE5D2A591C81092D00C9C650 /* ShrineCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShrineCollectionViewCell.swift; sourceTree = ""; }; - DE5D2A5B1C810E6600C9C650 /* ShrineCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShrineCollectionViewController.swift; sourceTree = ""; }; - DE6887E91C849C7C0074A8C5 /* AbrilFatface-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "AbrilFatface-Regular.ttf"; path = "Shrine/Resources/AbrilFatface-Regular.ttf"; sourceTree = ""; }; - DE6887EE1C849F7E0074A8C5 /* Shrine-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Shrine-Bridging-Header.h"; sourceTree = ""; }; - DE6887F21C84FCD40074A8C5 /* ShrineData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShrineData.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - DE5D2A411C81068100C9C650 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 6B0C01BA37AC5D6B39F49F16 /* Pods_Shrine.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 642D1F1EA5C73400CE64F5A2 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 2E79DB3BB3A6DCA8E56C685D /* Pods_Shrine.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 7DA6FBA546AC6B885D4CA01F /* Pods */ = { - isa = PBXGroup; - children = ( - 3D22DA7C0F576B707C1CA5BE /* Pods-Shrine.debug.xcconfig */, - 1C7F50E40E35B48C29C884F8 /* Pods-Shrine.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; - DE5D2A3B1C81068100C9C650 = { - isa = PBXGroup; - children = ( - DE5D2A461C81068100C9C650 /* Shrine */, - DE5D2A451C81068100C9C650 /* Products */, - DE6887E81C849C6C0074A8C5 /* Resources */, - 7DA6FBA546AC6B885D4CA01F /* Pods */, - 642D1F1EA5C73400CE64F5A2 /* Frameworks */, - ); - sourceTree = ""; - }; - DE5D2A451C81068100C9C650 /* Products */ = { - isa = PBXGroup; - children = ( - DE5D2A441C81068100C9C650 /* Shrine.app */, - ); - name = Products; - sourceTree = ""; - }; - DE5D2A461C81068100C9C650 /* Shrine */ = { - isa = PBXGroup; - children = ( - DE5D2A471C81068100C9C650 /* AppDelegate.swift */, - DE5D2A4E1C81068100C9C650 /* Assets.xcassets */, - DE5D2A531C81068100C9C650 /* Info.plist */, - DE5D2A501C81068100C9C650 /* LaunchScreen.storyboard */, - DE6887EE1C849F7E0074A8C5 /* Shrine-Bridging-Header.h */, - DE5D2A591C81092D00C9C650 /* ShrineCollectionViewCell.swift */, - DE5D2A5B1C810E6600C9C650 /* ShrineCollectionViewController.swift */, - DE6887F21C84FCD40074A8C5 /* ShrineData.swift */, - DE3CFD841C873A07006BACA7 /* ShrineDetailViewController.swift */, - DE064E721C83D20E00B17776 /* ShrineFlexibleHeaderContainerViewController.swift */, - DE34DEBE1C8A34B100E04B65 /* ShrineHeaderContentView.swift */, - DE34DEBA1C88E90000E04B65 /* ShrineInkOverlay.swift */, - CBAF7A301E1577380022A08F /* ShrineHeaderPage.swift */, - ); - path = Shrine; - sourceTree = ""; - }; - DE6887E81C849C6C0074A8C5 /* Resources */ = { - isa = PBXGroup; - children = ( - DE6887E91C849C7C0074A8C5 /* AbrilFatface-Regular.ttf */, - DE3CFD821C84FE0B006BACA7 /* products.json */, - ); - name = Resources; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - DE5D2A431C81068100C9C650 /* Shrine */ = { - isa = PBXNativeTarget; - buildConfigurationList = DE5D2A561C81068100C9C650 /* Build configuration list for PBXNativeTarget "Shrine" */; - buildPhases = ( - BD04A4A94E7A074ACE45ED21 /* [CP] Check Pods Manifest.lock */, - DE5D2A401C81068100C9C650 /* Sources */, - DE5D2A411C81068100C9C650 /* Frameworks */, - DE5D2A421C81068100C9C650 /* Resources */, - D1F846F8DE8314F37D7BCFC1 /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Shrine; - productName = Shrine; - productReference = DE5D2A441C81068100C9C650 /* Shrine.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - DE5D2A3C1C81068100C9C650 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0800; - ORGANIZATIONNAME = "Junius Gunaratne"; - TargetAttributes = { - DE5D2A431C81068100C9C650 = { - CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 1010; - ProvisioningStyle = Automatic; - }; - }; - }; - buildConfigurationList = DE5D2A3F1C81068100C9C650 /* Build configuration list for PBXProject "Shrine" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = DE5D2A3B1C81068100C9C650; - productRefGroup = DE5D2A451C81068100C9C650 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - DE5D2A431C81068100C9C650 /* Shrine */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - DE5D2A421C81068100C9C650 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - DE3CFD831C84FE0B006BACA7 /* products.json in Resources */, - DE5D2A521C81068100C9C650 /* LaunchScreen.storyboard in Resources */, - DE5D2A4F1C81068100C9C650 /* Assets.xcassets in Resources */, - DE6887EA1C849C7C0074A8C5 /* AbrilFatface-Regular.ttf in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - BD04A4A94E7A074ACE45ED21 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Shrine-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - D1F846F8DE8314F37D7BCFC1 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Shrine/Pods-Shrine-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/MDFInternationalization/MDFInternationalization.framework", - "${BUILT_PRODUCTS_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework", - "${BUILT_PRODUCTS_DIR}/MaterialComponents/MaterialComponents.framework", - "${BUILT_PRODUCTS_DIR}/RemoteImageServiceForMDCDemos/RemoteImageServiceForMDCDemos.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MDFInternationalization.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MDFTextAccessibility.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MaterialComponents.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RemoteImageServiceForMDCDemos.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Shrine/Pods-Shrine-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - DE5D2A401C81068100C9C650 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - DE3CFD851C873A07006BACA7 /* ShrineDetailViewController.swift in Sources */, - DE5D2A5A1C81092D00C9C650 /* ShrineCollectionViewCell.swift in Sources */, - DE5D2A481C81068100C9C650 /* AppDelegate.swift in Sources */, - DE5D2A5C1C810E6600C9C650 /* ShrineCollectionViewController.swift in Sources */, - DE34DEBF1C8A34B100E04B65 /* ShrineHeaderContentView.swift in Sources */, - CBAF7A311E1577380022A08F /* ShrineHeaderPage.swift in Sources */, - DE064E731C83D20E00B17776 /* ShrineFlexibleHeaderContainerViewController.swift in Sources */, - DE6887F31C84FCD40074A8C5 /* ShrineData.swift in Sources */, - DE34DEBB1C88E90000E04B65 /* ShrineInkOverlay.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - DE5D2A501C81068100C9C650 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - DE5D2A511C81068100C9C650 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - DE5D2A541C81068100C9C650 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.2; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - DE5D2A551C81068100C9C650 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.2; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - DE5D2A571C81068100C9C650 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3D22DA7C0F576B707C1CA5BE /* Pods-Shrine.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = Shrine/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = google.com.Shrine; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Shrine/Shrine-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; - }; - name = Debug; - }; - DE5D2A581C81068100C9C650 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 1C7F50E40E35B48C29C884F8 /* Pods-Shrine.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = Shrine/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = google.com.Shrine; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Shrine/Shrine-Bridging-Header.h"; - SWIFT_VERSION = 4.2; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - DE5D2A3F1C81068100C9C650 /* Build configuration list for PBXProject "Shrine" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - DE5D2A541C81068100C9C650 /* Debug */, - DE5D2A551C81068100C9C650 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - DE5D2A561C81068100C9C650 /* Build configuration list for PBXNativeTarget "Shrine" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - DE5D2A571C81068100C9C650 /* Debug */, - DE5D2A581C81068100C9C650 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = DE5D2A3C1C81068100C9C650 /* Project object */; -} diff --git a/demos/Shrine/Shrine.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/demos/Shrine/Shrine.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 964895fa9db..00000000000 --- a/demos/Shrine/Shrine.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/demos/Shrine/Shrine.xcodeproj/xcshareddata/xcschemes/Shrine.xcscheme b/demos/Shrine/Shrine.xcodeproj/xcshareddata/xcschemes/Shrine.xcscheme deleted file mode 100644 index b3d4345a236..00000000000 --- a/demos/Shrine/Shrine.xcodeproj/xcshareddata/xcschemes/Shrine.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demos/Shrine/Shrine.xcworkspace/contents.xcworkspacedata b/demos/Shrine/Shrine.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 2cd146d4c0d..00000000000 --- a/demos/Shrine/Shrine.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/demos/Shrine/Shrine/AppDelegate.swift b/demos/Shrine/Shrine/AppDelegate.swift deleted file mode 100644 index 5b141797897..00000000000 --- a/demos/Shrine/Shrine/AppDelegate.swift +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import UIKit -import MaterialComponents.MaterialIcons_ic_arrow_back - -@UIApplicationMain -class AppDelegate: UIResponder, UIApplicationDelegate { - - var window: UIWindow? - - func application(_ application: UIApplication, didFinishLaunchingWithOptions - launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - window = UIWindow(frame: UIScreen.main.bounds) - let flexHeadVC = ShrineFlexibleHeaderContainerViewController() - window?.rootViewController = flexHeadVC - window?.makeKeyAndVisible() - - MDCIcons.ic_arrow_backUseNewStyle(true) - - return true - } - -} diff --git a/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/Contents.json b/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index e21c8528d8d..00000000000 --- a/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "logo2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "logo3x.png", - "scale" : "3x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "logo_76.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "logo_76_2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "logo_167.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo2x.png b/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo2x.png deleted file mode 100644 index c72c0c58d91..00000000000 Binary files a/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo2x.png and /dev/null differ diff --git a/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo3x.png b/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo3x.png deleted file mode 100644 index 01a53b68a9d..00000000000 Binary files a/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo3x.png and /dev/null differ diff --git a/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo_167.png b/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo_167.png deleted file mode 100644 index 8843740fbcb..00000000000 Binary files a/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo_167.png and /dev/null differ diff --git a/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo_76.png b/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo_76.png deleted file mode 100644 index b55437f5471..00000000000 Binary files a/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo_76.png and /dev/null differ diff --git a/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo_76_2x.png b/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo_76_2x.png deleted file mode 100644 index d36c2e41e74..00000000000 Binary files a/demos/Shrine/Shrine/Assets.xcassets/AppIcon.appiconset/logo_76_2x.png and /dev/null differ diff --git a/demos/Shrine/Shrine/Assets.xcassets/Contents.json b/demos/Shrine/Shrine/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164c918..00000000000 --- a/demos/Shrine/Shrine/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/demos/Shrine/Shrine/Assets.xcassets/ShrineLaunchLogo.imageset/Contents.json b/demos/Shrine/Shrine/Assets.xcassets/ShrineLaunchLogo.imageset/Contents.json deleted file mode 100644 index 99786072e52..00000000000 --- a/demos/Shrine/Shrine/Assets.xcassets/ShrineLaunchLogo.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Shrine_logo_launchscreen.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/demos/Shrine/Shrine/Assets.xcassets/ShrineLaunchLogo.imageset/Shrine_logo_launchscreen.pdf b/demos/Shrine/Shrine/Assets.xcassets/ShrineLaunchLogo.imageset/Shrine_logo_launchscreen.pdf deleted file mode 100644 index beb3e1e2ff9..00000000000 Binary files a/demos/Shrine/Shrine/Assets.xcassets/ShrineLaunchLogo.imageset/Shrine_logo_launchscreen.pdf and /dev/null differ diff --git a/demos/Shrine/Shrine/Assets.xcassets/ShrineLogo.imageset/Contents.json b/demos/Shrine/Shrine/Assets.xcassets/ShrineLogo.imageset/Contents.json deleted file mode 100644 index 2d8fc3618c2..00000000000 --- a/demos/Shrine/Shrine/Assets.xcassets/ShrineLogo.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "shrine_logo_withicon.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/demos/Shrine/Shrine/Assets.xcassets/ShrineLogo.imageset/shrine_logo_withicon.pdf b/demos/Shrine/Shrine/Assets.xcassets/ShrineLogo.imageset/shrine_logo_withicon.pdf deleted file mode 100644 index a16bedec7dd..00000000000 Binary files a/demos/Shrine/Shrine/Assets.xcassets/ShrineLogo.imageset/shrine_logo_withicon.pdf and /dev/null differ diff --git a/demos/Shrine/Shrine/Assets.xcassets/ShrineTextLogo.imageset/Contents.json b/demos/Shrine/Shrine/Assets.xcassets/ShrineTextLogo.imageset/Contents.json deleted file mode 100644 index 35488c87ac4..00000000000 --- a/demos/Shrine/Shrine/Assets.xcassets/ShrineTextLogo.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "shrine_text_logo.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/demos/Shrine/Shrine/Assets.xcassets/ShrineTextLogo.imageset/shrine_text_logo.pdf b/demos/Shrine/Shrine/Assets.xcassets/ShrineTextLogo.imageset/shrine_text_logo.pdf deleted file mode 100644 index 4842ea21dd9..00000000000 Binary files a/demos/Shrine/Shrine/Assets.xcassets/ShrineTextLogo.imageset/shrine_text_logo.pdf and /dev/null differ diff --git a/demos/Shrine/Shrine/Base.lproj/LaunchScreen.storyboard b/demos/Shrine/Shrine/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 2c00847056b..00000000000 --- a/demos/Shrine/Shrine/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demos/Shrine/Shrine/Info.plist b/demos/Shrine/Shrine/Info.plist deleted file mode 100644 index 330b85aa31f..00000000000 --- a/demos/Shrine/Shrine/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - com.google.mdc-shrine - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UIAppFonts - - AbrilFatface-Regular.ttf - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/demos/Shrine/Shrine/Resources/AbrilFatface-Regular.ttf b/demos/Shrine/Shrine/Resources/AbrilFatface-Regular.ttf deleted file mode 100644 index e761f7b9c3c..00000000000 Binary files a/demos/Shrine/Shrine/Resources/AbrilFatface-Regular.ttf and /dev/null differ diff --git a/demos/Shrine/Shrine/Resources/OFL.txt b/demos/Shrine/Shrine/Resources/OFL.txt deleted file mode 100644 index 3ff92bb266f..00000000000 --- a/demos/Shrine/Shrine/Resources/OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright (c) 2011, TypeTogether (www.type-together.com), -with Reserved Font Names "Abril" and "Abril Fatface" -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/demos/Shrine/Shrine/Resources/products.json b/demos/Shrine/Shrine/Resources/products.json deleted file mode 100644 index f278fd87895..00000000000 --- a/demos/Shrine/Shrine/Resources/products.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "products": [{ - "title": "Perfect Goldfish \nBowl", - "image": "fish_bowl.png", - "price": "$25", - "description": "The Perfect Bowl Co makes the best bowls for just about anything you can think of. This Perfect Goldfish Bowl holds water and fish perfectly. Looks great in living rooms. Keep out of reach from cats." - }, { - "title": "Green Slip-ons", - "image": "green-shoes.png", - "price": "$75", - "description": "Feetsy has been making extraordinary slip-ons for decades. With each pair of shoes purchased Feetsy donates a pair to those in need. Buy yourself a pair, buy someone else a pair. Very Comfortable." - }, { - "title": "Teapot", - "image": "teapot.png", - "price": "$210", - "description": "Impress your guests with Teapot by Kitchen Stuff. Teapot holds extremely hot liquids and pours them from the spout. Use the handle, shown on the left, so your fingers don’t get burnt while pouring." - }, { - "title": "Beachball", - "image": "beachball.png", - "price": "$25", - "description": "Are you at a baseball game and feeling bored? At a pool party and looking for a laugh? Do you need something to take your anger out on? Beachball, by inflatable fun, is the perfect outlet." - }, { - "title": "Clock", - "image": "clock.png", - "price": "$120", - "description": "Timekeeper Co makes clocks that tell time precisely. Clock is very simple to use, set the time using your phone, hang it, and viola! You’ll never be late again." - }, { - "title": "Red Popsicle", - "image": "popsicle.png", - "price": "$5", - "description": "Looks can be deceiving. This Red Popsicle comes in a wide variety of flavors, including strawberry, that burst as soon as it hits the mouth. Red Popsicles melt slow, so savor the flavor." - }, { - "title": "Lime Flippers", - "image": "flippers.png", - "price": "$25", - "description": "Flippers are a nice tool to have when you’re being chased by an oversized sea turtle. Never get caught again with these fast water shoes. You’re like a fish, but more graceful." - }, { - "title": "Surfboard", - "image": "surfboard.png", - "price": "$300", - "description": "Who says you can’t walk on water? With Surfboard, by Surfboard Supply, you can fly on water. This beast is fast and handles like a porsche. Hang Ten Bro!" - }, { - "title": "Backpack", - "image": "backpack.png", - "price": "$150", - "description": "This backpack by Bags ‘n’ stuff can hold just about anything: a laptop, a pen, a protractor, notebooks, small animals, plugs for your devices, sunglasses, gym clothes, shoes, gloves, two kittens, and even lunch!" - }, { - "title": "Sunglasses", - "image": "sunnies.png", - "price": "$100", - "description": "Be an optimist. Carry Sunglasses with you at all times. All Tints and Shades products come with polarized lenses and super duper UV protection so you can look at the sun for however long you want. Sunglasses make you look cool, wear them." - }, { - "title": "Red Lipstick Set", - "image": "lipstick.png", - "price": "$25", - "description": "Trying to find the perfect shade to match your mood? Try no longer. Red Lipstick Set by StickLips has you covered for those nights when you need to get out, or even if you’re just headed to work." - }, { - "title": "Dipped Brush", - "image": "brush.png", - "price": "$15", - "description": "WeDipIt does it again. This handle dipped 4 inch brush is a perfect for painting 4 inch lines, or coloring in big areas with paint. Just be sure you don’t drop it in a bucket of red paint, then it won’t look dipped anymore." - }], - "shops": [{ "avatar": "ali-connors.png", - "shop": "Ali's shop", - "description": "Ali Connor’s makes custom goods for folks of all shapes and sizes made by hand and sometimes by machine, but always with love and care. Custom orders are available upon request if you need something extra special." }, - { "avatar": "sandra-adams.jpg", - "shop": "Sandra's shop", - "description": "Sandra specializes in furniture, beauty and travel products with a classic vibe. Custom orders are available if you’re looking for a certain color or material." }, - { "avatar": "zach.jpg", - "shop": "Zach's shop", - "description": "Zach Hanson sources housewares and styles from just about everywhere. Super cool and extra awesome all of his shop’s goods are handmade with love." }, - { "avatar": "peter-carlsson.png", - "shop": "Peter's shop", - "description": "Peter makes great stuff for awesome people like you. Super cool and extra awesome all of his shop’s goods are handmade with love. Custom orders are available upon request if you need something extra special." }, - { "avatar": "16c477b.jpg", - "shop": "Stella's shop", - "description": "Stella sells awesome stuff at lovely prices. made by hand and sometimes by machine, but always with love and care. Custom orders are available upon request if you need something extra special." }, - { "avatar": "ali-connors.png", - "shop": "Ali's shop", - "description": "Ali Connor’s makes custom goods for folks of all shapes and sizes made by hand and sometimes by machine, but always with love and care. Custom orders are available upon request if you need something extra special." }, - { "avatar": "sandra-adams.jpg", - "shop": "Sandra's shop", - "description": "Sandra specializes in furniture, beauty and travel products with a classic vibe. Custom orders are available if you’re looking for a certain color or material." }, - { "avatar": "zach.jpg", - "shop": "Zach's shop", - "description": "Zach Hanson sources housewares and styles from just about everywhere. Super cool and extra awesome all of his shop’s goods are handmade with love." }, - { "avatar": "peter-carlsson.png", - "shop": "Peter's shop", - "description": "Peter makes great stuff for awesome people like you. Super cool and extra awesome all of his shop’s goods are handmade with love. Custom orders are available upon request if you need something extra special." }, - { "avatar": "peter-carlsson.png", - "shop": "Peter's shop", - "description": "Peter makes great stuff for awesome people like you. Super cool and extra awesome all of his shop’s goods are handmade with love. Custom orders are available upon request if you need something extra special." }, - { "avatar": "16c477b.jpg", - "shop": "Stella's shop", - "description": "Stella sells awesome stuff at lovely prices. made by hand and sometimes by machine, but always with love and care. Custom orders are available upon request if you need something extra special." }, - { "avatar": "16c477b.jpg", - "shop": "Stella's shop", - "description": "Stella sells awesome stuff at lovely prices. made by hand and sometimes by machine, but always with love and care. Custom orders are available upon request if you need something extra special." } - ] -} diff --git a/demos/Shrine/Shrine/Shrine-Bridging-Header.h b/demos/Shrine/Shrine/Shrine-Bridging-Header.h deleted file mode 100644 index d87b745dafa..00000000000 --- a/demos/Shrine/Shrine/Shrine-Bridging-Header.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ diff --git a/demos/Shrine/Shrine/ShrineCollectionViewCell.swift b/demos/Shrine/Shrine/ShrineCollectionViewCell.swift deleted file mode 100644 index 683a4dda08c..00000000000 --- a/demos/Shrine/Shrine/ShrineCollectionViewCell.swift +++ /dev/null @@ -1,125 +0,0 @@ -/* - Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import UIKit -import RemoteImageServiceForMDCDemos - -class ShrineCollectionViewCell: UICollectionViewCell { - - var imageView = UIImageView() - var avatar = UIImageView() - var remoteImageService = RemoteImageService() - - fileprivate var label = UILabel() - fileprivate var labelAvatar = UILabel() - fileprivate var labelPrice = UILabel() - fileprivate var shrineInkOverlay = ShrineInkOverlay() - fileprivate var cellContent = UIView() - - override init(frame: CGRect) { - super.init(frame: frame) - cellContent.frame = bounds - cellContent.backgroundColor = UIColor.white - cellContent.autoresizingMask = [.flexibleWidth, .flexibleHeight] - cellContent.clipsToBounds = true - - imageView.contentMode = .scaleAspectFill - imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - cellContent.addSubview(imageView) - - avatar.layer.cornerRadius = 12 - avatar.backgroundColor = UIColor.lightGray - avatar.clipsToBounds = true - cellContent.addSubview(avatar) - - labelAvatar.lineBreakMode = .byWordWrapping - labelAvatar.textColor = UIColor.gray - labelAvatar.numberOfLines = 1 - labelAvatar.font = UIFont(name: "Helvetica", size: 14) - cellContent.addSubview(labelAvatar) - - labelPrice.lineBreakMode = .byWordWrapping - labelPrice.font = UIFont(name: "Helvetica-Bold", size: 16) - cellContent.addSubview(labelPrice) - - shrineInkOverlay.frame = self.bounds - shrineInkOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight] - cellContent.addSubview(shrineInkOverlay) - } - - required init(coder: NSCoder) { - super.init(coder: coder)! - } - - override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) { - super.apply(layoutAttributes) - } - - override func layoutSubviews() { - super.layoutSubviews() - self.addSubview(cellContent) - - let imagePad: CGFloat = 40 - imageView.frame = CGRect(x: imagePad, - y: imagePad, - width: self.frame.size.width - imagePad * 2, - height: self.frame.size.height - 10 - imagePad * 2) - let avatarDim: CGFloat = 24 - avatar.frame = CGRect(x: 10, - y: self.frame.size.height - avatarDim - 10, - width: avatarDim, - height: avatarDim) - labelAvatar.frame = CGRect(x: 15 + avatarDim, - y: self.frame.size.height - 30, - width: self.frame.size.width, - height: 16) - labelPrice.sizeToFit() - labelPrice.frame = CGRect(x: self.frame.size.width - labelPrice.frame.size.width - 10, - y: 10, - width: labelPrice.frame.size.width, - height: labelPrice.frame.size.height) - } - - override func prepareForReuse() { - super.prepareForReuse() - imageView.image = nil - avatar.image = nil - } - - func populateCell(_ title: String, - imageName: String, - avatar: String, - shopTitle: String, - price: String) { - labelAvatar.text = shopTitle - labelPrice.text = price - let urlString = ShrineData.baseURL + imageName - let url = URL(string: urlString) - remoteImageService.fetchImageAndThumbnail(from: url) { image, thumbnailImage in - DispatchQueue.main.sync(execute: { - self.imageView.image = thumbnailImage - }) - } - let avatarURLString = ShrineData.baseURL + avatar - let avatarURL = URL(string: avatarURLString) - remoteImageService.fetchImageAndThumbnail(from: avatarURL) { image, thumbnailImage in - DispatchQueue.main.sync(execute: { - self.avatar.image = thumbnailImage - }) - } - } - -} diff --git a/demos/Shrine/Shrine/ShrineCollectionViewController.swift b/demos/Shrine/Shrine/ShrineCollectionViewController.swift deleted file mode 100644 index 9d146e95561..00000000000 --- a/demos/Shrine/Shrine/ShrineCollectionViewController.swift +++ /dev/null @@ -1,142 +0,0 @@ -/* - Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import UIKit -import MaterialComponents.MaterialFlexibleHeader - -class ShrineCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout { - - var headerViewController: MDCFlexibleHeaderViewController! - fileprivate let shrineData: ShrineData - fileprivate var headerContentView = ShrineHeaderContentView(frame: CGRect.zero) - - override init(collectionViewLayout layout: UICollectionViewLayout) { - self.shrineData = ShrineData() - self.shrineData.readJSON() - super.init(collectionViewLayout: layout) - self.collectionView?.register(ShrineCollectionViewCell.self, - forCellWithReuseIdentifier: "ShrineCollectionViewCell") - self.collectionView?.backgroundColor = UIColor(white: 0.97, alpha: 1) - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func numberOfSections(in collectionView: UICollectionView) -> Int { - return 1 - } - - override func collectionView(_ collectionView: UICollectionView, - numberOfItemsInSection section: Int) -> Int { - return self.shrineData.titles.count - } - - override func collectionView(_ collectionView: UICollectionView, - cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ShrineCollectionViewCell", - for: indexPath) as! ShrineCollectionViewCell - let itemNum: NSInteger = (indexPath as NSIndexPath).row - - let title = self.shrineData.titles[itemNum] as! String - let imageName = self.shrineData.imageNames[itemNum] as! String - let avatar = self.shrineData.avatars[itemNum] as! String - let shopTitle = self.shrineData.shopTitles[itemNum] as! String - let price = self.shrineData.prices[itemNum] as! String - cell.populateCell(title, imageName:imageName, avatar:avatar, shopTitle:shopTitle, price:price) - - return cell - } - - func collectionView(_ collectionView: UICollectionView, - layout collectionViewLayout: UICollectionViewLayout, - sizeForItemAt indexPath: IndexPath) -> CGSize { - var safeAreaInset: CGFloat = 20 - if #available(iOS 11.0, *) { - safeAreaInset += self.view.safeAreaInsets.left + self.view.safeAreaInsets.right - } - let cellWidth = floor((self.view.frame.size.width - 10 - safeAreaInset) / 2) - let cellHeight = cellWidth * 1.2 - return CGSize(width: cellWidth, height: cellHeight) - } - - override func collectionView(_ collectionView: UICollectionView, - didSelectItemAt indexPath: IndexPath) { - let itemNum: NSInteger = (indexPath as NSIndexPath).row - - let detailVC = ShrineDetailViewController() - detailVC.productTitle = self.shrineData.titles[itemNum] as! String - detailVC.desc = self.shrineData.descriptions[itemNum] as! String - detailVC.imageName = self.shrineData.imageNames[itemNum] as! String - - self.present(detailVC, animated: true, completion: nil) - } - - override func scrollViewDidScroll(_ scrollView: UIScrollView) { - headerViewController.scrollViewDidScroll(scrollView) - let scrollOffsetY = scrollView.contentOffset.y - let duration = 0.5 - var opacity: CGFloat = 1.0 - var logoTextImageViewOpacity: CGFloat = 0 - if scrollOffsetY > -240 { - opacity = 0 - logoTextImageViewOpacity = 1 - } - UIView.animate(withDuration: duration, animations: { - self.headerContentView.scrollView.alpha = opacity - self.headerContentView.pageControl.alpha = opacity - self.headerContentView.logoImageView.alpha = opacity - self.headerContentView.logoTextImageView.alpha = logoTextImageViewOpacity - }) - - } - - func sizeHeaderView() { - let headerView = headerViewController.headerView - let bounds = UIScreen.main.bounds - if bounds.size.width < bounds.size.height { - headerView.maximumHeight = 440 - } else { - headerView.maximumHeight = 72 - } - headerView.minimumHeight = 72 - } - - override func willAnimateRotation(to toInterfaceOrientation: UIInterfaceOrientation, - duration: TimeInterval) { - sizeHeaderView() - collectionView?.collectionViewLayout.invalidateLayout() - } - - override func viewWillAppear(_ animated: Bool) { - sizeHeaderView() - collectionView?.collectionViewLayout.invalidateLayout() - } - - func setupHeaderView() { - let headerView = headerViewController.headerView - headerView.trackingScrollView = collectionView - headerView.maximumHeight = 440 - headerView.minimumHeight = 72 - headerView.minMaxHeightIncludesSafeArea = false - headerView.backgroundColor = UIColor.white - headerView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - headerContentView.frame = (headerView.bounds) - headerContentView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - headerView.addSubview(headerContentView) - } - -} diff --git a/demos/Shrine/Shrine/ShrineData.swift b/demos/Shrine/Shrine/ShrineData.swift deleted file mode 100644 index d74812be505..00000000000 --- a/demos/Shrine/Shrine/ShrineData.swift +++ /dev/null @@ -1,63 +0,0 @@ -/* - Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import UIKit - -class ShrineData { - - var imageNames = NSMutableArray() - var titles = NSMutableArray() - var descriptions = NSMutableArray() - var prices = NSMutableArray() - var avatars = NSMutableArray() - var shopTitles = NSMutableArray() - static var baseURL = "https://www.gstatic.com/angular/material-adaptive/shrine/" - - func readJSON() { - - let filePath = Bundle.main.path(forResource: "products", ofType: "json") - var json: NSDictionary! - do { - let data = try? Data(contentsOf: URL(fileURLWithPath: filePath!)) - let options = JSONSerialization.ReadingOptions() - json = try JSONSerialization.jsonObject(with: data!, options: options) as? NSDictionary - } catch _ { - print("Couldn't get JSON data") - } - - let products = json["products"] as! NSArray - for prod in products { - let product = prod as! NSDictionary - let imageName = product["image"] as! String - imageNames.add(imageName) - let title = product["title"] as! String - titles.add(title) - let description = product["description"] as! String - descriptions.add(description) - let price = product["price"] as! String - prices.add(price) - } - - let shops = json["shops"] as! NSArray - for shp in shops { - let shop = shp as! NSDictionary - let avatar = shop["avatar"] as! String - avatars.add(avatar) - let shopTitle = shop["shop"] as! String - shopTitles.add(shopTitle) - } - } -} diff --git a/demos/Shrine/Shrine/ShrineDetailViewController.swift b/demos/Shrine/Shrine/ShrineDetailViewController.swift deleted file mode 100644 index 8a1417b5e32..00000000000 --- a/demos/Shrine/Shrine/ShrineDetailViewController.swift +++ /dev/null @@ -1,164 +0,0 @@ -/* - Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import UIKit -import MaterialComponents.MaterialAppBar -import MaterialComponents.MaterialButtons -import MaterialComponents.MaterialIcons_ic_arrow_back -import RemoteImageServiceForMDCDemos - -class ShrineDetailView: UIScrollView { - - var title = "" - var desc = "" - var imageName = "popsicle.png" - fileprivate var remoteImageService = RemoteImageService() - fileprivate var label = UILabel() - fileprivate var labelDesc = UILabel() - fileprivate var imageView = UIImageView() - - override func layoutSubviews() { - super.layoutSubviews() - backgroundColor = .white - let minContentHeight = CGFloat(640) - contentSize = CGSize(width: frame.width, height: minContentHeight) - - let labelPadding: CGFloat = 50 - imageView.frame = CGRect(x: labelPadding, y: labelPadding, - width: frame.size.width - 2 * labelPadding, height: 220) - imageView.contentMode = UIView.ContentMode.scaleAspectFit - imageView.autoresizingMask = .flexibleHeight - addSubview(imageView) - let urlString: String = ShrineData.baseURL + imageName - let url = URL(string: urlString) - remoteImageService.fetchImageAndThumbnail(from: url) { (image: UIImage?, _) -> Void in - DispatchQueue.main.async(execute: { - self.imageView.image = image - self.imageView.setNeedsDisplay() - }) - } - - configureTitleLabel(label: label, labelPadding: labelPadding) - self.addSubview(label) - - configureDescriptionLabel(label: labelDesc, labelPadding: labelPadding) - self.addSubview(labelDesc) - } - - // MARK: Private - - func configureTitleLabel(label: UILabel, labelPadding: CGFloat) { - label.font = UIFont(name: "AbrilFatface-Regular", size: 36) - label.textColor = UIColor(red: 0.039, green: 0.192, blue: 0.259, alpha: 1) - label.lineBreakMode = .byWordWrapping - label.numberOfLines = 2 - - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.lineHeightMultiple = 0.8 - let attrString = NSMutableAttributedString(string: title) - attrString.addAttribute(NSAttributedString.Key.paragraphStyle, - value:paragraphStyle, - range: NSRange(location: 0, length:attrString.length)) - label.attributedText = attrString - label.sizeToFit() - label.frame = CGRect(x: labelPadding, - y: 280, width: label.frame.size.width, height: label.frame.size.height) - label.autoresizingMask = [.flexibleWidth, .flexibleHeight] - } - - func configureDescriptionLabel(label: UILabel, labelPadding: CGFloat) { - label.lineBreakMode = .byWordWrapping - label.numberOfLines = 5 - label.font = UIFont(name: "Helvetica", size: 14) - label.textColor = UIColor(white: 0.54, alpha: 1) - let descParagraphStyle = NSMutableParagraphStyle() - descParagraphStyle.lineHeightMultiple = 1.5 - let descAttrString = NSMutableAttributedString(string: desc) - descAttrString.addAttribute(NSAttributedString.Key.paragraphStyle, - value:descParagraphStyle, - range:NSRange(location: 0, length: descAttrString.length)) - label.attributedText = descAttrString - label.frame = CGRect(x: labelPadding, - y: 360, width: self.frame.size.width - 2 * labelPadding, height: 160) - label.sizeToFit() - label.autoresizingMask = [.flexibleWidth, .flexibleHeight] - } -} - -class ShrineDetailViewController: UIViewController { - - let fabPadding: CGFloat = 25 - var productTitle = "" - var desc = "" - var imageName = "popsicle.png" - fileprivate let appBar = MDCAppBar() - fileprivate let floatingButton = MDCFloatingButton() - - init() { - super.init(nibName: nil, bundle: nil) - - addChild(appBar.headerViewController) - appBar.headerViewController.headerView.backgroundColor = .clear - appBar.navigationBar.tintColor = .black - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - override func viewDidLoad() { - let detailView = ShrineDetailView(frame: view.frame) - detailView.title = productTitle - detailView.desc = desc - detailView.imageName = imageName - detailView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(detailView) - - appBar.addSubviewsToParent() - let backButton = UIBarButtonItem(title:"", - style:.done, - target:self, - action:#selector(dismissDetails)) - let backImage = MDCIcons.imageFor_ic_arrow_back() - backButton.image = backImage - appBar.navigationBar.leftBarButtonItem = backButton - - floatingButton.setTitle("+", for: UIControl.State()) - floatingButton.backgroundColor = - UIColor(red: 0.086, green: 0.941, blue: 0.941, alpha: 1) - floatingButton.sizeToFit() - view.addSubview(floatingButton) - } - - override func viewWillLayoutSubviews() { - var safeAreaInset: CGFloat = 0 - #if swift(>=3.2) - if #available(iOS 11.0, *) { - safeAreaInset = self.view.safeAreaInsets.bottom - } - #endif - let updatedFabPadding = max(fabPadding, safeAreaInset) - floatingButton.frame = CGRect(x: view.frame.width - floatingButton.frame.width - updatedFabPadding, - y: view.frame.height - floatingButton.frame.height - updatedFabPadding, - width: floatingButton.frame.width, - height: floatingButton.frame.height) - } - - @objc func dismissDetails() { - dismiss(animated: true, completion: nil) - } - -} diff --git a/demos/Shrine/Shrine/ShrineFlexibleHeaderContainerViewController.swift b/demos/Shrine/Shrine/ShrineFlexibleHeaderContainerViewController.swift deleted file mode 100644 index f0bdc7f7e40..00000000000 --- a/demos/Shrine/Shrine/ShrineFlexibleHeaderContainerViewController.swift +++ /dev/null @@ -1,45 +0,0 @@ -/* - Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import UIKit -import MaterialComponents.MaterialFlexibleHeader - -class ShrineFlexibleHeaderContainerViewController: MDCFlexibleHeaderContainerViewController { - - init() { - let layout = UICollectionViewFlowLayout() - let sectionInset: CGFloat = 10.0 - layout.sectionInset = UIEdgeInsets(top: sectionInset, - left: sectionInset, - bottom: sectionInset, - right: sectionInset) - - if #available(iOS 11.0, *) { - layout.sectionInsetReference = .fromSafeArea - } - - let collectionVC = ShrineCollectionViewController(collectionViewLayout: layout) - super.init(contentViewController: collectionVC) - - collectionVC.headerViewController = self.headerViewController - collectionVC.setupHeaderView() - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - -} diff --git a/demos/Shrine/Shrine/ShrineHeaderContentView.swift b/demos/Shrine/Shrine/ShrineHeaderContentView.swift deleted file mode 100644 index 84054c18446..00000000000 --- a/demos/Shrine/Shrine/ShrineHeaderContentView.swift +++ /dev/null @@ -1,180 +0,0 @@ -/* - Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import UIKit -import MaterialComponents.MaterialPageControl - -class ShrineHeaderContentView: UIView, UIScrollViewDelegate { - - var pageControl = MDCPageControl() - var scrollView = UIScrollView() - var logoImageView = UIImageView(image: UIImage(named: "ShrineLogo")) - var logoTextImageView = UIImageView(image: UIImage(named: "ShrineTextLogo")) - fileprivate var pages = NSMutableArray() - fileprivate var label = UILabel() - fileprivate var labelDesc = UILabel() - fileprivate var label2 = UILabel() - fileprivate var labelDesc2 = UILabel() - fileprivate var label3 = UILabel() - fileprivate var labelDesc3 = UILabel() - fileprivate var cyanBox = UIView() - fileprivate var cyanBox2 = UIView() - fileprivate var cyanBox3 = UIView() - fileprivate var imageView = UIImageView() - fileprivate var imageView2 = UIImageView() - fileprivate var imageView3 = UIImageView() - - override init(frame: CGRect) { - super.init(frame: frame) - commonInit() - } - - required init(coder: NSCoder) { - super.init(coder: coder)! - } - - func commonInit() { - let boundsWidth = bounds.width - let boundsHeight = bounds.height - scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - scrollView.delegate = self - scrollView.isPagingEnabled = true - scrollView.showsHorizontalScrollIndicator = false - addSubview(scrollView) - autoresizingMask = [.flexibleWidth, .flexibleHeight] - - for i in 0...2 { - let boundsLeft = CGFloat(i) * boundsWidth - let pageFrame = bounds.offsetBy(dx: boundsLeft, dy: 0) - let page = UIView(frame:pageFrame) - page.clipsToBounds = true - page.autoresizingMask = [.flexibleWidth, .flexibleHeight] - pages.add(page) - scrollView.addSubview(page) - } - - pageControl.numberOfPages = 3 - pageControl.autoresizingMask = [.flexibleWidth, .flexibleHeight] - let pageControlSize = pageControl.sizeThatFits(bounds.size) - pageControl.frame = CGRect(x: 0, - y: boundsHeight - pageControlSize.height, - width: boundsWidth, - height: pageControlSize.height) - pageControl.addTarget(self, action: #selector(didChangePage), - for: UIControl.Event.valueChanged) - addSubview(pageControl) - - addHeaderPages() - addSubview(logoImageView) - addSubview(logoTextImageView) - } - - func addHeaderPages() { - _ = ShrineHeaderPage(page: pages[0] as! UIView, - imageView: imageView, - label: label, - labelDesc: labelDesc, - cyanBox: cyanBox, - imageName: "chair.png", - description: "Green \ncomfort chair") - _ = ShrineHeaderPage(page: pages[1] as! UIView, - imageView: imageView2, - label: label2, - labelDesc: labelDesc2, - cyanBox: cyanBox2, - imageName: "backpack.png", - description: "Best gift for \nthe traveler") - _ = ShrineHeaderPage(page: pages[2] as! UIView, - imageView: imageView3, - label: label3, - labelDesc: labelDesc3, - cyanBox: cyanBox3, - imageName: "heels.png", - description: "Better \nwearing heels") - } - - override func layoutSubviews() { - super.layoutSubviews() - var safeAreaInset: CGFloat = 0 - #if swift(>=3.2) - if #available(iOS 11.0, *) { - safeAreaInset = min(self.safeAreaInsets.top, 10) - } - #endif - let boundsWidth = bounds.width - let boundsHeight = bounds.height - for i in 0...pages.count - 1 { - let boundsLeft = CGFloat(i) * boundsWidth - let pageFrame = bounds.offsetBy(dx: boundsLeft, dy: 0) - let page = pages[i] as! UIView - page.frame = pageFrame - } - let pageControlSize = pageControl.sizeThatFits(bounds.size) - pageControl.frame = CGRect(x: 0, y: boundsHeight - pageControlSize.height, width: boundsWidth, - height: pageControlSize.height) - let scrollWidth: CGFloat = boundsWidth * CGFloat(pages.count) - scrollView.frame = CGRect(x: 0, y: 0, width: boundsWidth, height: boundsHeight) - scrollView.contentSize = CGSize(width: scrollWidth, height: boundsHeight) - - let scrollViewOffsetX = CGFloat(pageControl.currentPage) * boundsWidth - scrollView.setContentOffset(CGPoint(x: scrollViewOffsetX, y: 0), animated: false) - logoImageView.center = CGPoint(x: (frame.size.width) / 2, y: 44 + safeAreaInset) - logoTextImageView.center = CGPoint(x: (frame.size.width) / 2, y: 44 + safeAreaInset) - - let labelWidth = CGFloat(250) - let labelWidthFrame = CGRect(x: frame.size.width - labelWidth, - y: 90, width: labelWidth, height: label.frame.size.height) - - let labelDescWidth = CGFloat(200) - let labelDescWidthFrame = CGRect(x: frame.size.width - labelDescWidth - 10, - y: 190, width: labelDescWidth, height: 40) - - label.frame = labelWidthFrame - labelDesc.frame = labelDescWidthFrame - label2.frame = labelWidthFrame - labelDesc2.frame = labelDescWidthFrame - label3.frame = labelWidthFrame - labelDesc3.frame = labelDescWidthFrame - - let cyanBoxFrame = CGRect(x: frame.size.width - 210, y: 180, width: 100, height: 8) - cyanBox.frame = cyanBoxFrame - cyanBox2.frame = cyanBoxFrame - cyanBox3.frame = cyanBoxFrame - - imageView.frame = CGRect(x: -180, y: 120, width: 420, height: frame.size.height) - imageView2.frame = CGRect(x: -220, y: 110, width: 420, height: frame.size.height) - imageView3.frame = CGRect(x: -180, y: 40, width: 420, height: frame.size.height) - } - - func scrollViewDidScroll(_ scrollView: UIScrollView) { - pageControl.scrollViewDidScroll(scrollView) - } - - func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { - pageControl.scrollViewDidEndDecelerating(scrollView) - } - - func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { - pageControl.scrollViewDidEndScrollingAnimation(scrollView) - } - - @objc func didChangePage(_ sender: MDCPageControl) { - var offset = scrollView.contentOffset - offset.x = CGFloat(sender.currentPage) * scrollView.bounds.size.width - scrollView.setContentOffset(offset, animated: true) - } - -} diff --git a/demos/Shrine/Shrine/ShrineHeaderPage.swift b/demos/Shrine/Shrine/ShrineHeaderPage.swift deleted file mode 100644 index e71ce0af69b..00000000000 --- a/demos/Shrine/Shrine/ShrineHeaderPage.swift +++ /dev/null @@ -1,95 +0,0 @@ -/* - Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import UIKit -import RemoteImageServiceForMDCDemos - -struct ShrineHeaderPage { - - let textColor = UIColor(red: 10 / 255, green: 49 / 255, blue: 66 / 255, alpha: 1) - let fontAbril = UIFont(name: "AbrilFatface-Regular", size: 36) - let fontHelvetica = UIFont(name: "Helvetica", size: 14) - let cyanBoxColor = UIColor(red: 0.19, green: 0.94, blue: 0.94, alpha: 1) - let descColor = UIColor(white: 0.54, alpha: 1) - let descString = "Leave the tunnel and the rain is fallin amazing things happen when you wait" - - var remoteImageService = RemoteImageService() - - var page: UIView - var imageView: UIView - var label: UILabel - var labelDesc: UILabel - var cyanBox: UIView - var imageName: String - var description: String - - init(page: UIView, imageView: UIImageView, label: UILabel, labelDesc: UILabel, - cyanBox: UIView, imageName: String, description: String) { - self.page = page - self.imageView = imageView - self.label = label - self.labelDesc = labelDesc - self.cyanBox = cyanBox - self.imageName = imageName - self.description = description - - imageView.contentMode = UIView.ContentMode.scaleAspectFill - imageView.autoresizingMask = .flexibleHeight - (page as AnyObject).addSubview(imageView) - let url = URL(string: ShrineData.baseURL + imageName) - remoteImageService.fetchImageAndThumbnail(from: url) { (image: UIImage?, _: UIImage?) -> Void in - DispatchQueue.main.async(execute: { - imageView.image = image - imageView.setNeedsDisplay() - }) - } - - label.font = fontAbril - label.textColor = textColor - label.lineBreakMode = .byWordWrapping - label.numberOfLines = 2 - label.attributedText = attributedString(description, lineHeightMultiple: 0.8) - label.sizeToFit() - (page as AnyObject).addSubview(label) - - labelDesc.lineBreakMode = .byWordWrapping - labelDesc.numberOfLines = 3 - labelDesc.font = fontHelvetica - labelDesc.textColor = descColor - labelDesc.attributedText = attributedString(descString, lineHeightMultiple: 1.2) - labelDesc.autoresizingMask = .flexibleWidth - (page as AnyObject).addSubview(labelDesc) - - cyanBox.backgroundColor = cyanBoxColor - (page as AnyObject).addSubview(cyanBox) - - let inkOverlay = ShrineInkOverlay(frame: (page as AnyObject).bounds) - inkOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight] - (page as AnyObject).addSubview(inkOverlay) - - } - - func attributedString(_ string: String, - lineHeightMultiple: CGFloat) -> NSMutableAttributedString { - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.lineHeightMultiple = lineHeightMultiple - let attrString = NSMutableAttributedString(string: string) - attrString.addAttribute(NSAttributedString.Key.paragraphStyle, value:paragraphStyle, - range:NSRange(location: 0, length: attrString.length)) - return attrString - } - -} diff --git a/demos/Shrine/Shrine/ShrineInkOverlay.swift b/demos/Shrine/Shrine/ShrineInkOverlay.swift deleted file mode 100644 index dc06e5e55ce..00000000000 --- a/demos/Shrine/Shrine/ShrineInkOverlay.swift +++ /dev/null @@ -1,41 +0,0 @@ -/* - Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import UIKit -import MaterialComponents.MaterialInk - -class ShrineInkOverlay: UIView, MDCInkTouchControllerDelegate { - - fileprivate var inkTouchController: MDCInkTouchController? - - override init(frame: CGRect) { - super.init(frame: frame) - let cyan = UIColor(red: 22 / 255, green: 240 / 255, blue: 240 / 255, alpha: 0.2) - self.inkTouchController = MDCInkTouchController(view:self) - self.inkTouchController!.defaultInkView.inkColor = cyan - self.inkTouchController!.addInkView() - self.inkTouchController!.delegate = self - } - - required init(coder: NSCoder) { - super.init(coder: coder)! - } - - override func layoutSubviews() { - super.layoutSubviews() - } - -} diff --git a/demos/supplemental/RemoteImageServiceForMDCDemos.podspec b/demos/supplemental/RemoteImageServiceForMDCDemos.podspec index 3815555c8ff..b494171053b 100644 --- a/demos/supplemental/RemoteImageServiceForMDCDemos.podspec +++ b/demos/supplemental/RemoteImageServiceForMDCDemos.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "RemoteImageServiceForMDCDemos" - s.version = "124.0.1" + s.version = "124.1.1" s.summary = "A helper image class for the MDC demos." s.description = "This spec is made for use in the MDC demos. It gets images via url." s.homepage = "https://github.com/material-components/material-components-ios"