本文记录的是如何绘制一个背景颜色渐变的滑动条,最终的效果如下图:

背景颜色渐变的滑动条

绘制渐变背景这里用到了CAGradientLayer和CALayer的mask,首先使用两个CAGradientLayer绘制如下的背景:

渐变背景

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
_bgLayer = [CALayer layer];
_bgLayer.bounds = CGRectMake(0, 0, CGRectGetWidth(self.bounds) - LS_HANDLE_RADIUS * 2, CGRectGetHeight(self.bounds) - LS_HANDLE_RADIUS * 2);
_bgLayer.position = self.centerPoint;
[self.layer addSublayer:_bgLayer];

CGColorRef topLeftColor = [UIColor blackColor].CGColor;
CGColorRef bottomColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1].CGColor;
CGColorRef topRightColor = [UIColor whiteColor].CGColor;

CAGradientLayer *leftGradientLayer = [CAGradientLayer layer];
leftGradientLayer.frame = CGRectMake(0, 0, CGRectGetWidth(_bgLayer.bounds)/2, CGRectGetHeight(_bgLayer.bounds));
leftGradientLayer.colors = @[(__bridge id)topLeftColor, (__bridge id)bottomColor];
leftGradientLayer.startPoint = CGPointMake(0.5, 0);
leftGradientLayer.endPoint = CGPointMake(0.5, 1);
[_bgLayer addSublayer:leftGradientLayer];

CAGradientLayer *rightGradientLayer = [CAGradientLayer layer];
rightGradientLayer.frame = CGRectMake(CGRectGetWidth(_bgLayer.bounds)/2, 0, CGRectGetWidth(_bgLayer.bounds)/2, CGRectGetHeight(_bgLayer.bounds));
rightGradientLayer.colors = @[(__bridge id)bottomColor, (__bridge id)topRightColor];
rightGradientLayer.startPoint = CGPointMake(0.5, 1);
rightGradientLayer.endPoint = CGPointMake(0.5, 0);
[_bgLayer addSublayer:rightGradientLayer];

然后绘制maskLayer,可使用UIShapeLayer+UIBezierPath组合绘制规则图形的maskLayer,如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
CGPoint circleCenterPoint = CGPointMake(CGRectGetWidth(_bgLayer.bounds)/2, CGRectGetHeight(_bgLayer.bounds)/2);
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.position = circleCenterPoint;
maskLayer.bounds = _bgLayer.bounds;
maskLayer.fillColor = [UIColor clearColor].CGColor;
maskLayer.strokeColor = [UIColor redColor].CGColor;
maskLayer.lineCap = kCALineCapRound;
maskLayer.lineWidth = self.lineWidth;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithArcCenter:circleCenterPoint radius:self.radius startAngle:CompassToCartesian(self.startradianFromNorth) endAngle:CompassToCartesian(self.endradianFromNorth) clockwise:true];
maskLayer.path = maskPath.CGPath;

_bgLayer.mask = maskLayer;

mask 的作用是将maskLayer层不透明像素覆盖的底层显示出来,而不被不透明像素覆盖的底层则隐藏,添加mask后效果如下所示:

渐变弧线

绘制滑动条肯定有个滑动块handler,这里使用CALayer实现,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
CGPoint handleCenter = [self pointOnCircleAtRadian:self.radianFromNorth];

_outerCircleLayer = [CALayer layer];
_outerCircleLayer.bounds = CGRectMake(0, 0, LS_HANDLE_RADIUS * 2, LS_HANDLE_RADIUS * 2);
_outerCircleLayer.cornerRadius = LS_HANDLE_RADIUS;
_outerCircleLayer.position = handleCenter;
_outerCircleLayer.shadowColor = [UIColor grayColor].CGColor;
_outerCircleLayer.shadowOffset = CGSizeZero;
_outerCircleLayer.shadowOpacity = 0.7;
_outerCircleLayer.shadowRadius = 3;
_outerCircleLayer.borderWidth = 1;
_outerCircleLayer.borderColor = [UIColor grayColor].CGColor;
_outerCircleLayer.backgroundColor = [UIColor whiteColor].CGColor;
[self.layer addSublayer:_outerCircleLayer];

CAShapeLayer *innerCircleLayer = [CAShapeLayer layer];
innerCircleLayer.bounds = CGRectMake(0, 0, LS_HANDLE_INNER_RADIUS * 2, LS_HANDLE_INNER_RADIUS * 2);
innerCircleLayer.cornerRadius = LS_HANDLE_INNER_RADIUS;
innerCircleLayer.position = CGPointMake(LS_HANDLE_RADIUS, LS_HANDLE_RADIUS);
innerCircleLayer.backgroundColor = self.handleFillColor.CGColor;
innerCircleLayer.borderWidth = 1;
innerCircleLayer.borderColor = [UIColor grayColor].CGColor;
[_outerCircleLayer addSublayer:innerCircleLayer];

最后控制滑动块在制定的轨道上活动,首先控制手指触摸事件的响应范围,重载UIView的- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event, 如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if ([self pointInsideHandle:point withEvent:event]) {
return YES; // Point is indeed within handle bounds
} else {
return [self pointInsideCircle:point withEvent:event]; // Return YES if point is inside slider's circle
}
}

//检测触摸点是否在slider所在圆圈范围内
- (BOOL)pointInsideCircle:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint p1 = [self centerPoint];
CGPoint p2 = point;
CGFloat xDist = (p2.x - p1.x);
CGFloat yDist = (p2.y - p1.y);
double distance = sqrt((xDist * xDist) + (yDist * yDist));
return distance < self.radius + self.lineWidth * 0.5;
}

//检测触摸点是否在滑动块中
- (BOOL)pointInsideHandle:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(@"%@", NSStringFromSelector(_cmd));
CGPoint handleCenter = [self pointOnCircleAtRadian:self.radianFromNorth];
CGFloat handleRadius = MAX(LS_HANDLE_RADIUS * 2, 44.0) * 0.5;
// Adhere to apple's design guidelines - avoid making touch targets smaller than 44 points

// Treat handle as a box around it's center
CGRect handleRect = CGRectMake(handleCenter.x - handleRadius, handleCenter.y - handleRadius, handleRadius * 2, handleRadius * 2);
return CGRectContainsPoint(handleRect, point);
}

其次,限定滑动块在轨道的范围内滑动,重载UIControl的事件函数 - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event

如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
[super continueTrackingWithTouch:touch withEvent:event];

CGPoint lastPoint = [touch locationInView:self];
self.radianFromNorth = [self radianFromPoint:lastPoint toReferencePoint:self.centerPoint];

BOOL continueTracking = YES;
if (self.radianFromNorth > self.endradianFromNorth) {
self.radianFromNorth = self.endradianFromNorth;
[self sendActionsForControlEvents:UIControlEventTouchDragExit];
continueTracking = NO;
} else if (self.radianFromNorth < self.startradianFromNorth) {
self.radianFromNorth = self.startradianFromNorth;
[self sendActionsForControlEvents:UIControlEventTouchDragExit];
continueTracking = NO;
}

[self moveHandle];

self.currentValue = RADIAN_TO_DEGREE(self.radianFromNorth);
[self sendActionsForControlEvents:UIControlEventValueChanged];

return continueTracking;
}

所有的代码在ios绘制渐变背景滑动条可以下载。

本文参考:


目前已转行教育行业,欢迎加微信交流:CaryaLiu