Skip to content

Commit

Permalink
[Slider] Move private MDCDiscreteDotView class into its own header an…
Browse files Browse the repository at this point in the history
…d implementation files.

PiperOrigin-RevId: 318375981
  • Loading branch information
bryanoltman authored and material-automation committed Jun 25, 2020
1 parent b3a632d commit ba63649
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 113 deletions.
90 changes: 0 additions & 90 deletions components/private/ThumbTrack/src/MDCThumbTrack.m
Original file line number Diff line number Diff line change
Expand Up @@ -63,96 +63,6 @@
return [[MDCTypography fontLoader] regularFontOfSize:12];
}

@implementation MDCDiscreteDotView

- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor clearColor];
_inactiveDotColor = UIColor.blackColor;
_activeDotColor = UIColor.blackColor;
_activeDotsSegment = CGRectMake(CGFLOAT_MIN, 0, 0, 0);
}
return self;
}

- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
[self setNeedsDisplay];
}

- (void)setActiveDotColor:(UIColor *)activeDotColor {
_activeDotColor = activeDotColor;
[self setNeedsDisplay];
}

- (void)setInactiveDotColor:(UIColor *)inactiveDotColor {
_inactiveDotColor = inactiveDotColor;
[self setNeedsDisplay];
}

- (void)setActiveDotsSegment:(CGRect)activeDotsSegment {
CGFloat newMinX = MAX(0, MIN(1, CGRectGetMinX(activeDotsSegment)));
CGFloat newMaxX = MIN(1, MAX(0, CGRectGetMaxX(activeDotsSegment)));

_activeDotsSegment = CGRectMake(newMinX, 0, (newMaxX - newMinX), 0);
[self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect {
[super drawRect:rect];

if (_numDiscreteDots >= 2) {
CGContextRef contextRef = UIGraphicsGetCurrentContext();

// The "dot" is a circle that gradually transforms into a rounded rectangle.
// * At 1- and 2-point track heights, use a circle filling the height.
// * At 3- and 4-point track heights, use a vertically-centered circle 2 points tall.
// * At greater track heights, create a vertically-centered rounded rectangle 2-points wide
// and half the track height.
CGFloat trackHeight = CGRectGetHeight(self.bounds);
CGFloat dotHeight = MIN(2, trackHeight);
CGFloat dotWidth = MIN(2, trackHeight);
CGFloat circleOriginY = (trackHeight - dotHeight) / 2;
if (trackHeight > 4) {
dotHeight = trackHeight / 2;
circleOriginY = (trackHeight - dotHeight) / 2;
}
CGRect dotRect = CGRectMake(0, (trackHeight - dotHeight) / 2, dotWidth, dotHeight);
// Increment within the bounds
CGFloat absoluteIncrement = (CGRectGetWidth(self.bounds) - dotWidth) / (_numDiscreteDots - 1);
// Increment within 0..1
CGFloat relativeIncrement = (CGFloat)1.0 / (_numDiscreteDots - 1);

// Allow an extra 10% of the increment to guard against rounding errors excluding dots that
// should genuinely be within the active segment.
CGFloat minActiveX = CGRectGetMinX(self.activeDotsSegment) - relativeIncrement * (CGFloat)0.1;
CGFloat maxActiveX = CGRectGetMaxX(self.activeDotsSegment) + relativeIncrement * (CGFloat)0.1;
for (NSUInteger i = 0; i < _numDiscreteDots; i++) {
CGFloat relativePosition = i * relativeIncrement;
if (minActiveX <= relativePosition && maxActiveX >= relativePosition) {
[self.activeDotColor setFill];
} else {
[self.inactiveDotColor setFill];
}
dotRect.origin.x = (i * absoluteIncrement);
// Clear any previous paths from the context
CGContextBeginPath(contextRef);
CGPathRef rectPathRef =
CGPathCreateWithRoundedRect(dotRect, dotWidth / 2, dotWidth / 2, NULL);
CGContextAddPath(contextRef, rectPathRef);
CGContextFillPath(contextRef);
CGPathRelease(rectPathRef);
}
}
}

- (void)setNumDiscreteDots:(NSUInteger)numDiscreteDots {
_numDiscreteDots = numDiscreteDots;
[self setNeedsDisplay];
}

@end

// TODO(iangordon): Properly handle broken tgmath

Expand Down
37 changes: 37 additions & 0 deletions components/private/ThumbTrack/src/private/MDCDiscreteDotView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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 <UIKit/UIKit.h>

// Credit to the Beacon Tools iOS team for the idea for this implementations
@interface MDCDiscreteDotView : UIView

@property(nonatomic, assign) NSUInteger numDiscreteDots;

/** The color of dots within the @c activeDotsSegment bounds. Defaults to black. */
@property(nonatomic, strong, nonnull) UIColor *activeDotColor;

/** The color of dots outside the @c activeDotsSegment bounds. Defaults to black. */
@property(nonatomic, strong, nonnull) UIColor *inactiveDotColor;

/**
The segment of the track that uses @c activeDotColor. The horizontal dimension should be bound
to [0..1]. The vertical dimension is ignored.
@note Only the @c origin.x and @c size.width are used to determine whether a dot is in the active
segment.
*/
@property(nonatomic, assign) CGRect activeDotsSegment;

@end
106 changes: 106 additions & 0 deletions components/private/ThumbTrack/src/private/MDCDiscreteDotView.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// 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 "MDCDiscreteDotView.h"

@implementation MDCDiscreteDotView

- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor clearColor];
_inactiveDotColor = UIColor.blackColor;
_activeDotColor = UIColor.blackColor;
_activeDotsSegment = CGRectMake(CGFLOAT_MIN, 0, 0, 0);
}
return self;
}

- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
[self setNeedsDisplay];
}

- (void)setActiveDotColor:(UIColor *)activeDotColor {
_activeDotColor = activeDotColor;
[self setNeedsDisplay];
}

- (void)setInactiveDotColor:(UIColor *)inactiveDotColor {
_inactiveDotColor = inactiveDotColor;
[self setNeedsDisplay];
}

- (void)setActiveDotsSegment:(CGRect)activeDotsSegment {
CGFloat newMinX = MAX(0, MIN(1, CGRectGetMinX(activeDotsSegment)));
CGFloat newMaxX = MIN(1, MAX(0, CGRectGetMaxX(activeDotsSegment)));

_activeDotsSegment = CGRectMake(newMinX, 0, (newMaxX - newMinX), 0);
[self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect {
[super drawRect:rect];

if (_numDiscreteDots >= 2) {
CGContextRef contextRef = UIGraphicsGetCurrentContext();

// The "dot" is a circle that gradually transforms into a rounded rectangle.
// * At 1- and 2-point track heights, use a circle filling the height.
// * At 3- and 4-point track heights, use a vertically-centered circle 2 points tall.
// * At greater track heights, create a vertically-centered rounded rectangle 2-points wide
// and half the track height.
CGFloat trackHeight = CGRectGetHeight(self.bounds);
CGFloat dotHeight = MIN(2, trackHeight);
CGFloat dotWidth = MIN(2, trackHeight);
CGFloat circleOriginY = (trackHeight - dotHeight) / 2;
if (trackHeight > 4) {
dotHeight = trackHeight / 2;
circleOriginY = (trackHeight - dotHeight) / 2;
}
CGRect dotRect = CGRectMake(0, (trackHeight - dotHeight) / 2, dotWidth, dotHeight);
// Increment within the bounds
CGFloat absoluteIncrement = (CGRectGetWidth(self.bounds) - dotWidth) / (_numDiscreteDots - 1);
// Increment within 0..1
CGFloat relativeIncrement = (CGFloat)1.0 / (_numDiscreteDots - 1);

// Allow an extra 10% of the increment to guard against rounding errors excluding dots that
// should genuinely be within the active segment.
CGFloat minActiveX = CGRectGetMinX(self.activeDotsSegment) - relativeIncrement * (CGFloat)0.1;
CGFloat maxActiveX = CGRectGetMaxX(self.activeDotsSegment) + relativeIncrement * (CGFloat)0.1;
for (NSUInteger i = 0; i < _numDiscreteDots; i++) {
CGFloat relativePosition = i * relativeIncrement;
if (minActiveX <= relativePosition && maxActiveX >= relativePosition) {
[self.activeDotColor setFill];
} else {
[self.inactiveDotColor setFill];
}
dotRect.origin.x = (i * absoluteIncrement);
// Clear any previous paths from the context
CGContextBeginPath(contextRef);
CGPathRef rectPathRef =
CGPathCreateWithRoundedRect(dotRect, dotWidth / 2, dotWidth / 2, NULL);
CGContextAddPath(contextRef, rectPathRef);
CGContextFillPath(contextRef);
CGPathRelease(rectPathRef);
}
}
}

- (void)setNumDiscreteDots:(NSUInteger)numDiscreteDots {
_numDiscreteDots = numDiscreteDots;
[self setNeedsDisplay];
}

@end
26 changes: 3 additions & 23 deletions components/private/ThumbTrack/src/private/MDCThumbTrack+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#import "MDCNumericValueLabel.h"
#import "MDCThumbTrack.h"
#import "MaterialInk.h"

// Credit to the Beacon Tools iOS team for the idea for this implementations
@interface MDCDiscreteDotView : UIView

@property(nonatomic, assign) NSUInteger numDiscreteDots;

/** The color of dots within the @c activeDotsSegment bounds. Defaults to black. */
@property(nonatomic, strong, nonnull) UIColor *activeDotColor;

/** The color of dots outside the @c activeDotsSegment bounds. Defaults to black. */
@property(nonatomic, strong, nonnull) UIColor *inactiveDotColor;

/**
The segment of the track that uses @c activeDotColor. The horizontal dimension should be bound
to [0..1]. The vertical dimension is ignored.
@note Only the @c origin.x and @c size.width are used to determine whether a dot is in the active
segment.
*/
@property(nonatomic, assign) CGRect activeDotsSegment;

@end
#import "MaterialInk.h"
#import "MDCNumericValueLabel.h"
#import "MDCDiscreteDotView.h"

@interface MDCThumbTrack (Private)

Expand Down

0 comments on commit ba63649

Please sign in to comment.