由于公司项目需要,现在需要制作一个游标滑尺来让用户选择金额与时间。查找了大量的资料和相关代码,再次完成后记录下来。
//添加底线
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0);//设置线的颜色,默认是黑色
CGPoint points[2];
points[0] = CGPointMake(_marginLeft, rect.size.height-1);
points[1] = CGPointMake(_marginLeft+_widthOfItem*_numberOfItem, rect.size.height-1);
CGContextAddLines(context, points, 2);
_lineViewRect = CGRectMake(_marginLeft-2, rect.origin.y, _widthOfItem*_numberOfItem+4, rect.size.height);
//画刻度
for (int i = 0; i <= _numberOfItem; i ++){
//移动画笔的位置
CGContextMoveToPoint(context, _marginLeft+_widthOfItem*i, rect.size.height);
//当是组的数值的时候需要画长线同时画数字
if ((i*_itemValue+_minValue)%_groupValue == 0 || i == 0){
//显示的数字内容
NSString *num = [NSString stringWithFormat:@"%.ld%@",i *_itemValue +_minValue,_unit];
//如果数字上万
if ([num floatValue]>1000000){
num = [NSString stringWithFormat:@"%.f万%@",[num floatValue]/10000.f,_unit];
}
//设置字体
NSDictionary *attribute = @{NSFontAttributeName:TextRulerFont,NSForegroundColorAttributeName:XC_APP_BGCOLOR};
CGFloat width = [num boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:0 attributes:attribute context:nil].size.width;
//画数字
[num drawInRect:CGRectMake(_marginLeft+_widthOfItem*i-width/2, 0, width, 14) withAttributes:attribute];
//画标尺
CGContextAddLineToPoint(context, _marginLeft+_widthOfItem*i,rect.size.height-longLineY);
}else{
//不是组的数值的时候,画短标尺
CGContextAddLineToPoint(context, _marginLeft+_widthOfItem*i,rect.size.height - shortLineY);
}
CGContextStrokePath(context);//开始绘制
}
drawRect方法的调用是在ViewController的loadView方法和ViewDidLoad方法调用之后,所以在ViewController中依然可以给drawRect方法中需要用到的值赋值。
但是仍然要注意以下几点:
1、如果在UIView初始化时没有设置rect大小,将会直接导致drawRect不被自动调用。
2、该方法在调用sizeThatFits后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。
3、通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
4、直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect不能为0.
以上1,2推荐;而3,4不提倡
既然已经画好了刻度尺,那么接下来的操作就是绘制游标以及对游标的控制了。
游标同样我们也选择绘制出来(暂时没有考虑使用图片,有需要的同学可以自己尝试做一下),废话不多说上代码:
//画游标
CGContextSetRGBFillColor(context, 255.0f, 0.0f, 0.0f, 1.0);
CGContextSetRGBStrokeColor(context, 255.0f, 0.0f, 0.0f, 1.0);
//画游标的实心圆
CGContextAddArc(context, _currentXPosition, 15, 5, 0, 2*M_PI, 0);
CGContextDrawPath(context, kCGPathFill);
CGPoint cursorLinePoint[2];
cursorLinePoint[0] = CGPointMake(_currentXPosition, 10);
cursorLinePoint[1] = CGPointMake(_currentXPosition, rect.size.height - 1);
CGContextAddLines(context, cursorLinePoint, 2);
CGContextStrokePath(context);//开始绘制
既然已经有了游标,那么接下来的问题是如何控制游标而不让刻度尺发生移动,在绘制的代码中,聪明的同学应该能够看出,我在横向坐标中并没有写成固定值,意思就是说游标的控制依然是要通过不断的重新绘制实现的,所以我写了一个全局的变量来规定游标的位置,在发生拖动事件的时候改变X坐标,并且让其重新绘制游标,就达到了我们移动游标的效果。
综上所述,我在beginTrackingWithTouch方法、continueTrackingWithTouch方法以及endTrackingWithTouch方法中进行了游标的位置更改的操作,将点击事件的x坐标赋值给了游标的位置,同时限制了游标的位置,防止超出刻度尺。而考虑到我们的需求,在停止滑动的时候还需要对游标的位置进行判定,要求其落在刻度线上。在我们修改完坐标后,调用
[self setNeedsDisplay];
重新绘制游标即可。
以上关于滑动时的坐标就已经解决清楚了,但是,我们还有一个问题没有解决好,当我们完成上述处理后,会发现我们的滑动事件处理的并不完备,虽然我们处理了滑动并且也能让游标在停止滑动的时候停在刻度尺上,但是我们还有一个需求:动态获取当前游标选择的值。关于这个需求我们的处理是在ViewController中给当前控件添加UIControlEventValueChanged的事件,然后在beginTrackingWithTouch方法、continueTrackingWithTouch方法以及endTrackingWithTouch方法中添加对事件的触发
[self sendActionsForControlEvents:UIControlEventValueChanged];
这样,我们就可以在ViewController的UIControlEventValueChanged对应的事件中获得到当前控件里游标的位置了。
至此,我们这个自定义控件的需求就全部完成。效果图如下:
下面给出源码地址:
Demo地址(CSDN)
本人学识经验尚浅,如果有什么改进意见或建议,请在下方留言,不胜感激。