diff --git a/components/ProgressView/examples/ProgressViewExample.m b/components/ProgressView/examples/ProgressViewExample.m index 5b146f6c226..5b3854e12fb 100644 --- a/components/ProgressView/examples/ProgressViewExample.m +++ b/components/ProgressView/examples/ProgressViewExample.m @@ -14,9 +14,9 @@ #import -#import "MaterialColorScheme.h" #import "MaterialPalettes.h" #import "MaterialProgressView.h" +#import "MaterialColorScheme.h" #import "MaterialTypographyScheme.h" static const CGFloat MDCProgressViewAnimationDuration = 1; @@ -34,6 +34,9 @@ @interface ProgressViewExample : UIViewController @property(nonatomic, strong) MDCProgressView *fullyColoredProgressView; @property(nonatomic, strong) UILabel *fullyColoredProgressLabel; +@property(nonatomic, strong) MDCProgressView *gradientColoredProgressView; +@property(nonatomic, strong) UILabel *gradientColoredProgressLabel; + @property(nonatomic, strong) MDCProgressView *backwardProgressResetView; @property(nonatomic, strong) UILabel *backwardProgressResetLabel; @@ -71,6 +74,16 @@ - (void)setupProgressViews { // Hide the progress view at setup time. _fullyColoredProgressView.hidden = YES; + _gradientColoredProgressView = [[MDCProgressView alloc] init]; + _gradientColoredProgressView.translatesAutoresizingMaskIntoConstraints = NO; + [self.container addSubview:_gradientColoredProgressView]; + _gradientColoredProgressView.progressTintColors = @[ + (id)MDCPalette.greenPalette.tint500.CGColor, (id)MDCPalette.bluePalette.tint500.CGColor, + (id)MDCPalette.redPalette.tint500.CGColor + ]; + _gradientColoredProgressView.trackTintColor = MDCPalette.yellowPalette.tint500; + _gradientColoredProgressView.progress = 0.33f; + _backwardProgressResetView = [[MDCProgressView alloc] init]; _backwardProgressResetView.translatesAutoresizingMaskIntoConstraints = NO; [self.container addSubview:_backwardProgressResetView]; @@ -164,6 +177,13 @@ - (void)setupLabels { _fullyColoredProgressLabel.translatesAutoresizingMaskIntoConstraints = NO; [self.container addSubview:_fullyColoredProgressLabel]; + _gradientColoredProgressLabel = [[UILabel alloc] init]; + _gradientColoredProgressLabel.text = @"Progress with gradient colors"; + _gradientColoredProgressLabel.font = self.typographyScheme.caption; + _gradientColoredProgressLabel.textColor = self.colorScheme.onBackgroundColor; + _gradientColoredProgressLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.container addSubview:_gradientColoredProgressLabel]; + _backwardProgressResetLabel = [[UILabel alloc] init]; _backwardProgressResetLabel.text = @"Backward progress (reset)"; _backwardProgressResetLabel.font = self.typographyScheme.caption; @@ -188,6 +208,8 @@ - (void)setupConstraints { @"tintedLabel" : _tintedProgressLabel, @"coloredView" : _fullyColoredProgressView, @"coloredLabel" : _fullyColoredProgressLabel, + @"gradientView" : _gradientColoredProgressView, + @"gradientLabel" : _gradientColoredProgressLabel, @"backwardResetView" : _backwardProgressResetView, @"backwardResetLabel" : _backwardProgressResetLabel, @"backwardAnimateView" : _backwardProgressAnimateView, @@ -205,6 +227,7 @@ - (void)setupConstraints { "[stockView(==h)]-(p)-[stockLabel]-(s)-" "[tintedView(==h)]-(p)-[tintedLabel]-(s)-" "[coloredView(==h)]-(p)-[coloredLabel]-(s)-" + "[gradientView(==h)]-(p)-[gradientLabel]-(s)-" "[backwardResetView(==h)]-(p)-[backwardResetLabel]-(s)-" "[backwardAnimateView(==h)]-(p)-[backwardAnimateLabel]" options:0 @@ -217,11 +240,13 @@ - (void)setupConstraints { @"H:|-(p)-[stockView]-(p)-|", @"H:|-(p)-[tintedView]-(p)-|", @"H:|-(p)-[coloredView]-(p)-|", + @"H:|-(p)-[gradientView]-(p)-|", @"H:|-(p)-[backwardResetView]-(p)-|", @"H:|-(p)-[backwardAnimateView]-(p)-|", @"H:|-(p)-[stockLabel]-(p)-|", @"H:|-(p)-[tintedLabel]-(p)-|", @"H:|-(p)-[coloredLabel]-(p)-|", + @"H:|-(p)-[gradientLabel]-(p)-|", @"H:|-(p)-[backwardResetLabel]-(p)-|", @"H:|-(p)-[backwardAnimateLabel]-(p)-|", ]; @@ -240,6 +265,7 @@ - (void)didPressAnimateButton:(UIButton *)sender { [self animateStep1:_stockProgressView]; [self animateStep1:_tintedProgressView]; [self animateStep1:_fullyColoredProgressView]; + [self animateStep1:_gradientColoredProgressView]; [self animateBackwardProgressResetViewWithCountdown:4]; [self animateBackwardProgressAnimateViewWithCountdown:4 completion:^(BOOL ignored) { diff --git a/components/ProgressView/src/MDCProgressView.h b/components/ProgressView/src/MDCProgressView.h index 68beaeffee2..eac3bd8f67c 100644 --- a/components/ProgressView/src/MDCProgressView.h +++ b/components/ProgressView/src/MDCProgressView.h @@ -39,6 +39,16 @@ IB_DESIGNABLE */ @property(nonatomic, strong, nullable) UIColor *progressTintColor UI_APPEARANCE_SELECTOR; +/** + An array of CGColorRef objects used to defining the color of each gradient stop. + All colors are spread uniformly across the range. + + Setting @c progressTintColor resets this property to @c nil. + + The default is nil. +*/ +@property(nonatomic, copy, nullable) NSArray *progressTintColors; + /** The color shown for the portion of the progress view that is not filled. diff --git a/components/ProgressView/src/MDCProgressView.m b/components/ProgressView/src/MDCProgressView.m index 1318cc883c8..6534fc3bd04 100644 --- a/components/ProgressView/src/MDCProgressView.m +++ b/components/ProgressView/src/MDCProgressView.m @@ -17,6 +17,7 @@ #include #import "MaterialPalettes.h" +#import "MDCProgressGradientView.h" #import "MaterialProgressViewStrings.h" #import "MaterialProgressViewStrings_table.h" #import "MaterialMath.h" @@ -35,7 +36,7 @@ static NSString *const kBundle = @"MaterialProgressView.bundle"; @interface MDCProgressView () -@property(nonatomic, strong) UIView *progressView; +@property(nonatomic, strong) MDCProgressGradientView *progressView; @property(nonatomic, strong) UIView *trackView; @property(nonatomic) BOOL animatingHide; // A UIProgressView to return the same format for the accessibility value. For example, when @@ -74,10 +75,12 @@ - (void)commonMDCProgressViewInit { _trackView.autoresizingMask = UIViewAutoresizingFlexibleWidth; [self addSubview:_trackView]; - _progressView = [[UIView alloc] initWithFrame:CGRectZero]; + _progressView = [[MDCProgressGradientView alloc] initWithFrame:CGRectZero]; [self addSubview:_progressView]; - _progressView.backgroundColor = MDCProgressViewDefaultTintColor(); + _progressView.colors = @[ + (id)MDCProgressViewDefaultTintColor().CGColor, (id)MDCProgressViewDefaultTintColor().CGColor + ]; _trackView.backgroundColor = [[self class] defaultTrackTintColorForProgressTintColor:MDCProgressViewDefaultTintColor()]; } @@ -100,17 +103,30 @@ - (void)layoutSubviews { - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { [super traitCollectionDidChange:previousTraitCollection]; + if (self.progressTintColor) { + self.progressView.colors = + @[ (id)self.progressTintColor.CGColor, (id)self.progressTintColor.CGColor ]; + } + if (self.traitCollectionDidChangeBlock) { self.traitCollectionDidChangeBlock(self, previousTraitCollection); } } -- (UIColor *)progressTintColor { - return self.progressView.backgroundColor; +- (void)setProgressTintColor:(UIColor *)progressTintColor { + _progressTintColor = progressTintColor; + _progressTintColors = nil; + if (progressTintColor != nil) { + self.progressView.colors = @[ (id)progressTintColor.CGColor, (id)progressTintColor.CGColor ]; + } else { + self.progressView.colors = nil; + } } -- (void)setProgressTintColor:(UIColor *)progressTintColor { - self.progressView.backgroundColor = progressTintColor; +- (void)setProgressTintColors:(NSArray *)progressTintColors { + _progressTintColors = [progressTintColors copy]; + _progressTintColor = nil; + self.progressView.colors = _progressTintColors; } - (UIColor *)trackTintColor { diff --git a/components/ProgressView/src/private/MDCProgressGradientView.h b/components/ProgressView/src/private/MDCProgressGradientView.h new file mode 100644 index 00000000000..76bced360c8 --- /dev/null +++ b/components/ProgressView/src/private/MDCProgressGradientView.h @@ -0,0 +1,29 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + A gradient view used for an MDCProgressView's progress bar. + */ +__attribute__((objc_subclassing_restricted)) @interface MDCProgressGradientView : UIView + +/** + An array of CGColorRef objects defining the color of each gradient stop + + Defaults to nil. + */ +@property(nonatomic, nullable, copy) NSArray *colors; + +@end diff --git a/components/ProgressView/src/private/MDCProgressGradientView.m b/components/ProgressView/src/private/MDCProgressGradientView.m new file mode 100644 index 00000000000..0f4d8693d92 --- /dev/null +++ b/components/ProgressView/src/private/MDCProgressGradientView.m @@ -0,0 +1,64 @@ +// 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 "MDCProgressGradientView.h" + +@interface MDCProgressGradientView () + +@property(nonatomic, readonly) CAGradientLayer *gradientLayer; + +@end + +@implementation MDCProgressGradientView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self commonMDCProgressGradientViewInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + self = [super initWithCoder:coder]; + if (self) { + [self commonMDCProgressGradientViewInit]; + } + return self; +} + +- (void)commonMDCProgressGradientViewInit { + self.gradientLayer.startPoint = CGPointMake(0.0f, 0.5f); + self.gradientLayer.endPoint = CGPointMake(1.0f, 0.5f); +} + +- (void)setColors:(NSArray *)colors { + self.gradientLayer.colors = colors; +} + +- (NSArray *)colors { + return self.gradientLayer.colors; +} + ++ (Class)layerClass { + return [CAGradientLayer class]; +} + +#pragma mark - Private Helpers + +- (CAGradientLayer *)gradientLayer { + return (CAGradientLayer *)self.layer; +} + +@end diff --git a/components/ProgressView/tests/snapshot/MDCProgressViewSnapshotTests.m b/components/ProgressView/tests/snapshot/MDCProgressViewSnapshotTests.m index fcb289b3001..78aea0fb9f7 100644 --- a/components/ProgressView/tests/snapshot/MDCProgressViewSnapshotTests.m +++ b/components/ProgressView/tests/snapshot/MDCProgressViewSnapshotTests.m @@ -134,4 +134,85 @@ - (void)testProgress100RTL { [self generateSnapshotAndVerifyForView:self.progressView]; } +- (void)testGradientProgress000LTR { + // Given + self.progressView.progressTintColors = @[ + (id)UIColor.greenColor.CGColor, (id)UIColor.blueColor.CGColor, (id)UIColor.redColor.CGColor + ]; + + // When + self.progressView.progress = 0; + + // Then + [self generateSnapshotAndVerifyForView:self.progressView]; +} + +- (void)testGradientProgress050LTR { + // Given + self.progressView.progressTintColors = @[ + (id)UIColor.greenColor.CGColor, (id)UIColor.blueColor.CGColor, (id)UIColor.redColor.CGColor + ]; + + // When + self.progressView.progress = 0.5f; + + // Then + [self generateSnapshotAndVerifyForView:self.progressView]; +} + +- (void)testGradientProgress100LTR { + // Given + self.progressView.progressTintColors = @[ + (id)UIColor.greenColor.CGColor, (id)UIColor.blueColor.CGColor, (id)UIColor.redColor.CGColor + ]; + + // When + self.progressView.progress = 1; + + // Then + [self generateSnapshotAndVerifyForView:self.progressView]; +} + +- (void)testGradientProgress000RTL { + // Given + self.progressView.progressTintColors = @[ + (id)UIColor.greenColor.CGColor, (id)UIColor.blueColor.CGColor, (id)UIColor.redColor.CGColor + ]; + + // When + [self changeViewToRTL:self.progressView]; + self.progressView.progress = 0; + + // Then + [self generateSnapshotAndVerifyForView:self.progressView]; +} + +- (void)testGradientProgress050RTL { + // Given + self.progressView.progressTintColors = @[ + (id)UIColor.greenColor.CGColor, (id)UIColor.blueColor.CGColor, (id)UIColor.redColor.CGColor + ]; + + // When + [self changeViewToRTL:self.progressView]; + self.progressView.progress = 0.5f; + + // Then + [self generateSnapshotAndVerifyForView:self.progressView]; +} + +- (void)testGradientProgress100RTL { + // Given + self.progressView.progressTintColors = @[ + (id)UIColor.greenColor.CGColor, (id)UIColor.blueColor.CGColor, (id)UIColor.redColor.CGColor + ]; + + // When + [self changeViewToRTL:self.progressView]; + self.progressView.progress = 1; + + // Then + [self generateSnapshotAndVerifyForView:self.progressView]; +} + @end