iOS核心動畫高級技巧之圖層變換和專用圖層(二)
iOS核心動畫高級技巧之核心動畫(三)
iOS核心動畫高級技巧之性能(四)
iOS核心動畫高級技巧之動畫總結(五)
仿射變換
iOS仿射變換是CGAffineTransform,仿射變換的特點是變換后的圖形對邊依然是平行的,它包括 CGAffineTransformMakeRotation(CGFloat angle) / CGAffineTransformMakeScale(CGFloat sx, CGFloat sy) / CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty),直接看代碼
1 blueView = UIView() 2 blueView.frame = CGRectMake(50, 100, 100, 100) 3 blueView.backgroundColor = UIColor.blueColor() 4 self.view.addSubview(blueView) 5 6 yellowView = UIView() 7 yellowView.frame = CGRectMake(50 + 200 / 1.414, 100, 100, 100)//注意yellowView的水平位置,和blueView作對比 8 yellowView.backgroundColor = UIColor.yellowColor() 9 self.view.addSubview(yellowView)10 11 blueView.transform = CGAffineTransformIdentity //初始化transform12 blueView.transform = CGAffineTransformMakeScale(0.5, 0.5 ) //縮小0.5倍13 blueView.transform = CGAffineTransformRotate(blueView.transform, CGFloat(M_PI_4)) //旋轉pi/414 blueView.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軸的旋轉都不會使它的方向改變,這是由于矩陣的值表示的范圍廣,不會互相影響.
1 blueLayer.transform = CATransform3DIdentity //初始化transform2 blueLayer.transform = CATransform3DMakeScale(0.5, 0.5, 0) //縮小0.5倍3 blueLayer.transform = CATransform3DRotate(blueLayer.transform,CGFloat( M_PI_4 ), 0.0, 1.0, 0) //旋轉pi/44 blueLayer.transform = CATransform3DTranslate(blueLayer.transform,400, 0, 0) //平移400
由于3D變換分成多個方向的,所以沿X或Y方向的旋轉就像縮放了一樣,可以通過設置向量對應的值來修復這個效果,讓它看上去更真實一點,結果它會有一個傾斜角度,有一個投影效果,這樣當你做平移的時候會改變顯示的大小,所以一般不要一起用了,而要讓這個效果有效,需要在初始化transform時就設置m34的值(這個值最好在-1/500.0 到 -1/5000.0之間),放到后面沒有效果,在后面也不能用scale放大縮小,不然會覆蓋這個值.所以這個效果基本是單獨拿出來顯示效果的.
1 var transform = CATransform3DIdentity //初始化transform2 transform.m34 = -1 / 500.0 //設置m34的值3 // transform = CATransform3DScale( transform, 0.5, 0.5, 0) //縮小0.5倍,這里不能用4 transform = CATransform3DRotate(transform,CGFloat( M_PI_4 ), 0.0, 1.0, 0) //旋轉pi/45 transform = CATransform3DTranslate(transform,400, 0, 0) //平移4006 blueLayer.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:
1 let shapePath = UIBezierPath() 2 shapePath.moveToPoint(CGPointMake(175, 100)) 3 shapePath.addArcWithCenter(CGPointMake(150, 100), radius: 25, startAngle: 0, endAngle:CGFloat( 2 * M_PI ), clockwise: true) 4 shapePath.moveToPoint(CGPointMake(150, 125)) 5 shapePath.addLineToPoint(CGPointMake(150, 175)) 6 shapePath.addLineToPoint(CGPointMake(125, 225)) 7 shapePath.moveToPoint(CGPointMake(150, 175)) 8 shapePath.addLineToPoint(CGPointMake(175, 225)) 9 shapePath.moveToPoint(CGPointMake(100, 150))10 shapePath.addLineToPoint(CGPointMake(200, 150))11 12 let shapeLayer = CAShapeLayer()13 shapeLayer.strokeColor = UIColor.redColor().CGColor14 shapeLayer.fillColor = UIColor.clearColor().CGColor15 shapeLayer.lineWidth = 516 shapeLayer.lineJoin = kCALineJoinRound17 shapeLayer.lineCap = kCALineCaPRound18 shapeLayer.path = shapePath.CGPath19 self.view.layer.addSublayer(shapeLayer)
DEMO2:
1 var rect = CGRectMake(50, 50, 100, 100);2 var radii = CGSizeMake(20, 20);3 var corners = UIRectCorner.TopRight | UIRectCorner.BottomRight | UIRectCorner.BottomLeft4 var shapePath = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: radii)
CATextLayer
可以在layer中直接用Core Graphics直接寫入文字,這就是UILabel的實現方式,如果直接在圖層上記錄文本將是一件很麻煩的事,iOS提供了CATextLayer來實現layer上顯示文本,它本身使用了Core text ,渲染速度很快.
1 var textLayer = CATextLayer() 2 textLayer.frame = CGRectMake(100, 100, 200, 300) 3 self.view.layer.addSublayer(textLayer) 4 5 //設置它的屬性 6 textLayer.foregroundColor = UIColor.redColor().CGColor 7 textLayer.alignmentMode = kCAAlignmentJustified 8 textLayer.wrapped = true 9 10 //它的font是CGFont類型,需要大小字體分開設置11 var font = UIFont.systemFontOfSize(24)12 textLayer.font = CGFontCreateWithFontName(font.fontName)13 textLayer.fontSize = font.pointSize14 15 var textStr = "hello kitty hi nohhhh no wo shi lllll aaaaawo jiojhello kitty hi nohhhh no wo shi lllll aaaaawo jioj"16 textLayer.string = textStr17 18 //它contentScale默認是1,為了讓它以retina的質量來顯示,設置為219 textLayer.contentsScale = UIScreen.mainScreen().scale
這里也可以用NSAttributedString富文本,但是由于swift類型轉換的問題挺麻煩的,不寫demo了.
CATransformLayer
這個圖層類解決了圖層間的層級關系,如下面這個demo所示:如果是CALayer旋轉就沒有層級,如果是CATransformLayer,它就有了層級
1 //outLayer = CALayer() 2 outLayer = CATransformLayer() //如果是上面這個普通的CALayer它如果旋轉就不會有雙層的效果 3 4 5 outLayer.frame = CGRectMake(50, 50, 300, 300) 6 outLayer.backgroundColor = UIColor.grayColor().CGColor 7 self.view.layer.addSublayer(outLayer) 8 9 var transform = CATransform3DIdentity //初始化transform10 transform.m34 = -1 / 500.0 //設置m34的值11 transform = CATransform3DRotate(transform,CGFloat( M_PI_2 ), 0.0, 1.0, 0) //旋轉pi/412 outLayer.transform = transform13 14 //outLayer.sublayerTransform = transform15 16 17 blueLayer = CALayer()18 blueLayer.frame = CGRectMake(50, 50, 200, 200)19 blueLayer.backgroundColor = UIColor.blueColor().CGColor20 outLayer.addSublayer(blueLayer)21 22 23 var blueLayerTransform = CATransform3DIdentity;24 blueLayerTransform = CATransform3DTranslate(blueLayerTransform, 0, 0, 50);25 blueLayer.transform = blueLayerTransform;26 27 redLayer = CALayer()28 redLayer.frame = CGRectMake(50, 50, 200, 200)29 redLayer.backgroundColor = UIColor.redColor().CGColor30 outLayer.addSublayer(redLayer)31 32 var fromValue = CATransform3DIdentity33 fromValue.m34 = -1 / 500.034 fromValue = CATransform3DRotate(fromValue, 0, 0, 1, 0)35 36 var toValue = CATransform3DIdentity37 toValue.m34 = -1 / 500.038 toValue = CATransform3DRotate(toValue, CGFloat( M_PI ), 0, 1, 0)39 40 var basicAnimation = CABasicAnimation(keyPath: "transform")41 basicAnimation.duration = 1.042 basicAnimation.fromValue = NSValue(CATransform3D:fromValue)43 basicAnimation.toValue = NSValue(CATransform3D:toValue)44 outLayer.transform = toValue45 outLayer.addAnimation(basicAnimation, forKey: "transform3D")
CAGradientLayer
CAGradientLayer它主要是可以設置漸變色,通過colors屬性和,startPoint和endPoint設置過渡顏色和位置,這時候設置背景色是沒有用的
1 gradientLayer = CAGradientLayer()2 gradientLayer.frame = CGRectMake(50, 50, 300, 300)3 gradientLayer.backgroundColor = UIColor.grayColor().CGColor4 self.view.layer.addSublayer(gradientLayer)5 6 gradientLayer.colors = [UIColor.redColor().CGColor,UIColor.blueColor().CGColor, UIColor.greenColor().CGColor]7 gradientLayer.startPoint = CGPointMake(0, 0)8 gradientLayer.endPoint = CGPointMake(1, 1)
它還有一個locations屬性,可以設置每個漸變色的間距,這個數組的長度需要和colors數組的長度相同,location的值是按endPoint的值來說的,不是是多少及時多少
CAReplicatorLayer
CAReplicatorLayer可以使用在需要創建多個同樣的layer只是軌跡顏色有規則的變化的時候,它會把它的子圖層有規律的重復展示出來,而可以設置它重復的次數/每次漸變的顏色遞增遞減值/每次transform變換的路徑.
1 repeatLayer = CAReplicatorLayer() 2 repeatLayer.frame = CGRectMake(50, 50, 300, 300) 3 repeatLayer.backgroundColor = UIColor.grayColor().CGColor 4 self.view.layer.addSublayer(repeatLayer) 5 6 var transform = CATransform3DIdentity 7 transform = CATransform3DTranslate(transform, 0, 100, 0); 8 transform = CATransform3DRotate(transform, CGFloat( M_PI / 5.0 ), 0, 0, 1); 9 transform = CATransform3DTranslate(transform, 0, -100, 0);10 repeatLayer.instanceTransform = transform;11 12 repeatLayer.instanceCount = 1013 14 repeatLayer.instanceBlueOffset = -0.115 repeatLayer.instanceRedOffset = -0.116 17 var layer = CALayer()18 layer.frame = CGRect(x: 125, y: 125, width: 50, height: 50)19 layer.backgroundColor = UIColor.whiteColor().CGColor20 repeatLayer.addSublayer(layer)
它最后的結果就是一圈顏色遞變的正方形,它可以做動畫中一個飛機的路徑等效果.它的一個重要實際用處就是做倒影,因為如果做圖層的復制的話,倒影不可能跟著原layer做實時更新,而CAReplicatorLayer就可以做到.
1 reflectionLayer = CAReplicatorLayer() 2 var img = UIImage(named: "3333.jpg") 3 reflectionLayer.frame = CGRectMake((self.view.bounds.size.width - img!.size.width) / 2 , 100, img!.size.width, img!.size.height * 1.5) 4 reflectionLayer.backgroundColor = UIColor.grayColor().CGColor 5 self.view.layer.addSublayer(reflectionLayer) 6 7 var transform = CATransform3DIdentity 8 transform = CATransform3DScale(transform, 1, -0.5, 1); 9 transform = CATransform3DTranslate(transform, 0, -img!.size.height * 3 / 4 + 2, 0.0);10 reflectionLayer.instanceTransform = transform;11 12 reflectionLayer.instanceCount = 213 14 var imageLayer = CALayer()15 imageLayer.frame = CGRect(x: 0, y: 0, width: img!.size.width, height: img!.size.height)16 imageLayer.backgroundColor = UIColor.whiteColor().CGColor17 imageLayer.contents = img!.CGImage18 //layer.anchorPoint = CGPointMake(0, 0)19 reflectionLayer.addSublayer(imageLayer)20 21 //設置透明度,在外面用個層來設置22 var gradientLayer = CAGradientLayer()23 gradientLayer.colors = [UIColor.whiteColor().colorWithAlphaComponent(0.2).CGColor, UIColor.whiteColor().CGColor]24 gradientLayer.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)25 self.view.layer.addSublayer(gradientLayer)26 27 28 //設置文字29 var textLayer = CATextLayer()30 textLayer.frame = CGRectMake(80, 50, 200, 300)31 self.view.layer.addSublayer(textLayer)32 33 //設置它的屬性34 textLayer.foregroundColor = UIColor.redColor().CGColor35 textLayer.alignmentMode = kCAAlignmentJustified36 textLayer.wrapped = true37 38 //它的font是CGFont類型,需要大小字體分開設置39 var font = UIFont.systemFontOfSize(24)40 textLayer.font = CGFontCreateWithFontName(font.fontName)41 textLayer.fontSize = font.pointSize42 43 var textStr = "What a fuck!"44 textLayer.string = textStr45 46 //它contentScale默認是1,為了讓它以retina的質量來顯示,設置為247 textLayer.contentsScale = UIScreen.mainScreen().scale48 49 imageLayer.addSublayer(textLayer)50 51 52 var basicAnimation = CABasicAnimation(keyPath: "position.y")53 basicAnimation.duration = 2.054 basicAnimation.fromValue = Float( textLayer.position.y )55 var toValue:Float = Float( textLayer.position.y + 100.0)56 basicAnimation.toValue = NSNumber(float: toValue)57 basicAnimation.removedOnCompletion = true58 basicAnimation.fillMode = kCAFillModeForwards59 textLayer.addAnimation(basicAnimation, forKey: nil)60 61 // textLayer.frame.origin.y = CGFloat( toValue)62 textLayer.position = CGPointMake(textLayer.position.x, textLayer.position.y + 100.0)
CAScrollLayer
CAScrollLayer和UIScrollView類似,它和CALayer相比多了個scrollPoint的方法,如果要用這個方法可以使用CAScrollLayer
1 NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: NSSelectorFromString("setPosition"), userInfo: nil, repeats: false)2 3 func setPosition () {4 self.scrollLayer.scrollToPoint(CGPointMake(0, 100))5 }
CATiledLayer
CATiledLayer的可以做到圖片/PDF等的分割顯示,如果一個一張圖片分辨率超過2048*2048(因平臺不同),超過了OpenGL最大紋理尺寸,所以會有性能問題,一個PDF一般也比較大,所以容易出現性能問題(后面會單獨寫一個demo)
CAEmitterLayer
CAEmitterLayer能模仿粒子反射,如果火焰等,能控制粒子顏色/放射的速率,透明度,方向,個數等.
1 emitterLayer = CAEmitterLayer() 2 emitterLayer.frame = CGRectMake(100 , 100, 200, 200) 3 emitterLayer.backgroundColor = UIColor.grayColor().CGColor 4 self.view.layer.addSublayer(emitterLayer) 5 6 emitterLayer.renderMode = kCAEmitterLayerAdditive 7 emitterLayer.emitterPosition = CGPointMake(emitterLayer.frame.size.width / 2, emitterLayer.frame.size.height / 2) 8 9 var cell = CAEmitterCell()10 cell.contents = UIImage(named: "lizi.png")?.CGImage11 cell.birthRate = 10 //粒子出現的速率12 cell.lifetime = 4.0 //聲明周期,秒13 cell.emissionRange = 2 //發射的方向14 15 cell.color = UIColor(red: 1, green: 1, blue: 0.5, alpha: 1).CGColor //粒子的顏色16 cell.alphaspeed = -0.4 //透明度改變速率17 cell.velocity = 50//粒子運動速度18 cell.velocityRange = 100//粒子速度范圍,約束速度19 20 emitterLayer.emitterCells = [cell]
CAEAGLLayer
它是一個提供用OpenGL ES來繪圖的layer,它可以預先假設要繪制的類型,快速繪制,它可以配合GLKit中的CLKView使用,具體demo專門寫.
AVFoundation
它是foundation框架里的,但是和layer的使用是一致的,demo如下
1 var urlStr = NSBundle.mainBundle().pathForResource("1.mp4", ofType: nil) 2 var url = NSURL(fileURLWithPath: urlStr!) 3 var player = AVPlayer(URL: url) 4 5 var playLayer = AVPlayerLayer(player: player) 6 playLayer.frame = CGRectMake(0, 0,400, 300) 7 playLayer.backgroundColor = UIColor.grayColor().CGColor 8 self.view.layer.addSublayer(playLayer) 9 10 player.play()
新聞熱點
疑難解答