设计效果以下: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中的要点都已经简单说明,但愿你们有所收获。