概述
今天我们来实现一个iOS平台上的进度条(progress bar or progress view)。这种进度条比APPLE自带的更加漂亮,更加有“B格”。它拥有渐变的颜色,而且这种颜色是动态移动的,这里称之为WGradientProgress。
先来看看我们的目标长什么样子:
WGradientProgress的使用方法很简单,主要有展示接口以及隐藏接口,目前显示的位置有两种选择:
WProgressPosDown //progress is on the down border of parent view,显示在parent view的底部(主流做法,默认)
- WProgressPosUp //progress is on the up border of parent view,也就是显示在parent view的顶部
主要的接口有以下几个:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
+ (WGradientProgress *)sharedInstance;
/**
* the main interface to show WGradientProgress obj, position is WProgressPosDown by default.
*
* @param parentView which view to be attach
*/
- (
void
)showOnParent:(UIView *)parentView;
/**
* the main interface to show WGradientProgress obj
*
* @param parentView which view to be attach
* @param pos up or down
*/
- (
void
)showOnParent:(UIView *)parentView position:(WProgressPos)pos;
/**
* the main interface to hide WGradientProgress obj
*/
- (
void
)hide;
|
分析
这里我们看一下,实现出这样的效果需要解决哪些技术难点:
- 如何实现一个静态的具有渐变颜色的色带
- 如何实现色带颜色循环移动
- 如何关联进度值与色带的宽度
(1)如何实现一个静态的具有渐变颜色的色带
这里需要使用CALayer的子类CAGradientLayer。CAGradientLayer用于实现颜色渐变,关于CAGradietnLayer的介绍请看这里。我们使用到的属性有startPoint、endPoint、colors。
我们可以这样子做出一个静态的渐变色带,你也可以修改colors数组来实现不同颜色的色带:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
if
(
self
.gradLayer ==
nil
) {
self
.gradLayer = [CAGradientLayer layer];
self
.gradLayer.frame =
self
.bounds;
//尺寸要与view的layer一致
}
self
.gradLayer.startPoint = CGPointMake(0, 0.5);
self
.gradLayer.endPoint = CGPointMake(1, 0.5);
//create colors, important section
NSMutableArray
*colors = [
NSMutableArray
array];
for
(
NSInteger
deg = 0; deg <= 360; deg += 5) {
UIColor *color;
color = [UIColor colorWithHue:1.0 * deg / 360.0
saturation:1.0
brightness:1.0
alpha:1.0];
[colors addObject:(
id
)[color CGColor]];
}
[
self
.gradLayer setColors:[
NSArray
arrayWithArray:colors]];
|
(2)如何实现色带颜色循环移动
色带颜色循环向前移动,本质上是渐变图层gradientLayer的colors数组循环变化。如果理解了这点,那就很容易往下做了。我的做法是使用定时器NSTimer,让定时器的执行方法去循环地改变color数组。另外,既然要做到循环,那么应该循环地取colors数组的最后一个颜色值插到数组开始处。定时器的执行代码如下:
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
|
/**
* here I use timer to circularly move colors
*/
- (
void
)setupTimer
{
CGFloat interval = 0.03;
if
(
self
.timer ==
nil
) {
self
.timer = [
NSTimer
timerWithTimeInterval:interval target:
self
selector:
@selector
(timerFunc)
userInfo:
nil
repeats:
YES
];
}
[[
NSRunLoop
currentRunLoop] addTimer:
self
.timer forMode:
NSDefaultRunLoopMode
];
}
/**
* rearrange color array
*/
- (
void
)timerFunc
{
CAGradientLayer *gradLayer =
self
.gradLayer;
NSMutableArray
*copyArray = [
NSMutableArray
arrayWithArray:[gradLayer colors]];
UIColor *lastColor = [copyArray lastObject];
[copyArray removeLastObject];
if
(lastColor) {
[copyArray insertObject:lastColor atIndex:0];
}
[
self
.gradLayer setColors:copyArray];
}
|
*强势插入:
NSTimer的启动、暂停、永远停止这三个操作要分清,尤其是暂停与停止:
- 启动:
1
2
3
4
5
6
|
- (
void
)startTimer
{
//start timer
[[
NSRunLoop
currentRunLoop] addTimer:
self
.timer forMode:
NSDefaultRunLoopMode
];
[
self
.timer setFireDate:[
NSDate
date]];
}
|
- 暂停:
1
2
3
4
5
6
7
8
|
/**
* here we just pause timer, rather than stopping forever.
* NOTE: [timer invalidate] is not fit here.
*/
- (
void
)pauseTimer
{
[
self
.timer setFireDate:[
NSDate
distantFuture]];
}
|
- 停止(无法再启动):
1
|
[
self
.timer invalidate]
|
(3)如何关联进度值与色带的宽度
这个问题看起来很简单,但实际上隐藏着一个很好用的技术:mask。mask也称为蒙版,当我们给一个layer设置了mask layer后,layer就只显示出mask layer所覆盖到的区域,其他区域不显示。用伪代码可以描述为:
1
2
3
|
CALayer *layer =
new
layer.mask = _maskLayer;
layer.visualSection = _maskLayer.bounds;
|
因此,我们可以将在一开始时就上文的渐变图层gradientLayer大小设置为与view同尺寸,然后通过mask layer设置可见区域。这样,进度条进度值设置问题就转化为mask layer的宽度问题了。
首先,我们添加一个mask layer到gradient layer上:
1
2
3
4
5
6
7
|
self
.mask = [CALayer layer];
[
self
.mask setFrame:CGRectMake(
self
.gradLayer.frame.origin.x,
self
.gradLayer.frame.origin.y,
self
.progress *
self
.width,
self
.height)];
self
.mask.borderColor = [[UIColor blueColor] CGColor];
self
.mask.borderWidth = 2;
[
self
.gradLayer setMask:
self
.mask];
[
self
.layer addSublayer:
self
.gradLayer];
|
然后相应进度值的改变如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
- (
void
)setProgress:(CGFloat)progress
{
if
(progress < 0) {
progress = 0;
}
if
(progress > 1) {
progress = 1;
}
_progress = progress;
CGFloat maskWidth = progress *
self
.width;
self
.mask.frame = CGRectMake(0, 0, maskWidth,
self
.height);
}
|
以上就是WGradientProgress的主要技术要点,更具体的细节以及使用方法请下载我github上的代码查看,下载时别忘记随手点个Star,给我更多支持与鼓励!
本文转自编程小翁博客园博客,原文链接:http://www.cnblogs.com/wengzilin/p/4665038.html,如需转载请自行联系原作者