論動畫在App中的重要性
引言
一款App是否足夠吸引人一方面是需要豐富的內(nèi)容曙博,另一方面就是要足夠人性化的交互簸搞,還有一些錦上添花的動畫效果,在這里我們討論一下關于Animation的基本實現(xiàn)鸣奔,推薦大家試用FaceBook Paper典尾,里面包含了大量的非原生動畫效果役拴,Paper團隊甚至封裝了相應的開源庫Pop,讓開發(fā)者接入自定義動畫動畫也十分簡便钾埂。動畫的接入要適當河闰,否則用戶面對眼花繚亂的動畫效果科平,都會無從下手。這里有個基于Pop的Demo
當然導入一個復雜的第三方庫可能有些小題大做姜性,所以我們開始從最基本的創(chuàng)作Animation開始
在iOS設備上畫動畫可以分為以下幾種方式瞪慧,最直接的時在UIView上實現(xiàn)動畫,假如實現(xiàn)效果復雜可以在Layer下繪制,此外iOS7還推出了UIDynamicAnimator部念,實現(xiàn)類似游戲中的諸多動畫效果
動畫不是那種天花亂墜式弃酌,而是在一些細節(jié)之處也添加動畫,例如在view之間的約束儡炼,在設備旋轉(zhuǎn)過程中調(diào)整對應動畫妓湘,viewController之間的切換也是可以自定義transitioning
UIView Animations
UIView可以設置動畫的屬性:
屬性 | 改變 |
---|---|
frame & bounds & center | 改變View的位置及自身大小 |
transform | 改變縮放scale,旋轉(zhuǎn)角度rotate(3D效果需要用Core Animation) |
alpha | 改變透明度 |
backgroundColor | 改其背景色 |
最直接的做法
//基本
UIView.animateWithDuration(0.5, animations: {
self.yourView.bounds.size.witdh += 70.0
self.yourView.backgroundColor = UIColor.greenColor()
self.yourView.alpha = 0.5
})
//View和View之間的過渡
UIView.transitionWithView(containerView, duration: 0.2, options: .TransitionFlipFromLeft, animations: { _ in fromView.removeFromSuperview(); containerView.addSubview(toView) }, completion: nil)
//入門Option,delay,option自由選擇
UIView.animateWithDuration(0.5, delay: 0.4, options: .Repeat, animations:{
self.yourView.center.x + = 30.0
},completion: nil)
//進階乌询,Spring屬性可以調(diào)整動畫的彈簧效果
UIView.animateWithDuration(1.5, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.0, options: nil, animations: {
self.loginButton.bounds.size.width += 80.0
label的文字可以進行一些過渡效果
}, completion: nil)
//高階榜贴,復雜動畫組合之KeyFrame
UIView.animateKeyframesWithDuration(1.5, delay: 0.0, options: nil, animations: {
//添加KeyFrames,options中可以選擇不同的限制
//1
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.25, animations: {
self.planeImage.center.x += 80.0
self.planeImage.center.y -= 10.0
})
//2
UIView.addKeyframeWithRelativeStartTime(0.1, relativeDuration: 0.4) { self.planeImage.transform = CGAffineTransformMakeRotation(CGFloat(-
M_PI_4/2)) }
}, completion: nil)
需要注意的是動畫一旦開始就沒有辦法暫停
Auto Layout
為AutoLayout Aonstraints創(chuàng)建動畫的首要條件就是楣责,你要先用了AutoLayout
動畫的創(chuàng)建方式也類似
//對已有constant作出對應修改操作 or 創(chuàng)建新的contsant
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 10.0, options: .CurveEaseIn, animations: {
self.view.layoutIfNeeded() }, completion: nil)
其中的layoutIfNeed()十分關鍵,因為這樣UIKit才會知道你修改了layout
Layer Animation
如果說View是高度定制化的給你簡單接口的就像這樣那CALayer就是這樣
Layer的強大之處是可以設置更多的屬性聂沙,實現(xiàn)3D效果秆麸,添加mask,添加shadow及汉,并且這一切都是建立在直接調(diào)用GPU來達到快速相應沮趣,一切都非常簡便,其實每個View都有Layer坷随,我們在Layer層面做的修改也可以展現(xiàn)在View上
屬性 | 作用 |
---|---|
size & position | 改變layer的位置及自身大小 |
transform | 改2D,3D情況下的現(xiàn)實效果 |
shadow | 改變陰影 |
border | 邊框效果 |
opacity | 改其透明度 |
//記得引入QuartzCore
//基本
let ownStyle = CABasicAnimation(keyPath:"position.x")
ownStyle.fromValue = -view.bounds.size.width/2
ownStyle.toValue = view,bounds.size.width/2
ownStyle.duration = 0.5
yourView.layer.addAnimation(ownStyle, forKey: nil)
//入門房铭,動畫之間存在時間差,我們可以設置fillMode和beginTime來實現(xiàn)特定效果
ownStyle.beginTime = CACurrentMediaTime() + 0.3
ownStyle.fillMode = KCAFillModeRemoved //default
//kCAFillMode主要作用就是控制你動畫在開始和結(jié)束時候的一些效果
//進階 CAAnimation delegate pattern
func animationDidStop & animationDidStart
//與block中的相類似温眉,你也可以利用KVC特性設置相應內(nèi)容
ownStyle.setValue(yourView.layer, forKey:"layer")
ownStyle.setValue("name", forKey:"form")
override func animationStop(anima: CAAnimation!, finished flag: Bool) {
if let name = ownStyle.valueForKey("form") as? String {
if name = "form" {
//add new animation and add it to the layer
}
}
}
//addAnimation中的Key作用缸匪,標示動畫,在恰當?shù)臅r候移除對應動畫类溢,而不是移除動畫效果本身
//高階凌蔬,AnimationGroup
let groupAnimation = CAAnimationGroup()
let oneAnimation = CABasicAnimation(keyPath:"transform.scale")
//blablabla
let twoAnimation = CABasicAnimation(keyPath:"opacity")
//detail set for twoAnimation
//sey timingFunction
groupAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
//這么只能一起做動畫,這里內(nèi)部沒有時間順序
groupAnimation.animations = [oneAnimation, twoAnimation]
yourView.layer.addAnimation(groupAnimation, forKey: nil)
//CAKeyFrame,關鍵幀動畫Layer級別效果
let wobble = CAKeyframeAnimation(keyPath: "transform.rotation") wobble.duration = 0.25
wobble.repeatCount = 4
//比View的keyFrame設置方便多了
//values與keyTimes一一對應
wobble.values = [0.0, -M_PI_4/4, 0.0, M_PI_4/4, 0.0]
wobble.keyTimes = [0.0, 0.25, 0.5, 0.75, 1.0] heading.layer.addAnimation(wobble, forKey: nil)
//不過坑爹的是這樣闯冷,比如CGPoint砂心,CGSize,CGRect蛇耀,CATransform3D辩诞,都要解包
let move = CABasicAnimation(keyPath: "position")
move.duration = 1.0
move.fromValue = NSValue(CGPoint:CGPoint(x:100.0, y:100.0))
move.toValue = NSValue(CGPoint:CGPoint(x:200.0, y:200.0))
有木有發(fā)現(xiàn)現(xiàn)在很多App中用戶頭像都是圓形的QQ的是,微博個人主頁纺涤,instagram,Path等等,這里還涉及到好多設計心理學的東西這里給個鏈接圓形頭像為何流行起來--知乎
廢話不多說直接上代碼吧译暂,so easy,簡單說來就是加一個mask,可這好像和動畫沒有什么關系抠忘,聽我慢慢道來
//1
_imageView.frame = CGRectMake(0, 0, iconWH, iconWH);
_imageView.layer.cornerRadius = _imageView.frame.size.width / 2;
_imageView.clipsToBounds = YES;
_imageView.layer.borderWidth = 2.0f;
_imageView.layer.borderColor = [UIColor whiteColor].CGColor;
//2
let photoLayer = CALayer()
let circleLayer = CAShapeLayer()
let maskLayer = CAShapeLayer()
override func didMoveToWindow() {
layer.addSublayer(photoLayer)
photoLayer.mask = maskLayer
layer.addSublayer(circleLayer)
addSubview(label)
}
//設置image的Frame
photoLayer.frame = CGRect(
x: (bounds.size.width - image.size.width + lineWidth)/2,
y: (bounds.size.height - image.size.height - lineWidth)/2,
width: image.size.width,
height: image.size.height)
//畫圓,用UIBezierPath
circleLayer.path = UIBezierPath(ovalInRect: bounds).CGPath
circleLayer.strokeColor = UIColor.whiteColor().CGColor
circleLayer.lineWidth = lineWidth
circleLayer.fillColor = UIColor.clearColor().CGColor
//設置對應的path
maskLayer.path = circleLayer.path
maskLayer.position = CGPoint(x: 0.0, y: 10.0)
三者對應關系circleLayer在頂秧秉,maskLayer在中褐桌,photoLayer在底
如果你要實現(xiàn)這個imageView如在撞擊墻壁時,mask產(chǎn)生變化象迎,變成方形荧嵌,這樣一個碰撞式的動畫還是很帶感的
let maskLayer = CAShapeLayer()
imageView.mask = maskLayer
let squarePath = UIBezierPath(rect: bounds).CGPath
let morph = CABasicAnimation(keyPath: "path")
morph.duration = 0.25
morph.fromValue = circleLayer.path
morph.toValue = squarePath
circleLayer.addAnimation(morph, forKey: nil)
maskLayer.addAnimation(morph, forKey: nil)
平面效果是否不夠酷炫,那么來足夠眼前一亮的的
3D效果,這里要扯點美術內(nèi)容了砾淌,透視法
目前的屏幕還是2D平面啦撮,也無法實現(xiàn)所謂全息影像
那么在2D屏幕下的3D就需要透過視覺差來投影出來3D效果
當相機距離很小時候拉伸效果明顯,后者距離很大
var identity = CATransform3DIdentity
//m34啥意思汪厨,簡單說來就是你看屏幕視角的遠近赃春,這里是指近大遠小,-1.0/1000啥概念劫乱,這里1000是指相機距離织中,距離越小,近大遠效果越明顯
identity.m34 = -1.0/1000
//percent在這里是指你在移動當前View時候frame.orgin.x與一固定值如width之間的比例衷戈,這樣view的移動就產(chǎn)生相應不同程度的過渡效果
let remainingPercent = 1.0 - percent
let angle = remainingPercent * CGFloat(-M_PI_2)
//這里是沿著y軸旋轉(zhuǎn)狭吼,這里不得不說一下iOS中坐標系是左手坐標系
let rotationTransform = CATransform3DRotate(identity, angle, 0.0, 1.0, 0.0)
let translationTransform = CATransform3DMakeTranslation(menuWidth * percent, 0, 0)
//還有諸如 CATransform3DTranslate,CATransform3DScale相關設置都可以嘗試
menuButton.imageView.layer.transform = CATransform3DConcat(rotationTransform, translationTransform)
左邊是左手坐標系殖妇,右邊是我們慣用的右手坐標系刁笙,兩者區(qū)別在于z軸的方向,iOS是左手系
view的轉(zhuǎn)動效果如下谦趣,x,y,z軸你可以設置根據(jù)相應的軸產(chǎn)生不同動畫以及組合動畫
致此疲吸,iOS的動畫初窺到此為止,發(fā)現(xiàn)具體到動畫中矩陣轉(zhuǎn)換還是有點搞不清楚前鹅,后續(xù)還會有更多關于iOS Animation的內(nèi)容摘悴。