iOS開發實踐:用Swift和Core Animatoin創建圓形圖片加載動畫
幾個星期之前,Michael Villar在 Motion試驗 中創建一個非常有趣的加載動畫。 下面的GIF圖片展示這個加載動畫,它將一個圓形進度指示器和圓形漸現動畫結合。這個組合的效果有趣,獨一無二和有點迷人。
這個教程將會教你如何使用Swift和Core Animatoin來重新創建這個效果,讓我們開始吧!
基礎
首先下載這個教程的 啟動項目 ,然后編譯和運行。過一會之后,你應該看到一個簡單的image顯示:
這個啟動項目已經預先在恰當的位置將views和加載邏輯編寫好了。花一分鐘來瀏覽來快速了解這個項目;那里有一個 ViewController,ViewController里有一個命名為CustomImageView的UIImageView子類, 還有一個SDWebImage的方法被調用來加載image。
你可能注意到當你第一次運行這個app的時候,當image下載時這個app似乎會暫停幾秒,然后image會顯示在屏幕。當然,此刻沒有圓形進度指示器 - 你將會在這個教程中創建它!
你會在兩個步驟中創建這個動畫:
- 圓形進度。 首先,你會畫一個圓形進度指示器,然后根據下載進度來更新它。
- 擴展圓形圖片。 第二,你會通過擴展的圓形窗口來揭示下載圖片。 </ol>
緊跟著下面步驟來逐步實現!
創建圓形指示器
想一下關于進度指示器的基本設計。這個指示器一開始是空來展示0%進度,然后逐漸填滿直到image完成下載。通過設置CAShapeLayer的path為circle來實現是相當簡單。
注意:如果你不熟悉CAShapeLayer(或CALayers)的基本概念,可以查看Scott Gardner的 CALayer in iOS with Swift 文章。
你可以通過CAShapeLayer的strokeStart和strokeEnd屬性來控制開始和結束位置的外觀。通過改變strokeEnd的值在0到1之間,你可以恰當地填充下載進度。
讓我們試一下。通過iOS\Source\Cocoa Touch Class template來創建一個新的文件,文件名為CircularLoaderView。設置它為UIView的子類。
點擊Next和Create。新的子類UIView將用來保存動畫的代碼。
打開CircularLoaderView.swift和添加以下屬性和常量到這個類:
let circlePathLayer = CAShapeLayer() let circleRadius: CGFloat = 20.0
circlePathLayer表示這個圓形路徑,而circleRadius表示這個圓形路徑的半徑。
添加以下初始化代碼到CircularLoaderView.swift來配置這個shape layer:
override init(frame: CGRect) { super.init(frame: frame) configure() }required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) configure() }
func configure() { circlePathLayer.frame = bounds circlePathLayer.lineWidth = 2 circlePathLayer.fillColor = UIColor.clearColor().CGColor circlePathLayer.strokeColor = UIColor.redColor().CGColor layer.addSublayer(circlePathLayer) backgroundColor = UIColor.whiteColor() }</pre>
兩個初始化方法都調用configure方法,configure方法設置一個shape layer的line width為2,fill color為clear,stroke color為red。將添加circlePathLayer添加到view's main layer。然后設置view的 backgroundColor 為white,那么當image加載時,屏幕的其余部分就忽略掉。
添加路徑
你會注意到你還沒賦值一個path給layer。為了做到這點,添加以下方法(還是在CircularLoaderView.swift文件):
func circleFrame() -> CGRect { var circleFrame = CGRect(x: 0, y: 0, width: 2*circleRadius, height: 2*circleRadius) circleFrame.origin.x = CGRectGetMidX(circlePathLayer.bounds) - CGRectGetMidX(circleFrame) circleFrame.origin.y = CGRectGetMidY(circlePathLayer.bounds) - CGRectGetMidY(circleFrame) return circleFrame }
上面那個方法返回一個CGRect的實例來界定指示器的路徑。這個邊框是2*circleRadius寬和2*circleRadius高,放在這個view的正中心。
每次這個view的size改變時,你會需要都重新計算circleFrame,所以你可能將它放在一個獨立的方法。
現在添加以下方法來創建你的路徑:
func circlePath() -> UIBezierPath { return UIBezierPath(ovalInRect: circleFrame()) }
這只是根據circleFrame限定來返回圓形的UIBezierPath。由于circleFrame()返回一個正方形,在這種情況下”橢圓“會最終成為一個圓形。
由于layers沒有autoresizingMask這個屬性,你需要在layoutSubviews方法更新circlePathLayer的frame來恰當地響應view的size變化。
下一步,覆蓋layoutSubviews()方法:
override func layoutSubviews() { super.layoutSubviews() circlePathLayer.frame = bounds circlePathLayer.path = circlePath().CGPath }
由于改變了frame,你要在這里調用circlePath()方法來觸發重新計算路徑。
現在打開CustomImageView.swift文件和添加以下CircularLoaderView實例作為一個屬性:
let progressIndicatorView = CircularLoaderView(frame: CGRectZero)
下一步,在之前下載圖片的代碼添加這幾行代碼到init(coder:)方法:
addSubview(self.progressIndicatorView) progressIndicatorView.frame = bounds progressIndicatorView.autoresizingMask = .FlexibleWidth | .FlexibleHeight
上面代碼添加進度指示器作為一個subview添加到自定義的image view。autoresizingMask確保進度指示器view保持與image view的size一樣。