概述
今天我們來實現一個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的頂部
主要的接口有以下幾個:
+ (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數組來實現不同顏色的色帶:
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數組的最后一個顏色值插到數組開始處。定時器的執行代碼如下:
/**
* 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的啟動、暫停、永遠停止這三個操作要分清,尤其是暫停與停止:
- 啟動:
- (void)startTimer
{
//start timer
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
[self.timer setFireDate:[NSDate date]];
}
- 暫停:
/**
* here we just pause timer, rather than stopping forever.
* NOTE: [timer invalidate] is not fit here.
*/
- (void)pauseTimer
{
[self.timer setFireDate:[NSDate distantFuture]];
}
- 停止(無法再啟動):
[self.timer invalidate]
(3)如何關聯進度值與色帶的寬度
這個問題看起來很簡單,但實際上隱藏着一個很好用的技術:mask。mask也稱為蒙版,當我們給一個layer設置了mask layer后,layer就只顯示出mask layer所覆蓋到的區域,其他區域不顯示。用偽代碼可以描述為:
CALayer *layer = new layer.mask = _maskLayer; layer.visualSection = _maskLayer.bounds;
因此,我們可以將在一開始時就上文的漸變圖層gradientLayer大小設置為與view同尺寸,然后通過mask layer設置可見區域。這樣,進度條進度值設置問題就轉化為mask layer的寬度問題了。
首先,我們添加一個mask layer到gradient layer上:
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];
然后相應進度值的改變如下:
- (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,給我更多支持與鼓勵!
源代碼下載:點我。https://github.com/weng1250/WGradientProgressDemo.git
原創文章,轉載請注明 編程小翁@博客園,郵件zilin_weng@163.com,歡迎各位與我在C/C++/Objective-C/機器視覺等領域展開交流!
