【iOS】自定义控件入门:可拖动的环形进度

有时候UIKit的标准控件并不能满足我们的需求,因此我们可以通过自定义控件得到满足我们需求的控件,例如这篇文章将教你如何自定义一个圆形的进度条,并且用户可以通过拖动进度条上的手柄来改变进度值。主要参考了这篇文章:HOW
TO BUILD A CUSTOM CONTROL IN IOS
。广告时间:我的一个免费APP:午睡闹钟 使用了这个控件,欢迎大家在AppStore搜索午睡闹钟进行下载使用。

我们的自定义控件继承自UIControl类,它是UIView的子类,是所有UIKit控件(UIButton, UISlider, UISwitch等等等)的父类。UIControl实例的作用是创建相应的逻辑来将action分发到相应的target,90%的情况下,它会根据自身的状态(例如Highlighted,
Selected和Disabled等)来绘制用户界面。

UIControl主要完成三个重要任务:

  • 绘制用户界面
  • 跟踪用户的交互操作
  • Target-Action模式

因此,对于这篇文章中要完成的圆形进度条,我们将要完成以下任务:

绘制一个用户可以通过拖动手柄滑块来进行交互的用户界面,用户的操作会被转化为target对应的actions,控件将滑块的frame origin转换为0-360之间的一个值,并用于target/action上。下面将分三步进行讲解,这三步对应上面提到的三个重要任务。

1.1绘制用户界面

如图,我们将通过Core Graphics来绘制灰色的进度条背景、红色的进度条、滑块手柄,关于Core Graphics的基础知识,本文不作详细介绍,如果大家有需要可以留言,我以后有空将给大家翻译官方的Core Graphics文档(毕竟有点多啊~)。

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    CGContextRef context = UIGraphicsGetCurrentContext();

    //1.绘制灰色的背景
    CGContextAddArc(context, self.frame.size.width/2, self.frame.size.height/2, radius, 0, M_PI*2, 0);
    [[UIColor grayColor] setStroke];
    CGContextSetLineWidth(context, _lineWidth);
    CGContextSetLineCap(context, kCGLineCapButt);
    CGContextDrawPath(context, kCGPathStroke);

    //2.绘制进度
    CGContextAddArc(context, self.frame.size.width/2, self.frame.size.height/2,radius,0, ToRad(_angle), 0);
    [[UIColor redColor] setStroke];
    CGContextSetLineWidth(context, _lineWidth);
    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextDrawPath(context, kCGPathStroke);

    //3.绘制拖动小块
    CGPoint handleCenter =  [self pointFromAngle: (self.angle)];
    CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 3,[UIColor blueColor].CGColor);
    [[UIColor redColor] setStroke];
    CGContextSetLineWidth(context, _lineWidth*2);
    CGContextAddEllipseInRect(context, CGRectMake(handleCenter.x, handleCenter.y, _lineWidth*2, _lineWidth*2));
    CGContextDrawPath(context, kCGPathStroke);
}

其中pointFromAngle方法是从给定的角度得到圆环上对应的经纬度,只是简单的使用了一下基本的三角函数关系:

-(CGPoint)pointFromAngle:(int)angleInt{
    //中心点
    CGPoint centerPoint = CGPointMake(self.frame.size.width/2 - _lineWidth, self.frame.size.height/2 - _lineWidth);

    //根据角度得到圆环上的坐标
    CGPoint result;
    result.y = round(centerPoint.y + radius * sin(ToRad(angleInt))) ;
    result.x = round(centerPoint.x + radius * cos(ToRad(angleInt)));

    return result;
}

1.2跟踪用户的交互操作

完成了1.1,现在我们已经绘制好了用户界面,但是它还不能响应我们的触摸事件。我们只要重写(override)UIControl的三个方法,就可以跟踪用户操作。

1.2.1开始跟踪触摸事件

当用户在UIControl的bound内进行触摸,beginTrackingWithTouch方法会被调用,此方法会返回BOOL类型,返回Yes表示要继续跟踪触摸事件。

-(BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
    [super beginTrackingWithTouch:touch withEvent:event];
    return YES;
}

1.2.2 持续跟踪触摸时间

上面的方法返回了Yes,表示要继续跟踪触摸事件,所以当用户在屏幕上拖动时,continueTrackingWithTouch会被调用,该方法返回的BOOL值表示是否继续跟踪touch事件。通过该方法我们将根据用户触摸的位置更新手柄的位置。

-(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
    [super continueTrackingWithTouch:touch withEvent:event];
    //获取触摸点
    CGPoint lastPoint = [touch locationInView:self];
    //使用触摸点来移动小块
    [self movehandle:lastPoint];
    //发送值改变事件
    [self sendActionsForControlEvents:UIControlEventValueChanged];
    return YES;
}

其中,我们调用了movehandle:方法来更新滑动手柄的位置:

-(void)movehandle:(CGPoint)lastPoint{

    //获得中心点
    CGPoint centerPoint = CGPointMake(self.frame.size.width/2,
                                      self.frame.size.height/2);
    //计算中心点到任意点的角度
    float currentAngle = AngleFromNorth(centerPoint,
                                        lastPoint,
                                        NO);
    int angleInt = floor(currentAngle);
    //保存新角度
    self.angle = angleInt;
    //重新绘制
    [self setNeedsDisplay];
}

AngleFromNorth(CGPoint p1,CGPoint p2,BOOL
flipped)方法是计算中心点到任意点的角度,

是从苹果是示例代码clockControl中拿来的函数,比较复杂(数学没学好...),就直接当作苹果提供的一个方法来调用就行。

static inline float AngleFromNorth(CGPoint p1, CGPoint p2, BOOL flipped) {
    CGPoint v = CGPointMake(p2.x-p1.x,p2.y-p1.y);
    float vmag = sqrt(SQR(v.x) + SQR(v.y)), result = 0;
    v.x /= vmag;
    v.y /= vmag;
    double radians = atan2(v.y,v.x);
    result = ToDeg(radians);
    return (result >=0  ? result : result + 360.0);
}

1.2.3结束跟踪

当结束跟踪时,这个方法会被调用,我们的例子中不需要override这个方法

现在,圆形滑块控件可以工作了,拖动滑动手柄看看。

1.3 Target-Action模式

如果希望自己定制的控件与UIControl行为保持一致,那么当控件的值发生变化时,需要进行通知处理:使用sendActionsForControlEvents方法,并制定特定的事件类型,值改变对应的事件一般是UIControlEventValueChanged。

苹果已经预定义了许多事件类型(Xcode中,在UIControlEventValueChanged上cmd + 鼠标单击)。如果你的控件是继承自UITextField,那么我们可能会对UIControlEventEdigitingDidBegin感兴趣,如果要做一个touch Up action,可以使用UIControlTouchUpInside。在本文前部分的continueTrackingWithTouch方法里面,我们已经调用了sendActionsForControlEvents方法。这样处理之后,当控件值发生变化时,每一个对象(观察者——注册该事件)都会收到响应的通知。

2.使用自定义控件

除了通过用户触摸事件来改变进度,有时我们还想通过代码进行修改,我们只要自己实现存储角度的属性angle的set方法:

-(void)changeAngle:(int)angle{
    _angle = angle;
     [self sendActionsForControlEvents:UIControlEventValueChanged];
    [self setNeedsDisplay];
}

好了,现在我们在viewcontroller中使用我们的控件:

    JXCircleSlider *slider = [[JXCircleSlider alloc] initWithFrame:CGRectMake(0, 0, 250, 250)];
    slider.center = self.view.center;

    [slider addTarget:self action:@selector(newValue:) forControlEvents:UIControlEventValueChanged];

    [slider changeAngle:120];
    [self.view addSubview:slider];

我们先进行初始化,然后设置为居中显示,注册了值改变响应事件,当进度值改变时,会调用一下方法

-(void)newValue:(JXCircleSlider*)slider{
    NSLog(@"newValue:%d",slider.angle);
}

项目源代码:https://github.com/dolacmeng/JXCircleSlider

最终效果如下:

时间: 2024-10-05 10:09:24

【iOS】自定义控件入门:可拖动的环形进度的相关文章

iOS 自定义控件 progressView(环形进度条)

转帖:http://blog.csdn.net/xiangzhang321/article/details/42688133 之前做项目的时候有用到环形进度条,先是在网上找了一下第三方控件,发现好用是好用,就是东西太多了,有点复杂,还不如自己写一个简单点适合自己用的. 先把自定义控件的效果图贴出来.     其实我写的这个控件很简单.索性就直接把源码贴出来吧. .h文件的内容就是一些声明 #import <UIKit/UIKit.h> @interface ProgressView : UIV

仿MIUI音量变化环形进度条实现

Android中使用环形进度条的业务场景其实蛮多的,比如下载文件的时候使用环形进度条,会给用户眼前一亮的感觉:再比如我大爱的MIUI系统,它的音量进度条就是使用环形进度条,尽显小米"为发烧而生"的宗旨.今天就为大家揭开那些高大上设计背后的故事,让我们也来实现如此酷炫的效果. 其实环形进度条只是一个稍稍复杂点的自定义控件,看过前段时间<Android自定义控件>系列的同学,理解起来会更加容易.还没看过的同学,出门右转,查看历史消息,再回头看今天的内容,会更加容易上手. 一.定

页面效果:圆形进度条 环形进度条

环形进度条(1.5秒之内倒计时) 效果做的比较粗糙,就是css的 clip属性,先切右边一半,再切左边一半.根据三角函数计算y高度 http://www.w3school.com.cn/cssref/pr_pos_clip.asp css的clip属性 <script src="http://cdn.bootcss.com/jquery/1.12.1/jquery.min.js"></script> <script type="text/java

HTML5 Canvas绘制环形进度条

最近比较迷恋canvas,加之做了一个个人网站,有用到环形进度条,记录下来. canvas中没有直接绘制圆的方法,但有一个绘制弧线的context.arc方法, 下面讲下用该方法如何绘制出图片效果. arc()方法介绍 context.arc(x,y,r,sAngle,eAngle,counterclockwise); 参数说明: x: 圆的中心的 x 坐标 y: 圆的中心的 y 坐标 r: 圆的半径 sAngle: 起始角,以弧度计.(弧的圆形的三点钟位置是 0 度) eAngle: 结束角,

iOS开发入门教程_iOS开发视频教程

iOS开发入门教程 (Object-C.网络编程.多线程.蓝牙.二维码.Cocos2D.OpenGL)适合人群:初级课时数量:34课时用到技术:IOS,Object-C,OpenGL,XCode,Cocos 2D涉及项目:Cocos+2D.Game Kit蓝牙数据处理等咨询QQ:1840215592 iOS开发入门教程详细查看:http://www.ibeifeng.com/goods-471.html1.1.课程目标iOS开发入门教程内容的目标是初学者入门,让入门者提高,让所有人符合企业招聘的

ios中创建可以拖动的view原理和实现详解

有时候我们会需要在界面上拖动view;uiview是继承于uiresponder的,所以可以响应触摸相关的事件. 重点是以下一组方法: - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesEnded:(NSSet *)touches withEvent:(UIE

利用jQuery和CSS实现环形进度条

html代码 1 <div class="circle" style="left:0"> 2 <div class="pie_left"><div class="left"></div></div> 3 <div class="pie_right"><div class="right"></div&

【IOS】IOS快速入门之OC语法

Objective-C 是 C 语言的超集 您还可以访问标准 C 库例程,例如在 stdlib.h 和 stdio.h 中声明的那些例程. Objective-C 还是一种非常动态的程序设计语言,而且这种动态是其最大优势.这种动态体现在它允许在运行应用程序时(即运行时)才去确定其行为,而不是在生成期间就已固定下来.因此,Objective-C 的动态机制让程序免受约束(编译和链接程序时施加的约束):进而在用户控制下,将大多数符号解析责任转移到运行时. 当您想要在源代码中包括头文件时,请在头文件或

ios中创建可以拖动的view原理和实现详解(含代码)

有时候我们会需要在界面上拖动view;uiview是继承于uiresponder的,所以可以响应触摸相关的事件. 重点是以下一组方法: - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesEnded:(NSSet *)touches withEvent:(UIE