IOS 自定义圆形进度条UISlider

发布时间:2020-04-18 18:02:04 作者:RoderickKennedy
来源:网络 阅读:1745

#import <UIKit/UIKit.h>


/** @name Constants */

/**

 * The styles permitted for the circular progress view.

 *

 * You can set and retrieve the current style of progress view through the progressViewStyle property.

 */

typedef enum {

UICircularSliderStyleCircle,

UICircularSliderStylePie,

} UICircularSliderStyle;


@interface UICircularSlider : UIControl


/**

 * The current value of the receiver.

 *

 * Setting this property causes the receiver to redraw itself using the new value.

 * If you try to set a value that is below the minimum or above the maximum value, the minimum or maximum value is set instead. The default value of this property is 0.0.

 */

@property (nonatomic) float value;


/**

 * The minimum value of the receiver.

 * 

 * If you change the value of this property, and the current value of the receiver is below the new minimum, the current value is adjusted to match the new minimum value automatically.

 * The default value of this property is 0.0.

 */

@property (nonatomic) float minimumValue;


/**

 * The maximum value of the receiver.

 * 

 * If you change the value of this property, and the current value of the receiver is above the new maximum, the current value is adjusted to match the new maximum value automatically.

 * The default value of this property is 1.0.

 */

@property (nonatomic) float maximumValue;


/**

 * The color shown for the portion of the slider that is filled.

 */

@property(nonatomic, retain) UIColor *minimumTrackTintColor;


/**

 * The color shown for the portion of the slider that is not filled.

 */

@property(nonatomic, retain) UIColor *maximumTrackTintColor;


/**

 * The color used to tint the standard thumb.

 */

@property(nonatomic, retain) UIColor *thumbTintColor;


/**

 * Contains a Boolean value indicating whether changes in the sliders value generate continuous update events.

 *

 * If YES, the slider sends update events continuously to the associated target’s action method.

 * If NO, the slider only sends an action event when the user releases the slider’s thumb control to set the final value.

 * The default value of this property is YES.

 *

 * @warning Not implemented Yet.

 */

@property(nonatomic, getter=isContinuous) BOOL continuous;


/**

 * The current graphical style of the receiver.

 *

 * The value of this property is a constant that specifies the style of the slider.

 The default style is UICircularSliderStyleCircle.

 * For more on these constants, see UICircularSliderStyle.

 */

@property (nonatomic) UICircularSliderStyle sliderStyle;


@end



/** @name Utility Functions */

#pragma mark - Utility Functions

/**

 * Translate a value in a source interval to a destination interval

 * @param sourceValue The source value to translate

 * @param sourceIntervalMinimum The minimum value in the source interval

 * @param sourceIntervalMaximum The maximum value in the source interval

 * @param destinationIntervalMinimum The minimum value in the destination interval

 * @param destinationIntervalMaximum The maximum value in the destination interval

 * @return The value in the destination interval

 *

 * This function uses the linear function method, a.k.a. resolves the y=ax+b equation where y is a destination value and x a source value

 * Formulas : a = (dMax - dMin) / (sMax - sMin)

 * b = dMax - a*sMax = dMin - a*sMin

 */

float translateValueFromSourceIntervalToDestinationInterval(float sourceValue, float sourceIntervalMinimum, float sourceIntervalMaximum, float destinationIntervalMinimum, float destinationIntervalMaximum);

/**

 * Returns the smallest angle between three points, one of them clearly indicated as the "junction point" or "center point".

 * @param centerPoint The "center point" or "junction point"

 * @param p1 The first point, member of the [centerPoint p1] segment

 * @param p2 The second point, member of the [centerPoint p2] segment

 * @return The angle between those two segments

 

 * This function uses the properties of the triangle and arctan (atan2f) function to calculate the angle.

 */

CGFloat angleBetweenThreePoints(CGPoint centerPoint, CGPoint p1, CGPoint p2);





#import "UICircularSlider.h"


@interface UICircularSlider()


@property (nonatomic) CGPoint thumbCenterPoint;


#pragma mark - Init and Setup methods

- (void)setup;


#pragma mark - Thumb management methods

- (BOOL)isPointInThumb:(CGPoint)point;


#pragma mark - Drawing methods

- (CGFloat)sliderRadius;

- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext:(CGContextRef)context;

- (CGPoint)drawCircularTrack:(float)track atPoint:(CGPoint)point withRadius:(CGFloat)radius inContext:(CGContextRef)context;

- (CGPoint)drawPieTrack:(float)track atPoint:(CGPoint)point withRadius:(CGFloat)radius inContext:(CGContextRef)context;


@end


#pragma mark -

@implementation UICircularSlider


@synthesize value = _value;

- (void)setValue:(float)value {

if (value != _value) {

if (value > self.maximumValue) { value = self.maximumValue; }

if (value < self.minimumValue) { value = self.minimumValue; }

_value = value;

[self setNeedsDisplay];

[self sendActionsForControlEvents:UIControlEventValueChanged];

}

}

@synthesize minimumValue = _minimumValue;

- (void)setMinimumValue:(float)minimumValue {

if (minimumValue != _minimumValue) {

_minimumValue = minimumValue;

if (self.maximumValue < self.minimumValue) { self.maximumValue = self.minimumValue; }

if (self.value < self.minimumValue) { self.value = self.minimumValue; }

}

}

@synthesize maximumValue = _maximumValue;

- (void)setMaximumValue:(float)maximumValue {

if (maximumValue != _maximumValue) {

_maximumValue = maximumValue;

if (self.minimumValue > self.maximumValue) { self.minimumValue = self.maximumValue; }

if (self.value > self.maximumValue) { self.value = self.maximumValue; }

}

}


@synthesize minimumTrackTintColor = _minimumTrackTintColor;

- (void)setMinimumTrackTintColor:(UIColor *)minimumTrackTintColor {

if (![minimumTrackTintColor isEqual:_minimumTrackTintColor]) {

_minimumTrackTintColor = minimumTrackTintColor;

[self setNeedsDisplay];

}

}


@synthesize maximumTrackTintColor = _maximumTrackTintColor;

- (void)setMaximumTrackTintColor:(UIColor *)maximumTrackTintColor {

if (![maximumTrackTintColor isEqual:_maximumTrackTintColor]) {

_maximumTrackTintColor = maximumTrackTintColor;

[self setNeedsDisplay];

}

}


@synthesize thumbTintColor = _thumbTintColor;

- (void)setThumbTintColor:(UIColor *)thumbTintColor {

if (![thumbTintColor isEqual:_thumbTintColor]) {

_thumbTintColor = thumbTintColor;

[self setNeedsDisplay];

}

}


@synthesize continuous = _continuous;


@synthesize sliderStyle = _sliderStyle;

- (void)setSliderStyle:(UICircularSliderStyle)sliderStyle {

if (sliderStyle != _sliderStyle) {

_sliderStyle = sliderStyle;

[self setNeedsDisplay];

}

}


@synthesize thumbCenterPoint = _thumbCenterPoint;


/** @name Init and Setup methods */

#pragma mark - Init and Setup methods

- (id)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];

    if (self) {

        [self setup];

    } 

    return self;

}

- (void)awakeFromNib {

[self setup];

}


- (void)setup {

self.value = 0.0;

self.minimumValue = 0.0;

self.maximumValue = 1.0;

self.minimumTrackTintColor = [UIColor blueColor];

self.maximumTrackTintColor = [UIColor whiteColor];

self.thumbTintColor = [UIColor darkGrayColor];

self.continuous = YES;

self.thumbCenterPoint = CGPointZero;

UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureHappened:)];

[self addGestureRecognizer:tapGestureRecognizer];

UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureHappened:)];

panGestureRecognizer.maximumNumberOfTouches = panGestureRecognizer.minimumNumberOfTouches;

[self addGestureRecognizer:panGestureRecognizer];

}


/** @name Drawing methods */

#pragma mark - Drawing methods

#define kLineWidth 5.0

#define kThumbRadius 12.0

- (CGFloat)sliderRadius {

CGFloat radius = MIN(self.bounds.size.width/2, self.bounds.size.height/2);

radius -= MAX(kLineWidth, kThumbRadius);

return radius;

}

- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext:(CGContextRef)context {

UIGraphicsPushContext(context);

CGContextBeginPath(context);

CGContextMoveToPoint(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y);

CGContextAddArc(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y, kThumbRadius, 0.0, 2*M_PI, NO);

CGContextFillPath(context);

UIGraphicsPopContext();

}


- (CGPoint)drawCircularTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius inContext:(CGContextRef)context {

UIGraphicsPushContext(context);

CGContextBeginPath(context);

float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, 2*M_PI);

CGFloat startAngle = -M_PI_2;

CGFloat endAngle = startAngle + angleFromTrack;

CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, NO);

CGPoint arcEndPoint = CGContextGetPathCurrentPoint(context);

CGContextStrokePath(context);

UIGraphicsPopContext();

return arcEndPoint;

}


- (CGPoint)drawPieTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius inContext:(CGContextRef)context {

UIGraphicsPushContext(context);

float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, 2*M_PI);

CGFloat startAngle = -M_PI_2;

CGFloat endAngle = startAngle + angleFromTrack;

CGContextMoveToPoint(context, center.x, center.y);

CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, NO);

CGPoint arcEndPoint = CGContextGetPathCurrentPoint(context);

CGContextClosePath(context);

CGContextFillPath(context);

UIGraphicsPopContext();

return arcEndPoint;

}


- (void)drawRect:(CGRect)rect {

    CGContextRef context = UIGraphicsGetCurrentContext();

CGPoint middlePoint;

middlePoint.x = self.bounds.origin.x + self.bounds.size.width/2;

middlePoint.y = self.bounds.origin.y + self.bounds.size.height/2;

CGContextSetLineWidth(context, kLineWidth);

CGFloat radius = [self sliderRadius];

switch (self.sliderStyle) {

case UICircularSliderStylePie:

[self.maximumTrackTintColor setFill];

[self drawPieTrack:self.maximumValue atPoint:middlePoint withRadius:radius inContext:context];

[self.minimumTrackTintColor setStroke];

[self drawCircularTrack:self.maximumValue atPoint:middlePoint withRadius:radius inContext:context];

[self.minimumTrackTintColor setFill];

self.thumbCenterPoint = [self drawPieTrack:self.value atPoint:middlePoint withRadius:radius inContext:context];

break;

case UICircularSliderStyleCircle:

default:

[self.maximumTrackTintColor setStroke];

[self drawCircularTrack:self.maximumValue atPoint:middlePoint withRadius:radius inContext:context];

[self.minimumTrackTintColor setStroke];

self.thumbCenterPoint = [self drawCircularTrack:self.value atPoint:middlePoint withRadius:radius inContext:context];

break;

}

[self.thumbTintColor setFill];

[self drawThumbAtPoint:self.thumbCenterPoint inContext:context];

}


/** @name Thumb management methods */

#pragma mark - Thumb management methods

- (BOOL)isPointInThumb:(CGPoint)point {

CGRect thumbTouchRect = CGRectMake(self.thumbCenterPoint.x - kThumbRadius, self.thumbCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2);

return CGRectContainsPoint(thumbTouchRect, point);

}


/** @name UIGestureRecognizer management methods */

#pragma mark - UIGestureRecognizer management methods

- (void)panGestureHappened:(UIPanGestureRecognizer *)panGestureRecognizer {

CGPoint tapLocation = [panGestureRecognizer locationInView:self];

switch (panGestureRecognizer.state) {

case UIGestureRecognizerStateChanged: {

CGFloat radius = [self sliderRadius];

CGPoint sliderCenter = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);

CGPoint sliderStartPoint = CGPointMake(sliderCenter.x, sliderCenter.y - radius);

CGFloat angle = angleBetweenThreePoints(sliderCenter, sliderStartPoint, tapLocation);

if (angle < 0) {

angle = -angle;

}

else {

angle = 2*M_PI - angle;

}

self.value = translateValueFromSourceIntervalToDestinationInterval(angle, 0, 2*M_PI, self.minimumValue, self.maximumValue);

break;

}

default:

break;

}

}

- (void)tapGestureHappened:(UITapGestureRecognizer *)tapGestureRecognizer {

if (tapGestureRecognizer.state == UIGestureRecognizerStateEnded) {

CGPoint tapLocation = [tapGestureRecognizer locationInView:self];

if ([self isPointInThumb:tapLocation]) {

}

else {

}

}

}


@end


/** @name Utility Functions */

#pragma mark - Utility Functions

float translateValueFromSourceIntervalToDestinationInterval(float sourceValue, float sourceIntervalMinimum, float sourceIntervalMaximum, float destinationIntervalMinimum, float destinationIntervalMaximum) {

float a, b, destinationValue;

a = (destinationIntervalMaximum - destinationIntervalMinimum) / (sourceIntervalMaximum - sourceIntervalMinimum);

b = destinationIntervalMaximum - a*sourceIntervalMaximum;

destinationValue = a*sourceValue + b;

return destinationValue;

}


CGFloat angleBetweenThreePoints(CGPoint centerPoint, CGPoint p1, CGPoint p2) {

CGPoint v1 = CGPointMake(p1.x - centerPoint.x, p1.y - centerPoint.y);

CGPoint v2 = CGPointMake(p2.x - centerPoint.x, p2.y - centerPoint.y);

CGFloat angle = atan2f(v2.x*v1.y - v1.x*v2.y, v1.x*v2.x + v1.y*v2.y);

return angle;

}






推荐阅读:
  1. UIView类图(IOS)
  2. iOS 常用控件

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

ios 进度条 自定义uislider

上一篇:Android学习——HorizontalScollview水平滚动控件

下一篇:三个重要的事件来看软件测试的重要性和软件测试的行情

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》