不允许在 Circular Slider iOS 中从 1% 滑动到 100%,反之亦然

Posted

技术标签:

【中文标题】不允许在 Circular Slider iOS 中从 1% 滑动到 100%,反之亦然【英文标题】:Not allow slide from 1% to 100% and vice versa in Circular Slider iOS 【发布时间】:2014-12-25 17:05:08 【问题描述】:

我正在使用这个类来创建一个圆形滑块,但是我有一个问题,当用户从 1% 滑到 100% 时,如何不允许,反之亦然?请帮我解决这个问题。提前致谢。

代码如下:

@interface SLCircularSlider()

@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 SLCircularSlider

@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];
        if (self.isContinuous) 
            [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.minimumTrackTintColor = [UIColor clearColor];
    self.maximumTrackTintColor = [UIColor clearColor];
    self.thumbTintColor = [UIColor clearColor];

    self.continuous = YES;
    self.thumbCenterPoint = CGPointZero;

    /**
     * This tapGesture isn't used yet but will allow to jump to a specific location in the circle
     */
    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];
   /* UILabel* percentlbl =(UILabel*) [self.superview viewWithTag:10];
    NSLog(@"percentlbl frame %f",percentlbl.frame.origin.y
          );
    if (CGRectContainsPoint(percentlbl.frame, tapLocation)) 
        NSLog(@"Tapped label");
    */


    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;
        
        case UIGestureRecognizerStateEnded:
            if (!self.isContinuous) 
                [self sendActionsForControlEvents:UIControlEventValueChanged];
            
            if ([self isPointInThumb:tapLocation]) 
                [self sendActionsForControlEvents:UIControlEventTouchUpInside];
            
            else 
                [self sendActionsForControlEvents:UIControlEventTouchUpOutside];
            
            break;
        default:
            break;
    

- (void)tapGestureHappened:(UITapGestureRecognizer *)tapGestureRecognizer 
    if (tapGestureRecognizer.state == UIGestureRecognizerStateEnded) 
        CGPoint tapLocation = [tapGestureRecognizer locationInView:self];
        if ([self isPointInThumb:tapLocation]) 
        
        else 
        
    


/** @name Touches Methods */
#pragma mark - Touches Methods
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
    [super touchesBegan:touches withEvent:event];

    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInView:self];
    if ([self isPointInThumb:touchLocation]) 
        [self sendActionsForControlEvents:UIControlEventTouchDown];
    


@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;

【问题讨论】:

@synthesize 的用法很奇怪。 【参考方案1】:

您需要做的是在手势识别中添加一个阶段,您可以在其中确定用户是在增加还是减少计数器。如果减少,一旦值降至零以下,不要响应减少的变化(您可能希望能够达到零,而不是停止在 1,尽管问题说了什么?)。如果增加,一旦值达到 100%,不要响应增加的变化。当然,您需要确保在最大情况下,如果用户更改为减小值,并且在为零时增加值,您仍能继续响应。

【讨论】:

以上是关于不允许在 Circular Slider iOS 中从 1% 滑动到 100%,反之亦然的主要内容,如果未能解决你的问题,请参考以下文章

iOS 音频通话 APP 使用 Circular Buffer 的原因是啥?

IOS 监听slider滑动

iOS 设备中 Flutter TabBarView 覆盖底部区域(Slider)

Slider FileReader JS 多张图片上传(递增索引)

iOS—UI-手势及控件(segment switch slider)的使用

AutoLayout Circular UIButton - 等效垂直和水平尺寸