在开发过程中,有时候标准的 UIKit 控件并不能满足我们的需求,这时候就需要我们自定义。
自定义控件可以参考系统中已有控件来实现,比如 UIButton, ,UISegmentedControl, UISlider, UISwitch 等,它们都继承自 UIControl。
先来看一张最终的效果:
该圆环控件可以做的事情:定制一个用户界面,通过该界面用户,可以在指定区域内拖动手柄,将当前的角度转化为需要显示的数据。交互操作会被转为控件的 Target 对应的 Action。
完整的工程示例可以在这里下载:SFCircularSlider
绘制用户界面
- 绘制底部圆环
CGContextSaveGState(context);
CGContextAddArc(context, kSelfWidth / 2, kSelfHeight / 2, _radius, -M_PI, M_PI * 2, 0);
[_circularColor setStroke];
CGContextSetLineWidth(context, _borderWidth);
CGContextSetLineCap(context, kCGLineCapButt);
CGContextDrawPath(context, kCGPathStroke);
CGContextRestoreGState(context);
这里绘制圆的方向,零度位于圆的最右侧,向上为负数,向下是正数。
- 绘制填充区域
CGContextSaveGState(context);
CGContextAddArc(context, kSelfWidth / 2, kSelfHeight / 2, _radius,
DegreesToRadians(kCircleDegreeMin),
DegreesToRadians(kCircleDegree360 - _angle), 0);
[_minimumTrackTintColor setStroke];
CGContextSetLineWidth(context, _borderWidth);
CGContextSetLineCap(context, kCGLineCapRound); //设置线起点终点形状
CGContextDrawPath(context, kCGPathStroke);
CGContextRestoreGState(context);
_angle 代表当前角度,需要转换到绘制角度上。绘制起点为最小值到当前手势所在位置。
- 绘制未填充区域圆弧
CGContextAddArc(context, kSelfWidth / 2, kSelfWidth / 2,
_radius, DegreesToRadians(kCircleDegree360 - _angle),
DegreesToRadians(kCircleDegreeMax), 0);
[_maximumTrackTintColor setStroke];
CGContextSetLineWidth(context, _borderWidth);
CGContextSetLineCap(context, kCGLineCapRound); //设置线起点终点形状
CGContextDrawPath(context, kCGPathStroke);
未填充颜色区域原理相同,起点为手势所在位置,终点为最大值处。
- 绘制手柄(thumb)
CGPoint handleCenter = [self pointFromAngle:_angle radius:_radius];
[_thumbTintColor set];
CGSize shadowOffset = CGSizeMake(0, 0);
CGFloat colorValues[] = {0, 0, 1/2, .6};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB ();
CGColorRef color = CGColorCreate (colorSpace, colorValues);
CGContextSetShadowWithColor(context, shadowOffset, 3, color);
CGContextFillEllipseInRect(context, CGRectMake(handleCenter.x - _thumbRadius,
handleCenter.y - _thumbRadius,
_thumbRadius * 2, _thumbRadius * 2));
CGContextSetLineCap(context, kCGLineCapButt);
CGContextDrawPath(context, kCGPathStroke);
CGColorRelease(color);
CGColorSpaceRelease(colorSpace);
手柄跟踪当前手势所在的位置。为了增强立体感,添加投影的效果。CGContextSetShadowWithColor 中 offset 参数代表偏移的距离和方向,左负右正,上负下正,{0,0} 代表在一周上投影。
跟踪用户操作
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {}
通过重载 beginTracking
方法控制起始的触发区域。
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event {}
重载 continueTracking
来连续跟踪用户的手势,并在超出响应范围之外返回 NO
。
使用示例
SFCircularSlider *circularSlider = [[SFCircularSlider alloc] init];
circularSlider.value = 5;
circularSlider.minimumValue = 0;
circularSlider.maximumValue = 10;
circularSlider.minimumTrackTintColor = [UIColor colorWithRed:255/255.0 green:177/255.0 blue:0/255.0 alpha:1];
[circularSlider addTarget:self action:@selector(toucheUpInsideAction:) forControlEvents:UIControlEventTouchUpInside];
[circularSlider addTarget:self action:@selector(valueChangedAction:) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:circularSlider];
支持设定最小/最大值,亦可接受 ValueChanged 和 TouchUpInside 的事件来响应当前拖动的值和拖动结束的值。