設計效果以下:git
Dribbble網址:Daily-UI-094-News
Github網址:CBPullToRefleshgithub
如下將針對設計過程當中的知識點進行詳細的記錄。swift
這樣的曲線相對簡單,咱們這裏直接使用系統提供的一次貝塞爾曲線方法:動畫
public func addQuadCurveToPoint(endPoint: CGPoint, controlPoint: CGPoint)
可是咱們還須要根據scrollView的contentOffSet來動態改變該曲線的弧線曲折度,因此這裏咱們將改變曲折度寫成一個方法:spa
func wavePath(bendDist bendDist:CGFloat) -> CGPathRef { let width = self.frame.width let height = self.frame.height let bottomLeftPoint = CGPointMake(0, height) let topMidPoint = CGPointMake(width / 2, -bendDist) let bottomRightPoint = CGPointMake(width, height) let bezierPath = UIBezierPath() bezierPath.moveToPoint(bottomLeftPoint) bezierPath.addQuadCurveToPoint(bottomRightPoint, controlPoint: topMidPoint) bezierPath.addLineToPoint(bottomLeftPoint) return bezierPath.CGPath }
這樣咱們就能夠經過只傳入bendDist來改變曲折度。設計
根據上面的動圖,咱們能夠看到開始刷新操做時,曲折度逐漸減少,這裏咱們須要一個動畫來實現這個功能:code
func boundAnimation(bendDist bendDist: CGFloat) { let bounce = CAKeyframeAnimation(keyPath: "path") bounce.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) let values = [ self.wavePath(bendDist: bendDist), self.wavePath(bendDist: bendDist * 0.8), self.wavePath(bendDist: bendDist * 0.6), self.wavePath(bendDist: bendDist * 0.4), self.wavePath(bendDist: bendDist * 0.2), self.wavePath(bendDist: 0) ] bounce.values = values bounce.duration = bounceDuration bounce.removedOnCompletion = false bounce.fillMode = kCAFillModeForwards bounce.delegate = self self.waveLayer.addAnimation(bounce, forKey: "return") }
至此波紋曲線的部分就基本完成。blog
在進行波紋曲線回滾動畫的時候,咱們的scrollView也有適當的上移,這樣的上移動畫,若是直接使用setContentOffset
來進行視圖的移動,選擇animation: true
的狀況下,每一次移動,都會使得scrollView從頂部從新移動到目標位置,形成視圖一直閃的狀況。圖片
爲了不這樣的狀況,咱們能夠依然選擇setContentOffset
來進行scrollView的視圖移動,可是咱們設置animation: false
來關閉系統提供的動畫,選擇本身來實現動畫的效果。rem
首先,咱們設置一個定時器:
NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "scollBackAnimation:", userInfo:stepNum, repeats: true)
這個定時器的時間步進咱們設置爲0.01s,是由於NSTimer
準確度並不高。而後咱們給這個定時器附加一個stepNum
的屬性,這個屬性指的是每一次執行setContentOffset
向上移動的距離,這個屬性的值,咱們這樣子計算:
而後,咱們每隔0.01秒,刷新一次contentOffset
,造成一種視圖向上持續移動的視覺效果,代碼以下:
func scollBackAnimation(timer: NSTimer) { let stepNum = timer.userInfo! as! CGFloat scrollViewContentOffSetY = scrollViewContentOffSetY! + stepNum if scrollViewContentOffSetY >= finalScrollViewContentOffSetY { timer.invalidate() } scrollView?.setContentOffset(CGPoint(x: 0, y: scrollViewContentOffSetY!), animated: false) }
接下來,咱們來作小球的部分。
這裏,咱們使用貝塞爾曲線畫任意弧度的一個方法:
public convenience init(arcCenter center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)
這個工廠方法用於畫弧,參數說明以下:
center:弧線中心點的座標
radius:弧線所在圓的半徑
startAngle:弧線開始的角度值
endAngle:弧線結束的角度值
clockwise:是否順時針畫弧線
畫出小球是比較容易的部分,可是想要畫出小球的運行軌跡就稍微有點複雜。
如下咱們較爲詳細的來講明一下:
這一部分,如何去作更重要,牽扯到不少小細節,代碼就不貼了,請看Github中的項目文件
至此,咱們知道了弧線所在圓的半徑R和該孤的角度Θ。如今,咱們只要知道x和y,就可使用上面提到的來畫出所要的弧線。
根據這個圖片,咱們定義一個常量爲ballSpace
(兩球之間的間隔),已知球的半徑爲ballSize / 2
,咱們能夠列出如下公式:
$$ x = (ballSize + ballSpace) * (1.5 - CGFloat(ballTag)) $$
ballTag
爲每隔球的序號,從0開始。
$$ y = stopDist / 2 $$
stopDist
爲scrollView停下,小球開始浮動動畫的位置。
小球的浮動只是簡單的上下位置的變換,值得注意的是每顆小球開始動畫的時間點存在差值,這個差值使得小球有了錯位浮動的效果,下面是代碼:
func floatUpOrDown() { let move = CAKeyframeAnimation(keyPath: "position.y") move.values = [0,1,2,3,4,5,4,3,2,1,0,-1,-2,-3,-4,-5,-4,-3,-2,-1,0] move.duration = 1 move.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) move.additive = true move.fillMode = kCAFillModeForwards move.removedOnCompletion = false self.addAnimation(move, forKey: move.keyPath) }
let timeDelay: NSTimeInterval = Double(layerTag!) * 0.2 timer = NSTimer.schedule(delay: timeDelay, repeatInterval: 1, handler: { (timer) -> Void in self.floatUpOrDown() })
至此,demo中的要點都已經簡單說明,但願你們有所收穫。