最近得空做了一個(gè)小的新聞?lì)怉PP,基本上都是照著News Digest的模子刻出來的,之所以這個(gè)為參考,是因?yàn)橛X得News Digest這個(gè)APP做得真的很酷炫!
猛戳這里獲取整個(gè)項(xiàng)目源代碼
項(xiàng)目前端主要由swift編寫,本地?cái)?shù)據(jù)用CoreData,后端由Node.js編寫,后臺(tái)數(shù)據(jù)庫用MongoDB。
News Digest(雅虎新聞)模仿秀第一彈
News Digest(雅虎新聞)模仿秀第二彈
News Digest(雅虎新聞)模仿秀第三彈
這期我們來說一下倒計(jì)時(shí)動(dòng)畫的實(shí)現(xiàn),先上效果圖:

這里要分兩部分來講,一部分是圓圈動(dòng)畫的填充,一部分是時(shí)間的快速滾動(dòng),位于項(xiàng)目CustomView/MenuView和CustomView/CountdownView
- 圓圈動(dòng)畫的執(zhí)行
init(timeType: TimeType) {
super.init(frame: defaultFrame)
type = timeType
self.backgroundColor = UIColor.clearColor()
// CAShaperLayer Setting
circleShadowLayer.frame = self.bounds
circleShadowLayer.fillColor = UIColor.clearColor().CGColor
circleShadowLayer.lineWidth = 2.0
circleShadowLayer.strokeEnd = 1.0
circleShadowLayer.strokeColor = UIColor.RGBColor(255, green: 255, blue: 255, alpha: 0.2).CGColor
circleLayer.frame = self.bounds
circleLayer.fillColor = UIColor.clearColor().CGColor
circleLayer.lineWidth = 2.0
circleLayer.strokeEnd = 0
circleLayer.strokeColor = UIColor.RGBColor(0, green: 121, blue: 166, alpha: 1).CGColor
self.layer.addSublayer(circleShadowLayer)
self.layer.addSublayer(circleLayer)
}
這里的circleShadowLayer,就是動(dòng)圖底層那一個(gè)灰色的圈,我們?cè)O(shè)置strokeEnd = 1.0(就是說完全填充,形成一個(gè)閉環(huán)),這個(gè)strokeEnd是從0.0-1.0范圍
override func layoutSubviews() {
super.layoutSubviews()
defaultCircleRadius = (WIDTH - 100) < (self.bounds.height - 32) ? (WIDTH - 100)/2.0 : defaultCircleRadius
circleShadowLayer.frame = self.bounds
circleShadowLayer.path = circlePath().CGPath
circleLayer.frame = self.bounds
circleLayer.path = circlePath().CGPath
}
//MARK: - CirclePath
func circlePath() -> UIBezierPath {
return UIBezierPath.init(ovalInRect: circleFrame())
}
func circleFrame() -> CGRect {
var circleFrame = CGRect(x: 0, y: 0, width: 2*defaultCircleRadius, height: 2*defaultCircleRadius)
circleFrame.origin.x = CGRectGetMidX(circleShadowLayer.bounds) - CGRectGetMidX(circleFrame)
circleFrame.origin.y = CGRectGetMidY(circleShadowLayer.bounds) - CGRectGetMidY(circleFrame)
return circleFrame
}
circleFrame方法是設(shè)置圓圈的frame,讓它形成位于圓心的,半徑為defaultCircleRadius的圓,然后用UIBezierPath繪制路徑,每次調(diào)用layoutSubviews方法把路徑賦值給circleLayer
layoutSubviews調(diào)用時(shí)機(jī):
- 直接調(diào)用setLayoutSubviews。
- addSubview的時(shí)候。
- 當(dāng)view的frame發(fā)生改變的時(shí)候。
- 滑動(dòng)UIScrollView的時(shí)候。
- 旋轉(zhuǎn)Screen會(huì)觸發(fā)父UIView上的layoutSubviews事件。
- 改變一個(gè)UIView大小的時(shí)候也會(huì)觸發(fā)父UIView上的layoutSubviews事件。
期初我打算用UIView.animateWithDuration來賦值circleLayer的strokeEnd參數(shù),然后發(fā)現(xiàn)無論我設(shè)置duration是多少,動(dòng)畫執(zhí)行時(shí)間都是一樣的,后來查了一下資料才發(fā)現(xiàn)UIView.animateWithDuration只能動(dòng)畫改變這幾個(gè)參數(shù)
/*
* UIView.animateWithDuration(13.2, animations: {
* self.circleLayer.strokeEnd = CGFloat(interval/43200)
* })
* 這里執(zhí)行方式不可以使用UIView.animateWithDuration,只能用CAAnimations
* UIView動(dòng)畫執(zhí)行只能改變一下幾個(gè)參數(shù)
*
* The following properties of the UIView class are animatable:
* @property frame
* @property bounds
* @property center
* @property transform
* @property alpha
* @property backgroundColor
* @property contentStretch
*/
所以動(dòng)畫執(zhí)行strokeEnd的改變只能使用CAAnimations,只需要把strokeEnd的數(shù)值賦值給toValue,然后設(shè)置執(zhí)行時(shí)間即可
func animationExecute() {
circleLayer.addAnimation(self.circleAnimationImplement(1.6, delay: 0.3, toValue: interval!/43200.0), forKey: nil)
}
// Animation Implement
private func circleAnimationImplement(duration: NSTimeInterval, delay: Double, toValue: Double) -> CABasicAnimation {
let animation = CABasicAnimation.init(keyPath: "strokeEnd")
animation.duration = duration < 0.8 ? 0.8 : duration
animation.beginTime = CACurrentMediaTime() + delay
animation.fromValue = 0.0
animation.toValue = toValue
animation.removedOnCompletion = false // 這里如果不是false fillMode屬性不起作用
animation.fillMode = kCAFillModeForwards; // 保留動(dòng)畫后的樣子
// 設(shè)置Delegate
animation.delegate = self
return animation
}
- 時(shí)間的快速滾動(dòng)效果
其實(shí)中間就是一個(gè)UILabel,然后動(dòng)態(tài)修改它的attributedText即可
這個(gè)數(shù)字快速滾動(dòng)效果,我是模仿下面這位大大的代碼寫的
滾動(dòng)的數(shù)字:FlickerNumber
如果需要詳細(xì)了解,戳上面這個(gè)鏈接就可以詳細(xì)了解.
總的來說,大概實(shí)現(xiàn)思路就是:
- 設(shè)定起始數(shù)字/終止數(shù)字/執(zhí)行時(shí)間等
- 設(shè)定數(shù)字滾動(dòng)的頻率,比如說1/30等等
- 然后根據(jù)終止數(shù)字*頻率/執(zhí)行時(shí)間,就獲得每次數(shù)字變化的值
- 最后一個(gè)NSTimer以頻率來執(zhí)行改變UILabel的值
- 滾動(dòng)圈圈下面那個(gè)日期選擇
具體就不細(xì)說了,就一個(gè)UICollectionView
今天就到這里了。