美文网首页
iOS 圆环型滑块(Circle Slider)

iOS 圆环型滑块(Circle Slider)

作者: Jixin | 来源:发表于2017-06-12 16:10 被阅读1995次

    一、圆环型滑块的设计

    • 最近设计师设计了一个圆环型滑块,其作用和UISlider差不多,用于拖动改变播放音频的进度和指示音频的播放进度。
    • 大概的样子如下图:


      图一 设计图
    • 有如下的特点:
      • 滑动的响应区域为圆环上,并且靠近滑块;
      • 点击滑块时,滑块有一个放大的动画,手指离开屏幕时滑块恢复原来大小;
      • 当value=0%时,滑块不可以再逆时针滑动,当value=100%时,滑块不可以再顺时针滑动。
      • 最后我自己再加了一个设计,指示音频loading的进度(这样以来圆环就有了三层)。

    二、成果展示Demo

    Github 传送门

    1. 重复滑动
    图二 重复滑动
    1. 限定360度和有loading的进度
    图三 限定360度

    三、接入使用

    1. 接入

    直接将我的项目中ZCircleSlider文件夹中的ZCircleSlider.h 和 ZCircleSlider.m拖到项目中即可

    2.使用

    //ZCircleSlider的背景色是透明的,不会挡住下面View
    - (ZCircleSlider *)circleSlider {
        if (!_circleSlider) {
            _circleSlider = [[ZCircleSlider alloc] initWithFrame:CGRectMake((kScreenWidth - 300) / 2.0, (kScreenHeight - 300) / 2.0, 300, 300)];
            //走过的进度的颜色
            _circleSlider.minimumTrackTintColor = kUIColorFromRGB(0x1482f0);
            //loading进度的颜色。如果loading = 1,即loading完成,那么也是圆环的颜色,同于backgroundTintColor
            _circleSlider.maximumTrackTintColor = kUIColorFromRGB(0xE62E2E);
            //圆环的颜色
            _circleSlider.backgroundTintColor = [UIColor colorWithWhite:0 alpha:0.2];
            //圆环的宽
            _circleSlider.circleBorderWidth = 5.0f;
            //圆形滑块的半径
            _circleSlider.thumbRadius = 8;
            //圆形滑块放大效果的半径
            _circleSlider.thumbExpandRadius = 12.5;
            //圆形滑块的颜色
            _circleSlider.thumbTintColor = [UIColor redColor];
            //圆环的半径
            _circleSlider.circleRadius = 260 / 2.0 + 2;
            //设定初始值value = 0
            _circleSlider.value = 0;
            //设定loadingProgress的初始值 = 0
            _circleSlider.loadProgress = 0;
            //开始点击,响应事件
            [_circleSlider addTarget:self
                              action:@selector(circleSliderTouchDown:)
                    forControlEvents:UIControlEventTouchDown];
            //拖动过程中,响应事件
            [_circleSlider addTarget:self
                              action:@selector(circleSliderValueChanging:)
                    forControlEvents:UIControlEventValueChanged];
            //拖动结束,响应事件
            [_circleSlider addTarget:self
                              action:@selector(circleSliderValueDidChanged:)
                    forControlEvents:UIControlEventTouchUpInside];
        }
        return _circleSlider;
    }
    
    #pragma mark - action
    
    /*以下三个方法,都要添加对slider.interaction的判断。
     *因为虽然看起来是个圆环,但是响应手势的区域确实整个矩形的View
     *在内部添加了interaction这个属性用于限定响应区域
     */
    - (IBAction)circleSliderTouchDown:(ZCircleSlider *)slider {
        if (!slider.interaction) {
            return;
        }
        
    }
    
    - (IBAction)circleSliderValueChanging:(ZCircleSlider *)slider {
        if (!slider.interaction) {
            return;
        }
        self.currentValueLabel.text = [NSString stringWithFormat:@"当前值:%.0f",slider.value * 100];
        self.progressSlider.value = slider.value;
    }
    
    - (IBAction)circleSliderValueDidChanged:(ZCircleSlider *)slider {
        if (!slider.interaction) {
            return;
        }
        self.finalValueLabel.text = [NSString stringWithFormat:@"最终值:%.0f",slider.value * 100];
    }
    
    

    四、实现原理

    简单来说就是,整个控件继承与UIControl,根据value,loadProgress的值改变重新绘制layer和改变thumb的位置;根据手势所在的位置,重新绘制layer和改变thumb的位置,并改变value。

    1. 根据所给value绘制圆弧

    circleSlider.value和circleSlider.loadProgress,都是绘制圆弧。

    1)在- (void)drawRect:(CGRect)rect;方法中绘制圆弧

    在iOS中,圆的0弧度的位置是圆心(x,y)的正右侧(x+r,y)
    这里我选择起始位置为-M_PI_2弧度,即圆心的正上方(x,y-r);
    弧长对应的变量就是运动的点相对于起点旋转过的角度,而这个角度就等于value/1.0 * 360
    下面给出了加载进度圆弧的绘制方法,value的圆弧绘制方法同理

    - (void)drawRect:(CGRect)rect {
        //加载的进度
        UIBezierPath *loadPath = [UIBezierPath bezierPath];
        CGFloat loadStart = -M_PI_2;
        CGFloat loadCurre = loadStart + 2 * M_PI * self.loadProgress;
        
        [loadPath addArcWithCenter:self.drawCenter
                            radius:self.radius
                        startAngle:loadStart
                          endAngle:loadCurre
                         clockwise:YES];
        CGContextSaveGState(ctx);
        CGContextSetShouldAntialias(ctx, YES);
        CGContextSetLineWidth(ctx, self.circleBorderWidth);
        CGContextSetStrokeColorWithColor(ctx, self.maximumTrackTintColor.CGColor);
        CGContextAddPath(ctx, loadPath.CGPath);
        CGContextDrawPath(ctx, kCGPathStroke);
        CGContextRestoreGState(ctx);
    }
    

    2) 在值改变的时候重新绘制layer

    在值改变的时候调用setNeedsDisplay方法重新绘制layer

    - (void)setLoadProgress:(float)loadProgress {
        _loadProgress = loadProgress;
        [self setNeedsDisplay];
    }
    
    

    2.拖动控制,以及保证thumb(滑块的那个圆点)在圆弧上运动

    这里主要给出了解决的思路方法,具体的实现可到Github中查看

    主要是在UIControl的以下三个方法上做文章:

    //点击开始
    - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event;
    
    //拖动过程中
    - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event;
    
    //拖动结束
    - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event;
    

    1) 对于拖动控制,以及保证thumb在圆弧上运动可以通过以下这道数学题做抽象

    1.设定平面直角坐标系原点为O(0,0),向右为x轴的正方向,向下为y轴的正方向;--------(iOS的屏幕坐标系)
    2.在坐标系中有一个已知的圆C(a,b),半径为r;--------(圆环型Slider)
    3.平面内任意一点S(m,n);--------(点击的位置)
    求:
        1)点S到圆C的最短距离是否小于44;(限定点击响应的区域为圆弧内外44个点的区域)
        2)线段SC与圆的交点T(xT,yT);(thumb的位置,肯定在圆弧上)
        3)线段ST的距离是否小于44;(限定点击开始时的响应区域为以thumb圆心,半径为44的圆以内)
    
    

    2) 对于当value=0%时,滑块不可以再逆时针滑动,当value=100%时,滑块不可以再顺时针滑动。

    1. 平面内一个圆C半径为r,其圆心位于坐标系原点,即C(0,0),圆弧上有一点T(x,y);
    2. 坐标系可分为第一、二、三和四象限。
        1)那么当点T相对于起始点(0,-r),顺时针转过的角度<60度时,禁止移动到第二,三,四象限;
        2)当点T相对于起始点(0,-r),顺时针转过角度>300度时,禁止移动到第一,二,三象限;
    
    

    相关文章

      网友评论

          本文标题:iOS 圆环型滑块(Circle Slider)

          本文链接:https://www.haomeiwen.com/subject/ouelqxtx.html