引言
- 一款App是否足够吸引人一方面是需要丰富的内容,另一方面就是要足够人性化的交互,还有一些锦上添花的动画效果,在这里我们讨论一下关于Animation的基本实现,推荐大家试用FaceBook Paper,里面包含了大量的非原生动画效果,Paper团队甚至封装了相应的开源库Pop,让开发者接入自定义动画动画也十分简便。动画的接入要适当,否则用户面对眼花缭乱的动画效果,都会无从下手。这里有个基于Pop的Demo
- 当然导入一个复杂的第三方库可能有些小题大做,所以我们开始从最基本的创作Animation开始
- 在iOS设备上画动画可以分为以下几种方式,最直接的时在UIView上实现动画,假如实现效果复杂可以在Layer下绘制,此外iOS7还推出了UIDynamicAnimator,实现类似游戏中的诸多动画效果
- 动画不是那种天花乱坠式,而是在一些细节之处也添加动画,例如在view之间的约束,在设备旋转过程中调整对应动画,viewController之间的切换也是可以自定义transitioning
UIView Animations
UIView可以设置动画的属性:
属性 | 改变 |
---|---|
frame & bounds & center | 改变View的位置及自身大小 |
transform | 改变缩放scale,旋转角度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属性可以调整动画的弹簧效果
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创建动画的首要条件就是,你要先用了AutoLayout
动画的创建方式也类似
//对已有constant作出对应修改操作 or 创建新的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的强大之处是可以设置更多的属性,实现3D效果,添加mask,添加shadow,并且这一切都是建立在直接调用GPU来达到快速相应,一切都非常简便,其实每个View都有Layer,我们在Layer层面做的修改也可以展现在View上
属性 | 作用 |
---|---|
size & position | 改变layer的位置及自身大小 |
transform | 改2D,3D情况下的现实效果 |
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来实现特定效果
ownStyle.beginTime = CACurrentMediaTime() + 0.3
ownStyle.fillMode = KCAFillModeRemoved //default
//kCAFillMode主要作用就是控制你动画在开始和结束时候的一些效果
//进阶 CAAnimation delegate pattern
func animationDidStop & animationDidStart
//与block中的相类似,你也可以利用KVC特性设置相应内容
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作用,标示动画,在恰当的时候移除对应动画,而不是移除动画效果本身
//高阶,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)
//这么只能一起做动画,这里内部没有时间顺序
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))
有木有发现现在很多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在底
如果你要实现这个imageView如在撞击墙壁时,mask产生变化,变成方形,这样一个碰撞式的动画还是很带感的
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效果,这里要扯点美术内容了,透视法
目前的屏幕还是2D平面,也无法实现所谓全息影像
那么在2D屏幕下的3D就需要透过视觉差来投影出来3D效果
当相机距离很小时候拉伸效果明显,后者距离很大
var identity = CATransform3DIdentity
//m34啥意思,简单说来就是你看屏幕视角的远近,这里是指近大远小,-1.0/1000啥概念,这里1000是指相机距离,距离越小,近大远效果越明显
identity.m34 = -1.0/1000
//percent在这里是指你在移动当前View时候frame.orgin.x与一固定值如width之间的比例,这样view的移动就产生相应不同程度的过渡效果
let remainingPercent = 1.0 - percent
let angle = remainingPercent * CGFloat(-M_PI_2)
//这里是沿着y轴旋转,这里不得不说一下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)
左边是左手坐标系,右边是我们惯用的右手坐标系,两者区别在于z轴的方向,iOS是左手系
view的转动效果如下,x,y,z轴你可以设置根据相应的轴产生不同动画以及组合动画
致此,iOS的动画初窥到此为止,发现具体到动画中矩阵转换还是有点搞不清楚,后续还会有更多关于iOS Animation的内容