diff --git a/components/Slider/examples/SliderCollectionViewController.m b/components/Slider/examples/SliderCollectionViewController.m index a5c61826fff..d5aad35f923 100644 --- a/components/Slider/examples/SliderCollectionViewController.m +++ b/components/Slider/examples/SliderCollectionViewController.m @@ -30,6 +30,8 @@ @interface MDCSliderModel : NSObject @property(nonatomic, assign) UIColor *labelColor; @property(nonatomic, assign) UIColor *bgColor; @property(nonatomic, nullable) UIColor *sliderColor; +@property(nonatomic, nullable) UIColor *filledTickColor; +@property(nonatomic, nullable) UIColor *backgroundTickColor; @property(nonatomic, nullable) UIColor *trackBackgroundColor; @property(nonatomic, assign) int numDiscreteValues; @property(nonatomic, assign) CGFloat value; @@ -112,6 +114,18 @@ - (void)applyModel:(MDCSliderModel *)model { _slider.inkColor = model.sliderColor; } + if (model.filledTickColor) { + [_slider setFilledTrackTickColor:model.filledTickColor forState:UIControlStateNormal]; + } else { + [_slider setFilledTrackTickColor:UIColor.blackColor forState:UIControlStateNormal]; + } + + if (model.backgroundTickColor) { + [_slider setBackgroundTrackTickColor:model.backgroundTickColor forState:UIControlStateNormal]; + } else { + [_slider setBackgroundTrackTickColor:UIColor.blackColor forState:UIControlStateNormal]; + } + // Add target/action pair [_slider addTarget:model action:@selector(didChangeMDCSliderValue:) @@ -202,6 +216,8 @@ - (instancetype)init { model.labelString = @"Discrete slider with numeric value label"; model.numDiscreteValues = 5; model.value = 0.2f; + model.backgroundTickColor = UIColor.blackColor; + model.filledTickColor = MDCPalette.bluePalette.tint100; [_sliders addObject:model]; model = [[MDCSliderModel alloc] init]; @@ -209,6 +225,8 @@ - (instancetype)init { model.numDiscreteValues = 7; model.value = 1.f; model.discreteValueLabel = NO; + model.backgroundTickColor = UIColor.blackColor; + model.filledTickColor = MDCPalette.bluePalette.tint100; [_sliders addObject:model]; model = [[MDCSliderModel alloc] init]; diff --git a/components/Slider/src/MDCSlider.h b/components/Slider/src/MDCSlider.h index ab922f4257d..54b600154cd 100644 --- a/components/Slider/src/MDCSlider.h +++ b/components/Slider/src/MDCSlider.h @@ -114,6 +114,53 @@ IB_DESIGNABLE */ - (nullable UIColor *)trackBackgroundColorForState:(UIControlState)state; +/** + Sets the color of the ticks within the filled track to use for the specified state. + + In general, if a property is not specified for a state, the default is to use the + @c UIControlStateNormal value. If the @c UIControlStateNormal value is not set, then the property + defaults to a default value. Therefore, at a minimum, you should set the value for the + normal state. + + @param tickColor The color of the tick marks within the filled track. + @param state The state of the slider. + */ +- (void)setFilledTrackTickColor:(nullable UIColor *)tickColor forState:(UIControlState)state; + +/** + Returns the tick color for the filled track portion associated with the specified state. + + @params state The state that uses the filled-track tick color. + @returns The filled-track tick color for the specified state. If no color has been set for the + specific state, this method returns the color associated with the @c UIControlStateNormal + state. + */ +- (nullable UIColor *)filledTrackTickColorForState:(UIControlState)state; + +/** + Sets the color of the ticks for the background (unfilled) track to use for the specified state. + + In general, if a property is not specified for a state, the default is to use the + @c UIControlStateNormal value. If the @c UIControlStateNormal value is not set, then the property + defaults to a default value. Therefore, at a minimum, you should set the value for the + normal state. + + @param tickColor The color of the tick marks outside the filled track. + @param state The state of the slider. + */ +- (void)setBackgroundTrackTickColor:(nullable UIColor *)tickColor forState:(UIControlState)state; + +/** + Returns the tick color for the background (unfilled) track portion associated with the specified + state. + + @params state The state that uses the background-track tick color. + @returns The background-track tick color for the specified state. If no color has been set for the + specific state, this method returns the color associated with the @c UIControlStateNormal + state. + */ +- (nullable UIColor *)backgroundTrackTickColorForState:(UIControlState)state; + /** The color of the Ink ripple. diff --git a/components/Slider/src/MDCSlider.m b/components/Slider/src/MDCSlider.m index ded62d561e0..3bba68770c7 100644 --- a/components/Slider/src/MDCSlider.m +++ b/components/Slider/src/MDCSlider.m @@ -39,6 +39,8 @@ @implementation MDCSlider { NSMutableDictionary *_thumbColorsForState; NSMutableDictionary *_trackFillColorsForState; NSMutableDictionary *_trackBackgroundColorsForState; + NSMutableDictionary *_filledTickColorsForState; + NSMutableDictionary *_backgroundTickColorsForState; } - (instancetype)initWithFrame:(CGRect)frame { @@ -97,6 +99,10 @@ - (void)commonMDCSliderInit { _trackBackgroundColorsForState = [@{} mutableCopy]; _trackBackgroundColorsForState[@(UIControlStateNormal)] = [[self class] defaultTrackOffColor]; _trackBackgroundColorsForState[@(UIControlStateDisabled)] = [[self class] defaultDisabledColor]; + _filledTickColorsForState = [@{} mutableCopy]; + _filledTickColorsForState[@(UIControlStateNormal)] = UIColor.blackColor; + _backgroundTickColorsForState = [@{} mutableCopy]; + _backgroundTickColorsForState[@(UIControlStateNormal)] = UIColor.blackColor; [self addSubview:_thumbTrack]; } @@ -167,6 +173,42 @@ - (UIColor *)thumbColorForState:(UIControlState)state { return color; } +- (void)setFilledTrackTickColor:(UIColor *)tickColor forState:(UIControlState)state { + _filledTickColorsForState[@(state)] = tickColor; + if (state == self.state) { + [self updateColorsForState]; + } +} + +- (UIColor *)filledTrackTickColorForState:(UIControlState)state { + UIColor *color = _filledTickColorsForState[@(state)]; + if (color) { + return color; + } + if (state != UIControlStateNormal) { + color = _filledTickColorsForState[@(UIControlStateNormal)]; + } + return color; +} + +- (void)setBackgroundTrackTickColor:(UIColor *)tickColor forState:(UIControlState)state { + _backgroundTickColorsForState[@(state)] = tickColor; + if (self.state == state) { + [self updateColorsForState]; + } +} + +- (UIColor *)backgroundTrackTickColorForState:(UIControlState)state { + UIColor *color = _backgroundTickColorsForState[@(state)]; + if (color) { + return color; + } + if (state != UIControlStateNormal) { + color = _backgroundTickColorsForState[@(UIControlStateNormal)]; + } + return color; +} + - (void)updateColorsForState { if (!self.isStatefulAPIEnabled) { return; @@ -183,6 +225,8 @@ - (void)updateColorsForState { // trackOnColor is null_resettable, so explicitly set to `.clear` for the correct effect _thumbTrack.trackOnColor = [self trackFillColorForState:self.state] ?: UIColor.clearColor; _thumbTrack.inkColor = self.inkColor; + _thumbTrack.trackOnTickColor = [self filledTrackTickColorForState:self.state]; + _thumbTrack.trackOffTickColor = [self backgroundTrackTickColorForState:self.state]; } #pragma mark - ThumbTrack passthrough methods diff --git a/components/Slider/tests/unit/SliderTests.m b/components/Slider/tests/unit/SliderTests.m index 37d2fc4061e..3afb88906a1 100644 --- a/components/Slider/tests/unit/SliderTests.m +++ b/components/Slider/tests/unit/SliderTests.m @@ -712,6 +712,180 @@ - (void)testSettingTrackBackgroundColorForStateHasNoEffectWhenStatefulAPIDisable [self.slider trackFillColorForState:UIControlStateDisabled]); } +#pragma mark - filledTrackTickColorForState + +- (void)testFilledTrackTickColorForStateDefaults { + // Then + NSUInteger maximumStateValue = UIControlStateNormal | UIControlStateSelected | + UIControlStateHighlighted | UIControlStateDisabled; + for (NSUInteger state = 0; state <= maximumStateValue; ++state) { + XCTAssertEqualObjects([self.slider filledTrackTickColorForState:state], + UIColor.blackColor); + } +} + +- (void)testFilledTrackTickColorForStateFallback { + // When + [self.slider setFilledTrackTickColor:UIColor.orangeColor forState:UIControlStateNormal]; + + // Then + NSUInteger maximumStateValue = UIControlStateNormal | UIControlStateSelected | + UIControlStateHighlighted | UIControlStateDisabled; + for (NSUInteger state = 0; state <= maximumStateValue; ++state) { + XCTAssertEqualObjects([self.slider filledTrackTickColorForState:state], UIColor.orangeColor, + @"(%@) is not equal to (%@) for state (%ld)", + [self.slider filledTrackTickColorForState:state], UIColor.orangeColor, + (long)state); + } +} + +- (void)testFilledTrackTickColorForStateAppliesToThumbTrack { + // Given + self.slider.statefulAPIEnabled = YES; + + // When + [self.slider setFilledTrackTickColor:UIColor.purpleColor forState:UIControlStateNormal]; + [self.slider setFilledTrackTickColor:UIColor.redColor forState:UIControlStateHighlighted]; + [self.slider setFilledTrackTickColor:UIColor.cyanColor forState:UIControlStateSelected]; + [self.slider setFilledTrackTickColor:UIColor.grayColor forState:UIControlStateDisabled]; + [self.slider setFilledTrackTickColor:UIColor.orangeColor + forState:UIControlStateHighlighted | UIControlStateSelected]; + [self.slider setFilledTrackTickColor:UIColor.yellowColor + forState:UIControlStateDisabled | UIControlStateSelected]; + + // Then + self.slider.enabled = NO; + self.slider.selected = NO; + self.slider.highlighted = NO; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOnTickColor, + [self.slider filledTrackTickColorForState:UIControlStateDisabled]); + self.slider.enabled = NO; + self.slider.selected = YES; + self.slider.highlighted = NO; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOnTickColor, + [self.slider filledTrackTickColorForState:UIControlStateDisabled | + UIControlStateSelected]); + self.slider.enabled = NO; + self.slider.selected = NO; + self.slider.highlighted = YES; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOnTickColor, + [self.slider filledTrackTickColorForState:UIControlStateDisabled]); + self.slider.enabled = NO; + self.slider.selected = YES; + self.slider.highlighted = YES; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOnTickColor, + [self.slider filledTrackTickColorForState:UIControlStateDisabled | + UIControlStateSelected]); + self.slider.enabled = YES; + self.slider.selected = NO; + self.slider.highlighted = NO; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOnTickColor, + [self.slider filledTrackTickColorForState:UIControlStateNormal]); + self.slider.enabled = YES; + self.slider.selected = YES; + self.slider.highlighted = NO; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOnTickColor, + [self.slider filledTrackTickColorForState:UIControlStateSelected]); + self.slider.enabled = YES; + self.slider.selected = NO; + self.slider.highlighted = YES; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOnTickColor, + [self.slider filledTrackTickColorForState:UIControlStateHighlighted]); + self.slider.enabled = YES; + self.slider.selected = YES; + self.slider.highlighted = YES; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOnTickColor, + [self.slider filledTrackTickColorForState:UIControlStateHighlighted | + UIControlStateSelected]); +} + +#pragma mark - backgroundTrackTickColorForState + +- (void)testBackgroundTrackTickColorForStateDefaults { + // Then + NSUInteger maximumStateValue = UIControlStateNormal | UIControlStateSelected | + UIControlStateHighlighted | UIControlStateDisabled; + for (NSUInteger state = 0; state <= maximumStateValue; ++state) { + XCTAssertEqualObjects([self.slider backgroundTrackTickColorForState:state], + UIColor.blackColor); + } +} + +- (void)testBackgroundTrackTickColorForStateFallback { + // When + [self.slider setBackgroundTrackTickColor:UIColor.orangeColor forState:UIControlStateNormal]; + + // Then + NSUInteger maximumStateValue = UIControlStateNormal | UIControlStateSelected | + UIControlStateHighlighted | UIControlStateDisabled; + for (NSUInteger state = 0; state <= maximumStateValue; ++state) { + XCTAssertEqualObjects([self.slider backgroundTrackTickColorForState:state], UIColor.orangeColor, + @"(%@) is not equal to (%@) for state (%ld)", + [self.slider backgroundTrackTickColorForState:state], UIColor.orangeColor, + (long)state); + } +} + +- (void)testBackgroundTrackTickColorForStateAppliesToThumbTrack { + // Given + self.slider.statefulAPIEnabled = YES; + + // When + [self.slider setBackgroundTrackTickColor:UIColor.purpleColor forState:UIControlStateNormal]; + [self.slider setBackgroundTrackTickColor:UIColor.redColor forState:UIControlStateHighlighted]; + [self.slider setBackgroundTrackTickColor:UIColor.cyanColor forState:UIControlStateSelected]; + [self.slider setBackgroundTrackTickColor:UIColor.grayColor forState:UIControlStateDisabled]; + [self.slider setBackgroundTrackTickColor:UIColor.orangeColor + forState:UIControlStateHighlighted | UIControlStateSelected]; + [self.slider setBackgroundTrackTickColor:UIColor.yellowColor + forState:UIControlStateDisabled | UIControlStateSelected]; + + // Then + self.slider.enabled = NO; + self.slider.selected = NO; + self.slider.highlighted = NO; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOffTickColor, + [self.slider backgroundTrackTickColorForState:UIControlStateDisabled]); + self.slider.enabled = NO; + self.slider.selected = YES; + self.slider.highlighted = NO; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOffTickColor, + [self.slider backgroundTrackTickColorForState:UIControlStateDisabled | + UIControlStateSelected]); + self.slider.enabled = NO; + self.slider.selected = NO; + self.slider.highlighted = YES; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOffTickColor, + [self.slider backgroundTrackTickColorForState:UIControlStateDisabled]); + self.slider.enabled = NO; + self.slider.selected = YES; + self.slider.highlighted = YES; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOffTickColor, + [self.slider backgroundTrackTickColorForState:UIControlStateDisabled | + UIControlStateSelected]); + self.slider.enabled = YES; + self.slider.selected = NO; + self.slider.highlighted = NO; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOffTickColor, + [self.slider backgroundTrackTickColorForState:UIControlStateNormal]); + self.slider.enabled = YES; + self.slider.selected = YES; + self.slider.highlighted = NO; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOffTickColor, + [self.slider backgroundTrackTickColorForState:UIControlStateSelected]); + self.slider.enabled = YES; + self.slider.selected = NO; + self.slider.highlighted = YES; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOffTickColor, + [self.slider backgroundTrackTickColorForState:UIControlStateHighlighted]); + self.slider.enabled = YES; + self.slider.selected = YES; + self.slider.highlighted = YES; + XCTAssertEqualObjects(self.slider.thumbTrack.trackOffTickColor, + [self.slider backgroundTrackTickColorForState:UIControlStateHighlighted | + UIControlStateSelected]); +} + #pragma mark - InkColor - (void)testInkColorDefault {