iOS核心动画高级技巧之CALayer(一)
iOS核心动画高级技巧之图层变换和专用图层(二)
iOS核心动画高级技巧之核心动画(三)
iOS核心动画高级技巧之性能(四)
iOS核心动画高级技巧之动画总结(五)
仿射变换
iOS仿射变换是CGAffineTransform,仿射变换的特点是变换后的图形对边依然是平行的,它包括 CGAffineTransformMakeRotation(CGFloat angle) / CGAffineTransformMakeScale(CGFloat sx, CGFloat sy) / CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty),直接看代码
1blueView =UIView()2blueView.frame = CGRectMake(50,100,100,100)3blueView.backgroundColor =UIColor.blueColor()4self.view.addSubview(blueView)56yellowView =UIView()7yellowView.frame = CGRectMake(50+200/1.414,100,100,100)//注意yellowView的水平位置,和blueView作对比8yellowView.backgroundColor =UIColor.yellowColor()9self.view.addSubview(yellowView)1011blueView.transform = CGAffineTransformIdentity//初始化transform12blueView.transform = CGAffineTransformMakeScale(0.5,0.5)//缩小0.5倍13blueView.transform = CGAffineTransformRotate(blueView.transform, CGFloat(M_PI_4))//旋转pi/414blueView.transform = CGAffineTransformTranslate(blueView.transform,400,0)//平移400
这里只需要注意transform的叠加互相之间是有影响的,blueView缩小了0.5倍和旋转了45°,所以本来应该在水平位置上平移400的最后在45°方向平移了200,所以在水平方向平移的距离大约是200/1.414,和yellowView形成对比.
3D变换
3D变换和仿射变换不同,虽然它也是乘以一个二维向量,但是它是4x4的二维向量,而仿射变换是2x3的向量,而且3D变换可以通过直接改变向量的单个的值来设置向量,比如transform.m11 / transform.m44 对应的就是向量上对应位置的值,由于是3维空间的变换,所以有些不同,仿射的旋转等价于3D变换的Z轴旋转,它的x,y的组合向量可以使它向倾斜的角度(如45°)旋转,而平移忽略掉Z轴上的就是一样的,缩放也是忽略掉Z轴上的就行.而由于是3D变换缩放会使平移缩放对应比例,而x/y/z轴的旋转都不会使它的方向改变,这是由于矩阵的值表示的范围广,不会互相影响.
1blueLayer.transform = CATransform3DIdentity//初始化transform2blueLayer.transform = CATransform3DMakeScale(0.5,0.5,0)//缩小0.5倍3blueLayer.transform = CATransform3DRotate(blueLayer.transform,CGFloat( M_PI_4 ),0.0,1.0,0)//旋转pi/44blueLayer.transform = CATransform3DTranslate(blueLayer.transform,400,0,0)//平移400
由于3D变换分成多个方向的,所以沿X或Y方向的旋转就像缩放了一样,可以通过设置向量对应的值来修复这个效果,让它看上去更真实一点,结果它会有一个倾斜角度,有一个投影效果,这样当你做平移的时候会改变显示的大小,所以一般不要一起用了,而要让这个效果有效,需要在初始化transform时就设置m34的值(这个值最好在-1/500.0 到 -1/5000.0之间),放到后面没有效果,在后面也不能用scale放大缩小,不然会覆盖这个值.所以这个效果基本是单独拿出来显示效果的.
1var transform = CATransform3DIdentity//初始化transform2transform.m34 = -1/500.0//设置m34的值3//transform = CATransform3DScale( transform, 0.5, 0.5, 0)//缩小0.5倍,这里不能用4transform = CATransform3DRotate(transform,CGFloat( M_PI_4 ),0.0,1.0,0)//旋转pi/45transform = CATransform3DTranslate(transform,400,0,0)//平移4006blueLayer.transform = transform
当你在投射的方向平移足够大的距离,它最后的成像就是一个点了,这个点一般是图层的anchorPoint点,如果是一个图层的好几个子图层都需要这种效果,那么直接设置它的父图层transform的m34为-1/ 500.0 然后设置它的sublayerTransform属性,当设置它的子layer的rotote属性那个子layer就会有对应的投射效果. 如:outLayer.sublayerTransform = outLayer.transform
如果旋转180°从背面去看layer,layer会显示出和layer对称的的图片,因为layer是双面绘制的,但如果有文字就会看起来很混乱并且浪费GPU资源,所以最好能够禁用它,而layer提供了这个属性:doubleSided,值为true双面为false则为单面.
内外层layer的选择可以相互叠加和抵消,这个在Z轴上的旋转是可以的,但是在X/Y上并不是这样的,是由于它们并不处于同一个3D空间内,同时需要注意的是,如果使用m34这个特殊的向量值做投影,每次先给一个transform初始化为CATransform3DIdentity,然后直接设置m34的值然后做旋转等,再赋值给layer的transform
专用图层
CAShapeLayer
CAShapeLayer 是专门用来绘画形状的layer,它对比画图有很大的优势,它使用了硬件加速,所以绘图速度很快,它不需要寄宿图片所以不会使用太多内存,它也不会和普通图层一样被裁剪掉,而且它不会被像素化,最后就不会模糊.你可以设置lineWith(线宽,用点表示单位)/ lineCap(线条结尾的样子)和lineJoin(线条之间的结合点的样子),但是只有一次设置的机会.CAShapeLayer还可以单独设置圆角,下面是代码和对应的两个运行效果,前面一个线条的人另一个是3个圆角的直角矩形.
DEMO1:
1let shapePath =UIBezierPath()2shapePath.moveToPoint(CGPointMake(175,100))3shapePath.addArcWithCenter(CGPointMake(150,100), radius:25, startAngle:0, endAngle:CGFloat(2* M_PI ), clockwise:true)4shapePath.moveToPoint(CGPointMake(150,125))5shapePath.addLineToPoint(CGPointMake(150,175))6shapePath.addLineToPoint(CGPointMake(125,225))7shapePath.moveToPoint(CGPointMake(150,175))8shapePath.addLineToPoint(CGPointMake(175,225))9shapePath.moveToPoint(CGPointMake(100,150))10shapePath.addLineToPoint(CGPointMake(200,150))1112let shapeLayer =CAShapeLayer()13shapeLayer.strokeColor =UIColor.redColor().CGColor14shapeLayer.fillColor =UIColor.clearColor().CGColor15shapeLayer.lineWidth =516shapeLayer.lineJoin =kCALineJoinRound17shapeLayer.lineCap =kCALineCaPRound18shapeLayer.path =shapePath.CGPath19self.view.layer.addSublayer(shapeLayer)
DEMO2:
1var rect = CGRectMake(50,50,100,100);2var radii = CGSizeMake(20,20);3var corners = UIRectCorner.TopRight | UIRectCorner.BottomRight |UIRectCorner.BottomLeft4var shapePath = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: radii)
CATextLayer
可以在layer中直接用Core Graphics直接写入文字,这就是UILabel的实现方式,如果直接在图层上记录文本将是一件很麻烦的事,iOS提供了CATextLayer来实现layer上显示文本,它本身使用了Core text ,渲染速度很快.
1var textLayer =CATextLayer()2textLayer.frame = CGRectMake(100,100,200,300)3self.view.layer.addSublayer(textLayer)45//设置它的属性6textLayer.foregroundColor =UIColor.redColor().CGColor7textLayer.alignmentMode =kCAAlignmentJustified8textLayer.wrapped =true910//它的font是CGFont类型,需要大小字体分开设置11var font = UIFont.systemFontOfSize(24)12textLayer.font =CGFontCreateWithFontName(font.fontName)13textLayer.fontSize =font.pointSize1415var textStr ="hello kitty hi nohhhh no wo shi lllll aaaaawo jiojhello kitty hi nohhhh no wo shi lllll aaaaawo jioj"16textLayer.string=textStr1718//它contentScale默认是1,为了让它以retina的质量来显示,设置为219textLayer.contentsScale = UIScreen.mainScreen().scale
这里也可以用NSAttributedString富文本,但是由于swift类型转换的问题挺麻烦的,不写demo了.
CATransformLayer
这个图层类解决了图层间的层级关系,如下面这个demo所示:如果是CALayer旋转就没有层级,如果是CATransformLayer,它就有了层级
1//outLayer = CALayer()2outLayer = CATransformLayer()//如果是上面这个普通的CALayer它如果旋转就不会有双层的效果345outLayer.frame = CGRectMake(50,50,300,300)6outLayer.backgroundColor =UIColor.grayColor().CGColor7self.view.layer.addSublayer(outLayer)89var transform = CATransform3DIdentity//初始化transform10transform.m34 = -1/500.0//设置m34的值11transform = CATransform3DRotate(transform,CGFloat( M_PI_2 ),0.0,1.0,0)//旋转pi/412outLayer.transform =transform1314//outLayer.sublayerTransform = transform151617blueLayer =CALayer()18blueLayer.frame = CGRectMake(50,50,200,200)19blueLayer.backgroundColor =UIColor.blueColor().CGColor20outLayer.addSublayer(blueLayer)212223var blueLayerTransform =CATransform3DIdentity;24blueLayerTransform = CATransform3DTranslate(blueLayerTransform,0,0,50);25blueLayer.transform =blueLayerTransform;2627redLayer =CALayer()28redLayer.frame = CGRectMake(50,50,200,200)29redLayer.backgroundColor =UIColor.redColor().CGColor30outLayer.addSublayer(redLayer)3132var fromValue =CATransform3DIdentity33fromValue.m34 = -1/500.034fromValue = CATransform3DRotate(fromValue,0,0,1,0)3536var toValue =CATransform3DIdentity37toValue.m34 = -1/500.038toValue = CATransform3DRotate(toValue, CGFloat( M_PI ),0,1,0)3940var basicAnimation = CABasicAnimation(keyPath:"transform")41basicAnimation.duration =1.042basicAnimation.fromValue =NSValue(CATransform3D:fromValue)43basicAnimation.toValue =NSValue(CATransform3D:toValue)44outLayer.transform =toValue45outLayer.addAnimation(basicAnimation, forKey:"transform3D")
View Code
CAGradientLayer
CAGradientLayer它主要是可以设置渐变色,通过colors属性和,startPoint和endPoint设置过渡颜色和位置,这时候设置背景色是没有用的
1gradientLayer =CAGradientLayer()2gradientLayer.frame = CGRectMake(50,50,300,300)3gradientLayer.backgroundColor =UIColor.grayColor().CGColor4self.view.layer.addSublayer(gradientLayer)56gradientLayer.colors =[UIColor.redColor().CGColor,UIColor.blueColor().CGColor, UIColor.greenColor().CGColor]7gradientLayer.startPoint = CGPointMake(0,0)8gradientLayer.endPoint = CGPointMake(1,1)
它还有一个locations属性,可以设置每个渐变色的间距,这个数组的长度需要和colors数组的长度相同,location的值是按endPoint的值来说的,不是是多少及时多少
CAReplicatorLayer
CAReplicatorLayer可以使用在需要创建多个同样的layer只是轨迹颜色有规则的变化的时候,它会把它的子图层有规律的重复展示出来,而可以设置它重复的次数/每次渐变的颜色递增递减值/每次transform变换的路径.
1repeatLayer =CAReplicatorLayer()2repeatLayer.frame = CGRectMake(50,50,300,300)3repeatLayer.backgroundColor =UIColor.grayColor().CGColor4self.view.layer.addSublayer(repeatLayer)56var transform =CATransform3DIdentity7transform = CATransform3DTranslate(transform,0,100,0);8transform = CATransform3DRotate(transform, CGFloat( M_PI /5.0),0,0,1);9transform = CATransform3DTranslate(transform,0, -100,0);10repeatLayer.instanceTransform =transform;1112repeatLayer.instanceCount =101314repeatLayer.instanceBlueOffset = -0.115repeatLayer.instanceRedOffset = -0.11617var layer =CALayer()18layer.frame = CGRect(x:125, y:125, width:50, height:50)19layer.backgroundColor =UIColor.whiteColor().CGColor20repeatLayer.addSublayer(layer)
它最后的结果就是一圈颜色递变的正方形,它可以做动画中一个飞机的路径等效果.它的一个重要实际用处就是做倒影,因为如果做图层的复制的话,倒影不可能跟着原layer做实时更新,而CAReplicatorLayer就可以做到.
1reflectionLayer =CAReplicatorLayer()2var img = UIImage(named:"3333.jpg")3reflectionLayer.frame = CGRectMake((self.view.bounds.size.width - img!.size.width) /2,100, img!.size.width, img!.size.height *1.5)4reflectionLayer.backgroundColor =UIColor.grayColor().CGColor5self.view.layer.addSublayer(reflectionLayer)67var transform =CATransform3DIdentity8transform = CATransform3DScale(transform,1, -0.5,1);9transform = CATransform3DTranslate(transform,0, -img!.size.height *3/4+2,0.0);10reflectionLayer.instanceTransform =transform;1112reflectionLayer.instanceCount =21314var imageLayer =CALayer()15imageLayer.frame = CGRect(x:0, y:0, width: img!.size.width, height: img!.size.height)16imageLayer.backgroundColor =UIColor.whiteColor().CGColor17imageLayer.contents = img!.CGImage18//layer.anchorPoint = CGPointMake(0, 0)19reflectionLayer.addSublayer(imageLayer)2021//设置透明度,在外面用个层来设置22var gradientLayer =CAGradientLayer()23gradientLayer.colors = [UIColor.whiteColor().colorWithAlphaComponent(0.2).CGColor, UIColor.whiteColor().CGColor]24gradientLayer.frame = CGRectMake(reflectionLayer.frame.origin.x , reflectionLayer.frame.origin.y + reflectionLayer.frame.size.height /3.0*2, reflectionLayer.frame.size.width, reflectionLayer.frame.size.height /2)25self.view.layer.addSublayer(gradientLayer)262728//设置文字29var textLayer =CATextLayer()30textLayer.frame = CGRectMake(80,50,200,300)31self.view.layer.addSublayer(textLayer)3233//设置它的属性34textLayer.foregroundColor =UIColor.redColor().CGColor35textLayer.alignmentMode =kCAAlignmentJustified36textLayer.wrapped =true3738//它的font是CGFont类型,需要大小字体分开设置39var font = UIFont.systemFontOfSize(24)40textLayer.font =CGFontCreateWithFontName(font.fontName)41textLayer.fontSize =font.pointSize4243var textStr ="What a fuck!"44textLayer.string=textStr4546//它contentScale默认是1,为了让它以retina的质量来显示,设置为247textLayer.contentsScale =UIScreen.mainScreen().scale4849imageLayer.addSublayer(textLayer)505152var basicAnimation = CABasicAnimation(keyPath:"position.y")53basicAnimation.duration =2.054basicAnimation.fromValue =Float( textLayer.position.y )55var toValue:Float = Float( textLayer.position.y +100.0)56basicAnimation.toValue = NSNumber(float: toValue)57basicAnimation.removedOnCompletion =true58basicAnimation.fillMode =kCAFillModeForwards59textLayer.addAnimation(basicAnimation, forKey: nil)6061//textLayer.frame.origin.y = CGFloat( toValue)62textLayer.position = CGPointMake(textLayer.position.x, textLayer.position.y +100.0)
View Code
CAScrollLayer
CAScrollLayer和UIScrollView类似,它和CALayer相比多了个scrollPoint的方法,如果要用这个方法可以使用CAScrollLayer
1NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: NSSelectorFromString("setPosition"), userInfo: nil, repeats:false)23func setPosition () {4self.scrollLayer.scrollToPoint(CGPointMake(0,100))5}
CATiledLayer
CATiledLayer的可以做到图片/PDF等的分割显示,如果一个一张图片分辨率超过2048*2048(因平台不同),超过了OpenGL最大纹理尺寸,所以会有性能问题,一个PDF一般也比较大,所以容易出现性能问题(后面会单独写一个demo)
CAEmitterLayer
CAEmitterLayer能模仿粒子反射,如果火焰等,能控制粒子颜色/放射的速率,透明度,方向,个数等.
1emitterLayer =CAEmitterLayer()2emitterLayer.frame = CGRectMake(100,100,200,200)3emitterLayer.backgroundColor =UIColor.grayColor().CGColor4self.view.layer.addSublayer(emitterLayer)56emitterLayer.renderMode =kCAEmitterLayerAdditive7emitterLayer.emitterPosition = CGPointMake(emitterLayer.frame.size.width /2, emitterLayer.frame.size.height /2)89var cell =CAEmitterCell()10cell.contents = UIImage(named:"lizi.png")?.CGImage11cell.birthRate =10//粒子出现的速率12cell.lifetime =4.0//声明周期,秒13cell.emissionRange =2//发射的方向1415cell.color = UIColor(red:1, green:1, blue:0.5, alpha:1).CGColor//粒子的颜色16cell.alphaspeed = -0.4//透明度改变速率17cell.velocity =50//粒子运动速度18cell.velocityRange =100//粒子速度范围,约束速度1920emitterLayer.emitterCells = [cell]
CAEAGLLayer
它是一个提供用OpenGL ES来绘图的layer,它可以预先假设要绘制的类型,快速绘制,它可以配合GLKit中的CLKView使用,具体demo专门写.
AVFoundation
它是foundation框架里的,但是和layer的使用是一致的,demo如下
1var urlStr = NSBundle.mainBundle().pathForResource("1.mp4", ofType: nil)2var url = NSURL(fileURLWithPath: urlStr!)3var player =AVPlayer(URL: url)45var playLayer =AVPlayerLayer(player: player)6playLayer.frame = CGRectMake(0,0,400,300)7playLayer.backgroundColor =UIColor.grayColor().CGColor8self.view.layer.addSublayer(playLayer)910player.play()