PaintCode 教程2:自定義進度條

PaintCode 是一個可以讓你像Photoshop同樣去設計你的用戶界面的軟件 – 可是它並非僅僅保存一張圖片看成資源讓你來使用,它可以爲你生成 CoreGraphic 源碼直接使用到View的繪製中.
在這個系列的第一部分教程中, 咱們已經告訴你們如何去用PaintCode去創造能夠隨時改變大小,隨時改變顏色的自定義按鈕.
此次的教程將會教你們如何使用PaintCode去創造設計一個屬於本身的進度條,而且將它集成到代碼裏.
對於看了第一部分教程讀者的來講這篇教程會相對更容易一些。固然,若是沒有看過,並沒關係,由於這邊教程仍然可以詳細的帶你們一步一步熟悉PaintCode。第一步必定是安裝PaintCode,而後下載這個我已經爲你們準備好了的初始項目.
話很少說,如今就打開PaintCode開始學習如何建立新的UI控件吧! spring

踏出第一步

在PaintCode中建立一個新的文件, 點擊 FileRename…,將它命名爲 DynamicProgressIndicator, 而後肯定.
在這個教程中,咱們作得控件並非retina顯示的。 任什麼時候候若是你須要生成一個retina顯示的控件, 在PaintCode窗口的右下角,先點擊Retina 按鈕, 而後點擊 Canvas 按鈕就能夠了。
若是你打開實時的代碼輸出panel, 這些按鈕將會在窗口最下面這個panel的頂端, 以下圖所示:
PaintCode canvas
接下來,爲了讓canvas更加適合咱們的進度條,咱們將canvas稍微的調小一點, 而後把它的背景色換成一個暗一點的顏色,這樣可讓看你空間看起來更溫馨.
具體步驟是: 點擊 Canvas 按鈕, 把 canvas 從 320 pixels調整到 70 pixels.而後把Underlay color 調整成以下圖同樣的灰色. 若是你想要設置成特定的顏色值, 使用 R=45, G=45, B=45, A=255.
Canvas color change
在canvas 一切就緒以後, 咱們能夠開始着手爲進度條建立基本的 shapes, gradients, 和 colors.
在工具欄選擇Round Rect 工具,而後畫一個圓角的長方形在canvas上作爲咱們控件的外邊框. 完成以後選中畫好的外邊框, 設置這個 shape’s 的屬性爲以下: canvas

  • X : 2
  • Y : 1
  • Width : 316
  • Height : 34
  • Radius : 4

你的 shape 屬性欄應該和下圖同樣:
Border rounded rectangle
保持外邊框繼續選中着, 在左邊欄裏找到 Stroke 設置, 而後把它設置爲 No Stroke. 一樣的, 找到並點擊 Fill 設置, 選擇 Add New Gradient…, 而後點擊漸變編輯框左下角的那個顏色選取點. 點擊右下角的顏色選取按鈕,把 RGB 值設置爲 to 82 82 82.而後在點擊漸變編輯框右下角那個顏色選取點,而且把他的 RGB 值設置爲 106 106 106. 最後將這個 gradient 命名爲 Outer Rect Gradient.
Renaming the gradienet xcode

注意: 若是之後須要修改PaintCode裏的 gradients, colors, 或者 shadows, 你們能夠再左邊欄下面的 panel 裏點擊對應的名字的 tab 去修改已命名的屬性. 雙擊須要修改的屬性的名字,而後在彈出的dialog裏作修改就行了. app

如今咱們先關掉dialog, 在外邊框的屬性欄裏找到 Outer Shadow section. 而後添加一個RGB 值是 51 51 51 的 Outer Shadow. 而後把它命名爲 DarkShadow, 以下圖所示:
PaintCode outer shadow
接着, 咱們繼續加一個淺色的RGB 值爲 171 171 171的 Inner Shadow. 命名 LightShadow,以下圖所示:
PaintCode inner shadow
最後咱們把這個Rounded Rectangle 的名字改成 Border.每當完成編輯一個元素馬上給他一個描述性的名字永遠都是一個好的習慣,由於這可讓你更好的在你本身的應用裏找到他們.
Renaming the Rectangle
到此爲止,進度條的基本外邊已經完成, 咱們接下來要開始作的是進度槽. ide

製做進度槽和背景

再找到 Round Rect 工具,而後另外拖一個矩形在你的canvas上。 把它屬性設置爲以下: 工具

  • X : 12
  • Y : 10
  • Width : 292
  • Height : 14
  • Radius : 7

Track rounded rectangle
而後給這個shape的Fill 設置一個新的gradient,左邊的顏色選取點的RGB設置爲 48 48 48,右邊的設置爲 63 63 63, 以下圖所示:
PaintCode rectangle gradient
設置 Outer Shadow 爲 LightShadowInner Shadow 爲 DarkShadow, 而後把這個shape命名爲ProgressTrack.
教程到這裏, 你的空間應該看起來像下圖同樣了:
Indicator progress
如今咱們來加最後一個可見的元素.
選擇Round Rect工具,再拖一個矩形而且把它的屬性設置爲以下: oop

  • X : 14
  • Y : 12
  • Width : 288
  • Height : 10
  • Radius : 5

最後的這個shape將會在 ProgressTrack 左邊緣靠右2像素的位置而且中心垂直對稱. 學習

注意:你會發現若是不是去直接修改數值的話,本身手動去修改這個shape的大小和位置會很難,由於進度槽和它的背景太類似了.
固然, 除了直接修改數值,你能夠經過下面2種方法達到一樣的效果: 1) 選中進度槽背景,複製粘貼,而後把它稍微的修改小一點, 或者 2) 若是你有一個trackpad,用手勢把當前的shape放大,這樣任然可使製做更加容易。 測試

爲了讓進度條看起來更加的顯眼一點,咱們把進度條改爲綠色的— RGB 0 226 0。 你們也能夠根據本身愛好去修改顏色。
最後, 把這個shape的 Shadow 和 Inner Shadow 都改爲 No Shadow, 取消 Stroke 而後把它更名爲 ProgressTrackActive. 如今你的控件應該和下面的差很少了:
Green indicator track
看起來很棒吧 — 可是若是想要它動起來,咱們須要加一些組件來讓它動態的響應事件. 動畫

加入 Control Frame

有一點須要你們注意的 – 若是你們想在PaintCode裏隨時的去調整一個元素的大小,那就應該把它放在一個frame裏面,,而且去設置他的屬性,這樣PaintCode才知道如何去調整它的大小當它的parent frame改變的時候。
下面是咱們須要作的:

  1. Group: 把全部你但願被同一個parent frame調整大小的元素放在一個group裏。
  2. Frame: 建立一個frame,而後把全部的元素都放在裏面。 選中 Apply only to entirely enclosed shapes來限定這個frame只能影響到在他裏面的元素.
  3. Springs  Struts: 爲每一個讓元素在frame改變的時候知道它們改如何改變本身的大寫,你須要設置它們的springs 和 struts。

在上方工具欄選擇 Frame tool。在綠色的進度條周圍拖出一個fame,以下圖所示:
PaintCode dynamic frame
而後把這個fame命名ActiveProgressFrame 而後把它和ProgressTrackActive方框group在一塊兒 (同時選中2個而後點擊工具欄裏的Group)。而後把這個把這個group命名爲 ProgressActiveGroup。
再次選中ActiveProgressFrame 而後勾選Apply only to entirely enclosed shapes。 這樣能使這個frame只能影響ProgressTrackActive bar,由於ProgressTrackActive 是這個group裏惟一放在在ActiveProgressFrame範圍內的元素.
爲了讓進度條水平居中,進度條須要設置爲固定高度以及可變的寬度。
首先,咱們須要選中 ProgressTrackActive 而後點擊右邊和下邊的 springs 將他們設置爲不可變的。而後點擊中間的2個橫條將他們變成spring, 以下圖所示:
Progress Track Resizing
爲了測試剛纔作得改動,你能夠嘗試向右拖動ActiveProgressFrame 的拖動點 。 綠色的橫條應該會隨着你的拖動拉伸。 若是你們嘗試將frame上下拖動,會發現綠色的方框只會水平的拉伸,豎直放假僅僅會跟着frame的邊框移動。
爲了能讓整個進度條隨着frame的改動而調整,咱們須要給整個進度條加一個frame。 首先,把全部元素都放在一個group裏面 (包含已經建立了的group),而後點擊工具欄裏的 Group按鈕。最後,命名把這個group爲Progress Bar。 新的group應該和下圖同樣:
Progress bar group
接着,選擇 Frame 工具,建立一個frame把整個進度條都包含進去。 而後把這個frame加入Progress Bargroup,最後把它命名 ProgressIndicatorFrame.。任然勾選 Apply only to entirely closed shapes,若是你忘記了爲何要這樣作,能夠在看看教程的前幾部。
接下來選中 ProgressActiveGroup。 爲了讓全部的元素都保持本身的位置,向前幾步同樣,咱們須要設置這個frame只能水平改變大小。
點擊 springs/struts 裏的水平橫條將它變成一個spring。爲了讓進度槽的位置相對於上左右邊框都不會改變, 因此確保只有下面的豎條是一個spring。 下面的圖便是frame的具體設置:
Progress active group sizing
將ProgressTrack 和 Border 修改成一樣的設置。
如今讓咱們開始試試剛纔的修改吧!選中ProgressIndicatorFrame而後隨便的拖拽改變他的大小 — 進度條應該只會水平的改變大小,豎直方向保持不變。
選中ProgressTrackActive shape 而後點擊 variable width 按鈕, 這個按鈕就在 Width 屬性的右邊,2箇中括號包住一個小圓點的圖標。
Progress track active rect width
記住!!!保存你全部的改動。 接下來咱們要開始把進度條和代碼聯繫起來了。

在項目里加入進度條

在 Xcode中,打開剛纔下載的初始項目,或者繼續你在第一部分教程建立的項目。打開項目以後,展開group Classes > Views。
如今咱們須要作的就是像教程1同樣,建立一個進度條的subclass。此次咱們繼承的是 UIProgressView ,這樣的話,你們用 PaintCode 建立的進度條就擁有和iOS進度條同樣的表現了。
右鍵點擊 Views group而後選擇 NewFile…. 接着選擇iOSCocoa TouchObjective-C class 模板。 把類命名爲 ProgressView,而後設置它繼承 UIProgressView。
打開 ProgressView.m文件,刪除方法 initWithFrame: 而後去掉 drawRect:方法的註釋。 下面的這個文件這時候的樣子:

[objc]  view plain copy
  1. #import "ProgressView.h"  
  2. @implementation ProgressView  
  3. - (void)drawRect:(CGRect)rect  
  4. {  
  5.     // Drawing code  
  6. }  
  7. @end   

如今咱們能夠開始粘貼PaintCode給你的代碼了。
回到 PaintCode 而後肯定代碼窗口在下面打開着; 若是沒有,在菜單裏選擇 View > Code 。 設置平臺爲 iOS > Objective-C,系統版本是 iOS 5+,orgin設置爲Default Origin,內存管理設置爲 ARC, 以下圖所示:
PaintCode code settings
如今把全部代碼複製粘貼到 drawRect:方法裏。 這段代碼可能很長,可是它是PaintCode早就爲你準備好的了。

[objc]  view plain copy
  1.     //// General Declarations  
  2. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();  
  3. CGContextRef context = UIGraphicsGetCurrentContext();  
  4. //// Color Declarations  
  5. UIColor* fillColor = [UIColor colorWithRed0.416 green0.416 blue0.416 alpha: 1];  
  6. UIColor* strokeColor = [UIColor colorWithRed0.322 green0.322 blue0.322 alpha: 1];  
  7. UIColor* shadowColor2 = [UIColor colorWithRed0.2 green0.2 blue0.2 alpha: 1];  
  8. UIColor* shadowColor3 = [UIColor colorWithRed0.671 green0.671 blue0.671 alpha: 1];  
  9. UIColor* fillColor2 = [UIColor colorWithRed0.247 green0.247 blue0.247 alpha: 1];  
  10. UIColor* strokeColor2 = [UIColor colorWithRed0.188 green0.188 blue0.188 alpha: 1];  
  11. UIColor* color = [UIColor colorWithRed0 green0.886 blue0 alpha: 1];  
  12. //// Gradient Declarations  
  13. NSArray* outerRectGradientColors = [NSArray arrayWithObjects:   
  14.     (id)strokeColor.CGColor,   
  15.     (id)fillColor.CGColor, nil nil];  
  16. CGFloat outerRectGradientLocations[] = {01};  
  17. CGGradientRef outerRectGradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)outerRectGradientColors, outerRectGradientLocations);  
  18. NSArray* gradientColors = [NSArray arrayWithObjects:   
  19.     (id)strokeColor2.CGColor,   
  20.     (id)fillColor2.CGColor, nil nil];  
  21. CGFloat gradientLocations[] = {01};  
  22. CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)gradientColors, gradientLocations);  
  23. //// Shadow Declarations  
  24. UIColor* darkShadow = shadowColor2;  
  25. CGSize darkShadowOffset = CGSizeMake(3.13.1);  
  26. CGFloat darkShadowBlurRadius = 5;  
  27. UIColor* lightShadow = shadowColor3;  
  28. CGSize lightShadowOffset = CGSizeMake(3.13.1);  
  29. CGFloat lightShadowBlurRadius = 5;  
  30. //// Frames  
  31. CGRect progressIndicatorFrame = CGRectMake(-1032147);  
  32. //// Subframes  
  33. CGRect group = CGRectMake(CGRectGetMinX(progressIndicatorFrame) + 10, CGRectGetMinY(progressIndicatorFrame) + 9, CGRectGetWidth(progressIndicatorFrame) - 2520);  
  34. CGRect activeProgressFrame = CGRectMake(CGRectGetMinX(group) + floor(CGRectGetWidth(group) * 0.00000 + 0.5), CGRectGetMinY(group) + floor(CGRectGetHeight(group) * 0.00000 + 0.5), floor(CGRectGetWidth(group) * 1.00000 + 0.5) - floor(CGRectGetWidth(group) * 0.00000 + 0.5), floor(CGRectGetHeight(group) * 1.00000 + 0.5) - floor(CGRectGetHeight(group) * 0.00000 + 0.5));  
  35. //// Abstracted Attributes  
  36. CGRect progressTrackActiveRect = CGRectMake(CGRectGetMinX(activeProgressFrame) + 4, CGRectGetMinY(activeProgressFrame) + 5, CGRectGetWidth(activeProgressFrame) - 810);  
  37. //// Progress Bar  
  38. {  
  39.     //// Border Drawing  
  40.     CGRect borderRect = CGRectMake(CGRectGetMinX(progressIndicatorFrame) + 2, CGRectGetMinY(progressIndicatorFrame) + 3, CGRectGetWidth(progressIndicatorFrame) - 534);  
  41.     UIBezierPath* borderPath = [UIBezierPath bezierPathWithRoundedRect: borderRect cornerRadius: 4];  
  42.     CGContextSaveGState(context);  
  43.     CGContextSetShadowWithColor(context, darkShadowOffset, darkShadowBlurRadius, darkShadow.CGColor);  
  44.     CGContextBeginTransparencyLayer(context, NULL);  
  45.     [borderPath addClip];  
  46.     CGContextDrawLinearGradient(context, outerRectGradient,  
  47.         CGPointMake(CGRectGetMidX(borderRect), CGRectGetMinY(borderRect)),  
  48.         CGPointMake(CGRectGetMidX(borderRect), CGRectGetMaxY(borderRect)),  
  49.         0);  
  50.     CGContextEndTransparencyLayer(context);  
  51.     ////// Border Inner Shadow  
  52.     CGRect borderBorderRect = CGRectInset([borderPath bounds], -lightShadowBlurRadius, -lightShadowBlurRadius);  
  53.     borderBorderRect = CGRectOffset(borderBorderRect, -lightShadowOffset.width, -lightShadowOffset.height);  
  54.     borderBorderRect = CGRectInset(CGRectUnion(borderBorderRect, [borderPath bounds]), -1, -1);  
  55.     UIBezierPath* borderNegativePath = [UIBezierPath bezierPathWithRect: borderBorderRect];  
  56.     [borderNegativePath appendPath: borderPath];  
  57.     borderNegativePath.usesEvenOddFillRule = YES;  
  58.     CGContextSaveGState(context);  
  59.     {  
  60.         CGFloat xOffset = lightShadowOffset.width + round(borderBorderRect.size.width);  
  61.         CGFloat yOffset = lightShadowOffset.height;  
  62.         CGContextSetShadowWithColor(context,  
  63.             CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)),  
  64.             lightShadowBlurRadius,  
  65.             lightShadow.CGColor);  
  66.         [borderPath addClip];  
  67.         CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(borderBorderRect.size.width), 0);  
  68.         [borderNegativePath applyTransform: transform];  
  69.         [[UIColor grayColor] setFill];  
  70.         [borderNegativePath fill];  
  71.     }  
  72.     CGContextRestoreGState(context);  
  73.     CGContextRestoreGState(context);  
  74.    
  75.     //// ProgressTrack Drawing  
  76.     CGRect progressTrackRect = CGRectMake(CGRectGetMinX(progressIndicatorFrame) + 12, CGRectGetMinY(progressIndicatorFrame) + 12, CGRectGetWidth(progressIndicatorFrame) - 2914);  
  77.     UIBezierPath* progressTrackPath = [UIBezierPath bezierPathWithRoundedRect: progressTrackRect cornerRadius: 7];  
  78.     CGContextSaveGState(context);  
  79.     CGContextSetShadowWithColor(context, lightShadowOffset, lightShadowBlurRadius, lightShadow.CGColor);  
  80.     CGContextBeginTransparencyLayer(context, NULL);  
  81.     [progressTrackPath addClip];  
  82.     CGContextDrawLinearGradient(context, gradient,  
  83.         CGPointMake(CGRectGetMidX(progressTrackRect), CGRectGetMinY(progressTrackRect)),  
  84.         CGPointMake(CGRectGetMidX(progressTrackRect), CGRectGetMaxY(progressTrackRect)),  
  85.         0);  
  86.     CGContextEndTransparencyLayer(context);  
  87.     ////// ProgressTrack Inner Shadow  
  88.     CGRect progressTrackBorderRect = CGRectInset([progressTrackPath bounds], -darkShadowBlurRadius, -darkShadowBlurRadius);  
  89.     progressTrackBorderRect = CGRectOffset(progressTrackBorderRect, -darkShadowOffset.width, -darkShadowOffset.height);  
  90.     progressTrackBorderRect = CGRectInset(CGRectUnion(progressTrackBorderRect, [progressTrackPath bounds]), -1, -1);  
  91.     UIBezierPath* progressTrackNegativePath = [UIBezierPath bezierPathWithRect: progressTrackBorderRect];  
  92.     [progressTrackNegativePath appendPath: progressTrackPath];  
  93.     progressTrackNegativePath.usesEvenOddFillRule = YES;  
  94.     CGContextSaveGState(context);  
  95.     {  
  96.         CGFloat xOffset = darkShadowOffset.width + round(progressTrackBorderRect.size.width);  
  97.         CGFloat yOffset = darkShadowOffset.height;  
  98.         CGContextSetShadowWithColor(context,  
  99.             CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)),  
  100.             darkShadowBlurRadius,  
  101.             darkShadow.CGColor);  
  102.         [progressTrackPath addClip];  
  103.         CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(progressTrackBorderRect.size.width), 0);  
  104.         [progressTrackNegativePath applyTransform: transform];  
  105.         [[UIColor grayColor] setFill];  
  106.         [progressTrackNegativePath fill];  
  107.     }  
  108.     CGContextRestoreGState(context);  
  109.     CGContextRestoreGState(context);  
  110.    
  111.     //// Group  
  112.     {  
  113.         //// ProgressTrackActive Drawing  
  114.         UIBezierPath* progressTrackActivePath = [UIBezierPath bezierPathWithRoundedRect: progressTrackActiveRect cornerRadius: 5];  
  115.         [color setFill];  
  116.         [progressTrackActivePath fill];  
  117.     }  
  118. }  
  119. //// Cleanup  
  120. CGGradientRelease(outerRectGradient);  
  121. CGGradientRelease(gradient);  
  122. CGColorSpaceRelease(colorSpace);  

注意: 你的代碼可能看起來和上面的有一些不同,這取決於每一個人在畫圖的時候的細節,這沒啥關係。

這裏咱們能夠明顯看到PaintCode給咱們作了多少工做。你們能夠想象若是一行一行的去寫會用多少時間!可是若是這是你的愛好,固然沒有關係— 可是大部分應該會更想花時間在編寫一些更有趣的代碼上吧! :]
代碼前部分是 colors, gradients, 和 shadows的申明。 接着是frame的申明。 這裏會有一個 progressTrackActiveRect 在//// Abstracted Attributes 代碼段裏, 他是用來控制進度條綠色部分的進度的, 工做原理和 UIProgressView 同樣。
接下來 Progress Bar 代碼段是用來繪製整個進度條的邊框,進度槽,以及其餘的部分,而後另外一個代碼段是管理 ProgressActiveGroup的。 最後就是一些收尾代碼。

注意: 這篇教程並不會去深刻的講解 Core Graphics繪製。 咱們使用PaintCode的緣由就是讓你們可以在不瞭解CoreGraphic狀況下去建立更多的炫酷的控件。這讓你們可以花更多時間在開發和調試上,而不是在這裏慢慢的畫畫。

可是,瞭解一點點Core Graphics 知識能讓你們更好的去認識PaintCode生成的代碼,也一樣可讓你們去修改這些代碼。 要想了解更多 Core Graphics, 請查看咱們的些列教程 Core Graphics tutorial series!

 

顯示進度條

如今咱們的進度條已經準備好了,接下來的第一個測試就是看它是否能顯示出來。
在xcode裏找到 ResourcesStoryboardsMainStoryboard.storyboard ,而後打開它。 選擇 Progress View Controller scene 而後從Object Library添加一個 View 進去。在Identity Inspector 裏改變這個view的class爲ProgressView, 而後在 Size Inspector裏面把它的屬性改成以下:

  • X: 20
  • Y: 183
  • Width: 280
  • Height: 46

Progress view size inspector
最後把這個項目運行一次。咱們的進度條應該和下面同樣:
Progress bar first run
雖然進度條看起來已經不錯了 — 可是他並無按照storyboard裏view的大小繪製出來,要解決這個問題,咱們繼續回到 ProgressView.m裏的 drawRect: 找到下面這行:

[objc]  view plain copy
  1. …  
  2. //// Frames  
  3. CGRect progressIndicatorFrame = CGRectMake(2131834);  
  4. ...  

把它改爲下面的這句:

[objc]  view plain copy
  1. - (void)drawRect:(CGRect)rect  
  2. {  
  3.     ...  
  4.     // Frames  
  5.     CGRect progressIndicatorFrame = rect;  
  6.     ...  
  7. }  

這句是讓進度條的大小和咱們在storyboard裏設置的view的大小同樣。
再編譯運行一次,進度條就應該和storyboard裏的veiw同樣大小了:
Progress bar frame fixed
如今進度條已經能正確的顯示出來了,接下來咱們要作的就是讓他動起來。

讓進度條動起來

打開 ProgressViewController.m 在 @implementation 以前加入下面的代碼:

[objc]  view plain copy
  1. // 1  
  2. #import "ProgressView.h"  
  3. #import "ProgressViewController.h"  
  4. // 2  
  5. #define kSecondsForCompleteUpdate   3.0  
  6. #define kUpdateInterval             0.02  
  7. // 3  
  8. @interface  ProgressViewController ()  
  9. @property (weak, nonatomic) IBOutlet ProgressView *progressView;  
  10. @property (weak, nonatomic) IBOutlet UIButton *startProgressButton;  
  11. @property (strongnonatomicNSTimer *timer;  
  12. @end  

咱們如今來看看上面的代碼作了什麼:

  1. 引入 ProgressView.h 頭文件,這樣在view controller裏就能夠引用它。
  2. 定義了2個常量 kSecondsForCompleteUpdate 和 kUpdateInterval 並給他們賦值。 用它們來控制着進度條的行爲。
  3. 增長了一個類的Extension 而且爲 UIButton 和 UIProgressView申明瞭帶xib鏈接口的屬性, 以及一個定時器用來更新進度條的 progress屬性.

接着添加下面的代碼 (任然在 ProgressViewController.m裏):

[objc]  view plain copy
  1. -(IBAction)startProgressTapped {  
  2.     self.progressView.progress = 0.0;  
  3.    
  4.     self.startProgressButton.enabled = NO;  
  5.    
  6.     self.timer = [NSTimer timerWithTimeInterval:kUpdateInterval  
  7.                                          target:self  
  8.                                        selector:@selector(updateProgressView)  
  9.                                        userInfo:nil  
  10.                                         repeats:YES];  
  11.    
  12.     [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];  
  13.    
  14.     [self.timer fire];  
  15. }  

當屏幕上的按鈕被點擊時,這段代碼將被調用。 首先先把進度條的 progress屬性設置爲 0。而後將按鈕禁用掉,這樣在進度條動畫進行的時候咱們將不能再次點擊按鈕。 而後,建立一個定時器,用來控制動畫的進行時長。
最後, 把定時器加進run loop來啓動定時器-模擬事件的開始。
讓咱們來看看self.timer 這行代碼。 你們會發現定事情調用了 updateProgressView這個方法,這是用來更新進度條的進度的。 咱們立刻加加入這段代碼,
把下面這段加入 ProgressViewController.m中:

[objc]  view plain copy
  1. -(void)updateProgressView; {  
  2.     if (self.progressView.progress < 1.0) {  
  3.         self.progressView.progress += (kUpdateInterval / kSecondsForCompleteUpdate);  
  4.     } else {  
  5.         [self.timer invalidate];  
  6.         self.startProgressButton.enabled = YES;  
  7.     }  
  8. }  

這段代碼會檢查progress屬性是否還小於1, 也就是尚未完成。 若是還沒完成,逐步的增長進度, 用前面定義2個常量來控制進度條增長的速率。
若是進度條的progress大於1了,也就是他100%完成了, 咱們會把定時器禁用掉,而後恢復按鈕來讓咱們可以再次模擬進度條增長。
如今咱們剩下的就是添加一個按鈕來觸發 startProgressTapped方法,讓咱們能開始模擬整個測試了。

加入開始按鈕

回到storyboard而後建立一個 Round Rect Button 放在 Progress View Controller 裏面。 在 Attributes Inspector 裏面把它的 title 改爲 「Start Progress」 選擇 Touch Up Inside 把他和代碼裏的 startProgressTapped鏈接起來。
而後修改按鈕屬性,在 Size Inspector裏,應該和下圖同樣:

  • X: 97
  • Y: 20
  • Width: 128
  • Height: 44
  • Autosizing: 左上右固定間隙,固定寬。 可變的下間隙,和可變的高。

Button size attributes
接下來,連接代碼裏的按鈕和進度條。 你的連接應該和下面同樣:
Progress controller connections
編譯運行一次項目,而後點擊 Start Progress 按鈕。咱們的進度條應該就和下圖同樣了:
Testing progress with button
Umm…進度條竟然沒有動! 點了按鈕之後啥都沒發生!!怎麼破!

更新進度條

問題出在,在 ProgressView 裏面沒有代碼去更新進度槽。這玩意兒仍然是靜態的被顯示着,就像在PaintCode裏同樣。 咱們須要增長一些代碼,來讓進度條能夠基於本身的 progress屬性來繪製。
換到 ProgressView.m文件裏.。找到下面這段:

[objc]  view plain copy
  1. …  
  2. //// Abstracted Attributes  
  3. CGRect progressTrackActiveRect = CGRectMake(CGRectGetMinX(activeProgressFrame) + 2, CGRectGetMinY(activeProgressFrame) + 2, CGRectGetWidth(activeProgressFrame) - 410);  
  4. …  

用下面的代碼替換掉整個 CGRect progressTrackActiveRect這行:

[objc]  view plain copy
  1. CGRect progressTrackActiveRect = CGRectMake(CGRectGetMinX(activeProgressFrame) + 3,  
  2.                                                 CGRectGetMinY(activeProgressFrame) + 2,  
  3.                                                 (CGRectGetWidth(activeProgressFrame) - 4) * self.progress,  
  4.                                                 10);  

新的代碼會基於進度條的 progress 屬性去改變它的rect的寬。
最後一件事 —設置進度條的progress 爲0。 這樣在程序啓動的時候,進度條就能和咱們但願的同樣處於0的位置。
回到 ProgressViewController.m 文件加入下面的代碼:

[objc]  view plain copy
  1. -(void)viewDidLoad {  
  2.     [super viewDidLoad];  
  3.    
  4.     self.progressView.progress = 0.0;  
  5. }  

在跑一次程序,點擊按鈕。 咱們的的進度條應該開始逐步的填滿整個進度槽了。
Final project
如今咱們完成了能夠動態更新的進度條了。 他能夠用來模擬3秒鐘的後臺任務或者下載任務。

總結

你能夠在這裏下載完整的PaintCode和XCode項目。
恭喜你們 – 在這個過程當中使用PaintCode製做了一個自定義的進度條,而且把它集成進了本身的項目中,相信你們必定學到了很多。
有了這個新技術,你們能夠想怎麼作怎麼作,隨意的去自定義本身的進度條 – 甚至是iOS7裏的扁平進度條 :]
咱們還有好消息 – 更多的教程會陸續而!在第三部分教程裏,咱們將會教你如何去用bezier路徑畫出一個箭頭,並把它使用在一個簡單的遊戲裏。


原帖:http://www.raywenderlich.com/35720/paintcode-tutorial-custom-progress-bar

相關文章
相關標籤/搜索