iOS 圆环型滑块(Circle Slider)

一、圆环型滑块的设计

  • 最近设计师设计了一个圆环型滑块,其作用和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度时,禁止移动到第一,二,三象限;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,596评论 4 59
  • 三月,惊蜇。 一声春雷,万物复苏了。南方有句农谚:雷打惊蜇前,四十八天雨绵绵。前些天跟一个南方的朋友通...
    川江水阅读 189评论 0 1
  • “快跑——!快跑——”尾音未落,这个仿佛要将嗓子撕裂般吼叫出来的声音便在瞬间湮没在了熊熊燃烧的大火之中。 少女的两...
    ea3a431876df阅读 383评论 0 1
  • 参加早起群已经有六天了,之前就有早起的想法,但是尝试了几天后发现自己坚持不下来。主要是两个原因,一个是没有一个早起...
    楠牧阅读 205评论 1 1
  • 前言 在上一节我们通过使用NumPy的数组分割成功的在我们的图像上画了一个绿色的方块,但是如果我们想画一个单一的线...
    楼上小宇阅读 3,286评论 1 2