CALayer入門教程


本文淘汰.最新文章http://www.reibang.com/p/b64136e6fc79


我這篇譯文為完全翻譯,原作者的10個例子我都翻完了.


CALayer Tutorial: Getting Started

大家都清楚,iOS應(yīng)用里能看到的東西都是view,而實際上view的顯示功能是由另一個類CALayer來實現(xiàn)的.這篇文章講了10個展示CALyer酷炫效果的例子.學(xué)習(xí)這個教程的前提是你有iOS開發(fā)的基礎(chǔ)能力,懂swift,用storyboard布置UI.

Getting Started

最快明白layer的方法就是實戰(zhàn).廢話不多說,我們建個項目玩一玩layer.
1.啟動Xcode(廢話)
2.菜單里選擇 File\New\Project….如果是剛啟動出現(xiàn)在開始界面的話就選擇新建一個新項目
3.在對話欄選擇 iOS\Application\Single View Application
4.點next,在Product Name輸入"CALayerPlayground",organization name 和 identifier 隨你來
5.Language選擇swift,Devices選Universal
6.保證Core Data的勾沒有被打上,選next
7.選個你喜歡的路徑放你的項目,然后creat
好,項目創(chuàng)建OK了,接下來要有view
8.在項目導(dǎo)航器里選Main.storyboard
9.把Object LibraryAssistant Editor顯示出來,如果已經(jīng)顯示了就跳過這一步
10.選擇Editor\Canvas\Show Bounds Rectangles,好讓你看到view的邊框線
11.從Object Library拖一個view到viewcontroller的scene里,把x和y設(shè)為150,寬高設(shè)為300
12.選中這個view,給這個view添加Horizontal Center in ContainerVertical Center in Container這兩個約束,在Xcode底部的align按鈕里操作.
13.同樣點擊底部的pin按鈕,把寬高設(shè)為300
14.最后通過control加拖view的方式把view連接到ViewController.swift文件中,outlet起名為viewForlayer
好了,Xcode現(xiàn)在應(yīng)該是醬紫的

ViewController.swift文件中的內(nèi)容替換為如下內(nèi)容

import UIKit         
class ViewController: UIViewController {  

  @IBOutlet weak var viewForLayer: UIView!  

  var l: CALayer { 
    return viewForLayer.layer
  }  
  override func viewDidLoad() { 
    super.viewDidLoad() 
    setUpLayer() 
  } 
  func setUpLayer() { 
  l.backgroundColor = UIColor.blueColor().CGColor
  l.borderWidth = 100.0 
  l.borderColor =UIColor.redColor().CGColor
  l.shadowOpacity = 0.7 
  l.shadowRadius = 10.0
  } 
}

前面說的每個view都有一個layer,你能用yourView.layer來訪問layer.這段代碼做的第一件事就是創(chuàng)建了一個可以用來訪問viewForLayer的layer的屬性,這個屬性名稱是小寫l,緊接著的代碼就用到了這個屬性的內(nèi)容.
這段代碼還調(diào)用了setUpLayer方法來設(shè)置layer的幾個屬性,比如陰影,背景色,還有線框色.等下你就會明白setUpLayer()方法.不過現(xiàn)在還是運行一下程序來看看你的自定義layer.
[圖片上傳失敗...(image-8d1b48-1587374045880)]
才幾行代碼就有折么酷炫的效果.每一個view都可以做這樣的事,只要它有l(wèi)ayer.接下來仔細(xì)看看.

CALayer的基礎(chǔ)屬性

CALayer有幾個可以改變它外觀的屬性,回想一下你做過哪些
1.把layer的背景色從默認(rèn)的沒有顏色改成藍(lán)的
2.把它的線寬從默認(rèn)的0改成了100
3.把它的顏色從黑色改成了紅色
4.最后還是改了陰影,把不透明度從默認(rèn)的0改到了0.7,這樣就顯示了陰影.然后又把陰影半徑從默認(rèn)的3改到了10.
你能設(shè)置CALayer的幾個屬性.來,多試兩個.把下面兩行代碼加到setUpLayer()方法的底部:

l.contents = UIImage(named: "star")?.CGImage
l.contentsGravity = kCAGravityCenter

content屬性能讓你給layer的內(nèi)容物設(shè)置一張圖片,所以你是放了一張名稱是star的圖片在這里.當(dāng)然,首先你得把圖片放到項目里.[圖片上傳失敗...(image-a05a0c-1587374045880)]
把它放到項目,運行看一下效果
[圖片上傳失敗...(image-1e6b7a-1587374045880)]
看看圖片為什么是居中的,因為把contentsGravity屬性設(shè)置為kCAGravityCenter了.你沒想錯,你也可以設(shè)為居上下左右,左上左下等等.

改變Layer的外觀

我們來玩一下,加幾個手勢識別器來控制layer的外觀.在Xcode里,拖一個點擊手勢識別器到viewForLayer對象上.參考一下,下面的圖片是拖手勢識別器到對象上看起來的樣子:
[圖片上傳失敗...(image-e60a52-1587374045880)]

提示:如果對手勢識別器不熟悉,可以看下這篇文章Using UIGestureRecognizer with Swift.

重復(fù)上面的步驟,再加一個縮放手勢.

通過control加拖拽的方式把storyboard頂部的兩個手勢識別器拖到ViewController.swift里去,把它們加到setUpLayer()方法下面.
[圖片上傳失敗...(image-d92efa-1587374045880)]

把tapGestureRecognized(_:)改成這樣:

@IBAction func tapGestureRecognized(sender: 
UITapGestureRecognizer) { 
l.shadowOpacity = l.shadowOpacity == 0.7 ? 0.0 : 0.7  
}

這段代碼的意思是讓layer根據(jù)點擊手勢來讓它的不透明度在0和0.7之間變動.
當(dāng)然你也可以重寫hitTest(_:)方法達(dá)到同樣的效果,等下我們就會用到這個方法.我們來理解一下上面方法的邏輯,layer能做的只有hit testing,它與手勢識別互動不了.所以我們是把手勢識別器加到view上面.

現(xiàn)在來把pinchGestureRecognized(_:)改成這樣:

@IBAction func pinchGestureRecognized(sender: 
UIPinchGestureRecognizer) {
 let offset: CGFloat = sender.scale < 1 ? 5.0 : -5.0 
 let oldFrame = l.frame
 let oldOrigin = oldFrame.origin 
 let newOrigin = CGPoint(x: oldOrigin.x + offset, y: oldOrigin.y + offset) 
 let newSize = CGSize(width: oldFrame.width + (offset * -2.0), height:oldFrame.height + (offset * -2.0))    
 let newFrame = CGRect(origin: newOrigin, size: newSize) 
 if newFrame.width >= 100.0 && newFrame.width <= 300.0 { 
 l.borderWidth -= offset 
 l.cornerRadius += (offset / 2.0) 
 l.frame = newFrame }
}

這段代碼根據(jù)縮放手勢來生成正的或者負(fù)的位移,調(diào)整layer的frame,邊線的寬度和圓角.
layer的圓角默認(rèn)都是0,也就是個矩形.增加圓角值能生成圓角,當(dāng)圓角值是layer寬度的一半就會變成一個圓了.
設(shè)置圓角不會剪切l(wèi)ayer的內(nèi)容,只有把masksToBounds設(shè)為true才會剪切.
運行看一下吧:
[圖片上傳失敗...(image-108568-1587374045880)]

The Great CALayer Tour

[圖片上傳失敗...(image-6090d9-1587374045880)]

CALayer不是只有你想的幾個屬性和方法,它還有幾個擁有獨一無二的屬性和方法的子類.
有比guided tour更好的了解這些API的途徑嗎,raywenderlich.com-style?
對于接下來的文章,你需要兩樣?xùn)|西:
The Layer Player App
The Layer Player Source Code

這個handy app有十個不同的等下你會學(xué)習(xí)到的CALayer類型的樣例,來看看大概是神馬樣子的:
[圖片上傳失敗...(image-db9625-1587374045880)]

每當(dāng)學(xué)到下面的某個樣例,我建議你玩一下這個CALayer app,隨意地看一下我給你的源碼.不要慌,你不用把所有代碼都碼下來,放輕松慢慢看.
這些樣例應(yīng)該是很不錯的學(xué)習(xí)不同CALayer的途徑,我們希望你能夠喜歡.

Example #1: CALayer

前面已經(jīng)講了怎樣使用CALayer類.
下面是另外幾個沒講到的屬性
****1.layer可以擁有子layer.**** 就像view可以擁有子view.你能拿來做很多酷炫效果.
****2.layer屬性是具有動畫性的.**** 修改layer的屬性能生成隨時間變化的動畫效果.也可以自定義動畫的時間.
****3.layer是輕量級的.**** layer要比view輕量,因此layer能達(dá)到更高效的性能.
****4.layer有很多有用的屬性.**** 你已經(jīng)看了很多了,接下來再看看其他的吧.

前面你已經(jīng)看到了,layer有很多有用的屬性.我們來看下CALayer的所有屬性吧,有些已經(jīng)看到過.相當(dāng)便手哦!

1
let layer = CALayer()
layer.frame = someView.bounds 

2
layer.contents = UIImage(named: "star")?.CGImage
layer.contentsGravity = kCAGravityCenter 

3
layer.magnificationFilter = kCAFilterLinear
layer.geometryFlipped = false

4
layer.backgroundColor = UIColor(red: 11/255.0, green: 86/255.0, blue:14/255.0, alpha: 1.0).CGColor    
layer.opacity = 1.0
layer.hidden = false
layer.masksToBounds = false 

5
layer.cornerRadius = 100.0
layer.borderWidth = 12.0
layer.borderColor = UIColor.whiteColor().CGColor 

6
layer.shadowOpacity = 0.75
layer.shadowOffset = CGSize(width: 0, height: 3)
layer.shadowRadius = 3.0
layer.shouldRasterize = true
someView.layer.addSublayer(layer)

1.創(chuàng)建一個CALayer的實例,把someView的bounds設(shè)給它.
2.居中設(shè)置一張圖片給這個layer.注意.CGImage對象是assigned的.
3.使用contentsGravity會使用這個放大濾鏡,能夠改變所有的size和position.如果geometryFlipped沒有設(shè)為true,坐標(biāo)系和陰影會反轉(zhuǎn).
4.背景色設(shè)為綠色,將layer設(shè)為可見, masksToBounds設(shè)為false,也就是當(dāng)layer的size比contents得size要小時,不會裁剪contents.
5.圓角值設(shè)為寬度的一半,那么視覺上就會成為一個圓.注意,layer的顏色是指定為CGColor類型的.
6.創(chuàng)建了陰影效果,把shouldRasterize開啟為true(下面會講到).(譯者注:原文代碼里并沒有l(wèi)ayer.shouldRasterize = true這句代碼,我加了進(jìn)去)

嗯,來看一下效果:
[圖片上傳失敗...(image-c5cf4a-1587374045880)]

CALayer有兩個提高性能的屬性,分別是 shouldRasterize 和 drawsAsynchronously.

shouldRasterize默認(rèn)為false.設(shè)為true能提高性能的原因是layer的contents只會被渲染一次.這個方法對于那些只是用來在屏幕上做動畫,但是外觀不會變的對象有奇效.
drawsAsynchronously則與shouldRasterize完全相反.它默認(rèn)也是false.如果某個對象需要重復(fù)地被繪制,那么開啟true就能提高性能.比如例子發(fā)射layer,它會持續(xù)不斷地渲染動畫性的細(xì)小的圖像(譯者:可以想象一下雨雪,火焰這樣的東西).(等下CAEmitterLayer會講到.)
提醒一句:使用這兩個屬性時想一下可能的后果.對比一下開合沒開的性能.當(dāng)沒開時,性能可能低得一塌糊涂.

接下來把注意力轉(zhuǎn)移到 Layer Player.它集合了對layer屬性的很多控制:
[圖片上傳失敗...(image-1d1631-1587374045880)]

注意:在CALayerPlayground 項目里你了解到,layer不是響應(yīng)鏈里的一員,所以它對觸摸和手勢不能做出反應(yīng).
但是你可以CATransformLayer的樣例里看到我們可以hit test他們.在CAReplicatorLayer樣例又能看到添加自定義動畫效果

xample #2: CAScrollLayer

CAScrollLayer顯示一個可滾動layer的一部分.CAScrollLayer相當(dāng)?shù)幕A(chǔ),它不能對觸摸做出反應(yīng),甚至查看這個可滾動layer的bounds,所以它只能防止?jié)L動超出范圍.
UIScrollView并沒有使用CAScrollLayer來實現(xiàn)自己的滾動功能,UIScrollView實際上是通過修改layer的bounds來達(dá)到滾動效果的.
對于CAScrollLayer,你能告訴它滾動模式是什么,垂直的或者水平的,或者讓它滾動到某個精確的點或者矩形.

// In ScrollingView.swift
import UIKit 
class ScrollingView: UIView { 

  // 1 
override class func layerClass() -> AnyClass { 
return CAScrollLayer.self
 }
} 
// In CAScrollLayerViewController.swift
import UIKit 
class CAScrollLayerViewController: UIViewController { 
  @IBOutlet weak var scrollingView: ScrollingView!  

// 2 
   var scrollingViewLayer: CAScrollLayer {
   return scrollingView.layer as CAScrollLayer 
 } 
   override func viewDidLoad() 
  { super.viewDidLoad() 

   // 3
   scrollingViewLayer.scrollMode = kCAScrollBoth
  } 
   @IBAction func tapRecognized(sender: 
  UITapGestureRecognizer) { 

     // 4 
  var newPoint = CGPoint(x: 250, y: 250)
   UIView.animateWithDuration(0.3, delay: 0, options: .CurveEaseInOut,           animations: {
   [unowned self] in 
   self.scrollingViewLayer.scrollToPoint(newPoint) }, completion: nil)
 } 
}

上面代碼:
1.寫了一個UIView的子類,重寫了layerClass()方法,用來返回CAScrollLayer.這是個不常規(guī)的用來生成一個新layer并添加為子layer的方法.就像CALayer樣例里那樣.
2.設(shè)置一個屬性,能配合自定義UIView的滾動視圖的layer進(jìn)行高效工作.
3.滾動模式設(shè)為垂直水平都可以.
4.點擊時,生成一個點,這個滾動layer就通過會動畫滾動到這個點的位置.單單scrollToPoint(:)
和scrollToRect(
:)并不會生成動畫

一個ScrollingView持有一張比本身的bounds大的圖片.運行程序,點擊視圖就會有下面一樣的效果:
[圖片上傳失敗...(image-74955d-1587374045880)]

Layer Player有鎖定滾動方向的功能.
下面是是CAScrollLayer的一些使用Tips:
1.當(dāng)你需要一個輕量的只需要程序來控制滾動的東西時,考慮一下CAScrollLayer.
2.如果你想用戶來操控滾動時,那么就用UIScrollView.這是關(guān)于UIScrollView的教程.上干貨
3.如果要滾動超級無敵大的圖片,還是考慮用CATiledLayer吧.下面有講哦.

Example #3: CATextLayer

CATextLayer能夠快速高效簡單地來渲染純文本或者attributed strings.不像UILable,CATextLayer沒有指定的UIFont,只有一個CTFontRef 或者 CGFontRef.

一段這樣的代碼能該改變字體,字體大小,顏色,對齊方式等等:

// 1
let textLayer = CATextLayer()
textLayer.frame = someView.bounds // 2
var string = ""
for _ in 1...20 {
 string += "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce     auctor arcu quis velit congue dictum. "
}
textLayer.string = string 
// 3
let fontName: CFStringRef = "Noteworthy-Light"
textLayer.font = CTFontCreateWithName(fontName, fontSize, nil)
 // 4
textLayer.foregroundColor = UIColor.darkGrayColor().CGColor
textLayer.wrapped = true
textLayer.alignmentMode = kCAAlignmentLeft
textLayer.contentsScale = UIScreen.mainScreen().scale
someView.layer.addSublayer(textLayer)

解釋下上面代碼:
1.創(chuàng)建一個CATextLayer實例,bounds設(shè)為someView的bounds.
2.創(chuàng)建一段重復(fù)地文本,賦給這個textlayer
3.創(chuàng)建一個字體,賦給textlayer
4.設(shè)置文本環(huán)繞并且左對齊(你可以設(shè)為自然,右對齊.中心對齊,或者你覺得合理的).把它的contentsScale設(shè)為跟屏幕一樣.然后添加到layer的繼承樹里.

所有的layer類,包括CATextLayer,都是在默認(rèn)1的縮放比例下渲染的.當(dāng)本來就是屬于某個視圖里時,layer會自動把他們的contentsScale調(diào)整到對當(dāng)前屏幕來說合理的縮放比例.手動創(chuàng)建的layer需要你顯示地調(diào)整contentsScale,否則縮放比例一直是1,在視網(wǎng)膜屏看起來就會模糊蛋疼.

如果添加到了一個方形的someView,那么生成的文本是這個樣子的:
[圖片上傳失敗...(image-6f9b48-1587374045880)]

高興得時候你可以用省略號來代表被剪切的文本.(截斷)Truncation默認(rèn)是沒有的,你可以設(shè)在開頭,中間,或者結(jié)尾.看下面示意圖:
[圖片上傳失敗...(image-a12b3b-1587374045880)]
[圖片上傳失敗...(image-ece582-1587374045880)]
[圖片上傳失敗...(image-fe6061-1587374045880)]

Layer Player有許多能操作CATextLayer屬性的控制功能:
[圖片上傳失敗...(image-9b6cd9-1587374045880)]

Example #4: AVPlayerLayer

AVPlayerLayer有一個用來播放av多媒體文件的avplayer(AVPlayerItems).
下面是個例子:

override func viewDidLoad() { 
super.viewDidLoad()

 // 1 let playerLayer = AVPlayerLayer() 
playerLayer.frame = someView.bounds  

// 2
 let url = NSBundle.mainBundle().URLForResource("someVideo", withExtension:     "m4v") 
let player = AVPlayer(URL: url)  

// 3
player.actionAtItemEnd = .None 
playerLayer.player = player 
someView.layer.addSublayer(playerLayer)  


// 4 
 NSNotificationCenter.defaultCenter().addObserver(self, selector:"playerDidReachEndNotificationHandler:", name:         "AVPlayerItemDidPlayToEndTimeNotification", object: player.currentItem)} 

deinit {
 NSNotificationCenter.defaultCenter().removeObserver(self)
} 

// 5@IBAction func playButtonTapped(sender: UIButton) {
 if playButton.titleLabel?.text == "Play" {
 player.play() 
playButton.setTitle("Pause", forState: .Normal) 
} else {
 player.pause()
 playButton.setTitle("Play", forState: .Normal)
 } 
 updatePlayButtonTitle() 
updateRateSegmentedControl()} 

// 6
func playerDidReachEndNotificationHandler(notification: NSNotification) {
 let playerItem = notification.object as      
 AVPlayerItemplayerItem.seekToTime(kCMTimeZero)

}

代碼分析:
1.創(chuàng)建新的player layer,設(shè)置frame
2.用一個AV資源創(chuàng)建一個player
3.告訴player結(jié)束時神馬都不做.可選的操作有:如暫停播放下一個項目
4.注冊AVPlayer當(dāng)播放結(jié)束時的通知.(銷毀時移除監(jiān)聽通知)
5.播放按鈕點下時,觸發(fā)播放控制并設(shè)置按鈕的標(biāo)題.

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 13.0px 'Lucida Grande'; color: #4e4e4e; -webkit-text-stroke: #4e4e4e}span.s1 {font-kerning: none}

AVPlayerLayer 和其上面創(chuàng)建的 AVPlayer 會直觀地表示 AVPlayerItem 實例的第一個幀:
[圖片上傳失敗...(image-ff4069-1587374045880)]
AVPlayerLayer有一對額外的屬性:

videoGravity :設(shè)置視頻顯示的大小調(diào)整行為
videoGravity :確認(rèn)視頻是否可以播放

AVPlayer,另一面它有不少額外的屬性和方法.一個就是播放速率,從0到1的速度.0是暫停,1是正常(1x).
暫停就是把播放速率設(shè)為0,播放就是設(shè)為1.那么快進(jìn)呢,慢動作呢,循環(huán)播放呢.AVPlayerLayer都已經(jīng)想到了.設(shè)為2就是2倍快進(jìn),負(fù)當(dāng)然是慢動作了.
當(dāng)不以正常速度播放時,最好先確認(rèn)一下AVPlayerItem可以以一下速率播放:
canPlayFastForward()

canPlaySlowForward()

canPlayReverse()

canPlaySlowReverse()

canPlayFastReverse()

大多數(shù)視頻都可以快進(jìn),但是可以倒退播放的視頻不多見.Layer Player當(dāng)然也有播放可控制功能:
[圖片上傳失敗...(image-7dbf1c-1587374045880)]

Example #5: CAGradientLayer

CAGradientLayer能輕易地混合多種顏色,所以很適合用來當(dāng)背景.如何設(shè)置呢,你給他一個CGColor的數(shù)組,還有一個開始點和結(jié)束點.
記住,開始點和結(jié)束點不是清楚的點.他們定義在layer的bounds中.x為1表示在這個layer的右邊緣上,y是1的話就說明在layer的底部線上.
CAGradientLayer有個type屬性,雖然只有一個kCAGradientLayerAxial選項,它線性地轉(zhuǎn)換數(shù)組里的顏色.
這表示如果你在開始點和結(jié)束點中間畫了條線,漸進(jìn)性會出現(xiàn)在A線,而B線上的顏色都是一樣的:
[圖片上傳失敗...(image-75b3f3-1587374045880)]

你可以通過一個值介于0到1的數(shù)組控制locations屬性.

下面是創(chuàng)建gradient layer的代碼:

let gradientLayer = CAGradientLayer()
gradientLayer.frame = someView.bounds
gradientLayer.colors = [cgColorForRed(209.0, green: 0.0, blue: 0.0), 
cgColorForRed(255.0, green: 102.0, blue: 34.0), 
cgColorForRed(255.0, green: 218.0, blue: 33.0),
cgColorForRed(51.0, green: 221.0, blue: 0.0), 
cgColorForRed(17.0, green: 51.0, blue: 204.0), 
cgColorForRed(34.0, green: 0.0, blue: 102.0),
 cgColorForRed(51.0, green: 0.0, blue: 68.0)]
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 0, y: 1)
someView.layer.addSublayer(gradientLayer)
func cgColorForRed(red: CGFloat, green: CGFloat, blue: CGFloat) ->     AnyObject {         return UIColor(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha:       1.0).CGColor as AnyObject
}

上面的代碼創(chuàng)建了一個gradient layer,將layer的frame匹配someView的bounds.指定一組顏色,設(shè)置開始點和結(jié)束點,把layer添加到視圖的layer上,最后長這個樣子:
[圖片上傳失敗...(image-db54bd-1587374045880)]

叼炸天!

Layer Player也有這些功能哦:
[圖片上傳失敗...(image-a8c7dc-1587374045880)]

Example #6: CAReplicatorLayer

CAReplicatorLayer能復(fù)制一個layer指定的次數(shù),又能做出酷炫的效果了.
每一個layer拷貝可以有他自己的位置和顏色變化,可以推遲其繪圖動畫效果給整體復(fù)制器層饼丘。此外可以保存深度髓梅,使復(fù)制層具有 3D 效果。下面是一個示例 ︰

// 1
let replicatorLayer = CAReplicatorLayer()
replicatorLayer.frame = someView.bounds 
// 2
replicatorLayer.instanceCount = 30
replicatorLayer.instanceDelay = CFTimeInterval(1 / 30.0)
replicatorLayer.preservesDepth = false
replicatorLayer.instanceColor = UIColor.whiteColor().CGColor 

// 3
replicatorLayer.instanceRedOffset = 0.0
replicatorLayer.instanceGreenOffset = -0.5
replicatorLayer.instanceBlueOffset = -0.5
replicatorLayer.instanceAlphaOffset = 0.0 

// 4
let angle = Float(M_PI * 2.0) / 30
replicatorLayer.instanceTransform=CATransform3DMakeRotation(CGFloat(angle), 0.0, 0.0, 1.0)
someView.layer.addSublayer(replicatorLayer) 

// 5
let instanceLayer = CALayer()
let layerWidth: CGFloat = 10.0
let midX = CGRectGetMidX(someView.bounds) - layerWidth / 2.0
instanceLayer.frame = CGRect(x: midX, y: 0.0, width: layerWidth, height:     layerWidth * 3.0)
instanceLayer.backgroundColor =UIColor.whiteColor().CGColor
replicatorLayer.addSublayer(instanceLayer) 

// 6
let fadeAnimation = CABasicAnimation(keyPath: "opacity")
fadeAnimation.fromValue = 1.0
fadeAnimation.toValue = 0.0
fadeAnimation.duration = 1
fadeAnimation.repeatCount = Float(Int.max) 

// 7
instanceLayer.opacity = 0.0
instanceLayer.addAnimation(fadeAnimation, forKey: "FadeAnimation")

解釋下:
1.創(chuàng)建一個CAReplicatorLayer實例,把bounds與someView的匹配.
2.設(shè)置這個重復(fù)器layer拷貝的數(shù)量(instanceCount),然后延遲繪制.設(shè)這個重復(fù)器layer的深度為2D(preservesDepth = false),設(shè)置拷貝的顏色為白色
3.給每個連續(xù)被拷貝的實例的RGB顏色的值添加offsets值.每個顏色offset值默認(rèn)是0,添加了之后就會影響每一個實例.這個例子當(dāng)中,實例起初就被設(shè)為白色,所以紅,綠,藍(lán)的值都是1.之后紅的又被改成了0,綠和藍(lán)被改成了負(fù)數(shù),這樣會讓紅色成為一個亮眼的顏色.同樣的,透明度也加到了每個連續(xù)的被拷貝對象上.
4.把所有被復(fù)制實例旋轉(zhuǎn)變換成一個圓
5.創(chuàng)建一個layer,bounds改成能讓第一個被復(fù)制的對象居于中上方的位置,好放下組成的整個圓
6.做一個透明度從0到1的漸變動畫
7.把后來創(chuàng)建的layer的不可見度調(diào)到0

[圖片上傳失敗...(image-aeca2-1587374045880)]

[圖片上傳失敗...(image-96c8fe-1587374045880)]

Example #7: CATiledLayer

CATiledLayer異步地一塊一塊地繪制內(nèi)容.對超級大的圖像或者很多內(nèi)容在某個時刻只看它一小塊的情況很有效果.不用每次看的時候就立馬把整個內(nèi)容加載到內(nèi)存里,每次加載一部分.

有很多繪制的方法.一種就是是重寫UIView,用CATiledLayer重復(fù)地繪制一塊一塊的內(nèi)容,然后填充到背景里,像這樣:

// In ViewController.swift
import UIKit 
class ViewController: UIViewController {  

// 1 
@IBOutlet weak var tiledBackgroundView:
 TiledBackgroundView! 
} 

// In TiledBackgroundView.swift
import UIKit 
class TiledBackgroundView: UIView {
  let sideLength = CGFloat(50.0)  

// 2
 override class func layerClass() -> AnyClass { 
      return CATiledLayer.self
 } 

 // 3
 required init(coder aDecoder: NSCoder) {
   super.init(coder: aDecoder) 
   srand48(Int(NSDate().timeIntervalSince1970))
   let layer = self.layer as CATiledLayer
   let scale = UIScreen.mainScreen().scale
   layer.contentsScale = scale
   layer.tileSize = CGSize(width: sideLength * scale, height: sideLength *   scale)
 } 

 // 4 
override func drawRect(rect: CGRect) { 
   let context = UIGraphicsGetCurrentContext()
   var red = CGFloat(drand48()) 
   var green = CGFloat(drand48())
   var blue = CGFloat(drand48()) 
  CGContextSetRGBFillColor(context, red, green, blue, 1.0)     CGContextFillRect(context, rect)
 } 
}

來看一下:
1.tiledBackgroundView放在點(150.150),寬高都是300.
2.layerClass()被重寫了,這樣這個view的layer就會變成CATiledLayer類
3.rand48()用來在drawRect()里生成隨機(jī)色,把scales與屏幕匹配,并設(shè)置繪制塊的大小
4.重寫drawRect(),用隨機(jī)色填充背景
最后代碼繪制一個6x6的顏色方塊:
[圖片上傳失敗...(image-217d0d-1587374045880)]

Layer Player還在上面畫了一條路徑:
[圖片上傳失敗...(image-140437-1587374045881)]

放大上面的星星,你會發(fā)現(xiàn)細(xì)節(jié)處變得模糊了:
[圖片上傳失敗...(image-a37087-1587374045881)]

模糊的原因是layer保持的細(xì)節(jié)量(levelsOfDetail)不高.CATiledLayer有兩個相關(guān)的屬性,levelsOfDetail 和 levelsOfDetailBias.
levelsOfDetail,表示所持有的細(xì)節(jié)量.一般默認(rèn)是1,最大值就是最底層每個細(xì)節(jié)都有一個像素.
levelsOfDetailBias,是這個layer緩存的放大級別的細(xì)節(jié)的數(shù)量.默認(rèn)0.
舉個栗子,把上面的例子的levelsOfDetailBias弄成5,會把緩存等級提高到2x, 4x, 8x, 16x 和32x,縮放后看起來像這樣:
[圖片上傳失敗...(image-150bfd-1587374045881)]

接下來講一下CATiledLayer對于滾動超大圖片的用處.

雖然你需要給這個layer提供繪制方塊,和當(dāng)用戶拖動時哪塊方塊抓住的邏輯,感覺很麻煩.但是它帶來的性能提升是非常大的.
Layer Player的UIImage+TileCutter.swift里有一個UIImage extension..它的工作是把源圖片切割成特定大小的方塊,然后根據(jù)行和列起名.比如下面那個清晰的方塊就是3行7列:
[圖片上傳失敗...(image-4777bb-1587374045881)]

通過放置這些方塊就可以創(chuàng)建這個方塊layer了:

import UIKit 
class TilingViewForImage: UIView { 

   // 1
   let sideLength = CGFloat(640.0) 
  let fileName = "windingRoad"
   let cachesPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)[0] as String  

// 2
 override class func layerClass() -> AnyClass {
   return CATiledLayer.self 
}  

// 3 
required init(coder aDecoder: NSCoder) { 
  super.init(coder: aDecoder)
   let layer = self.layer as CATiledLayer
   layer.tileSize = CGSize(width: sideLength, height: sideLength)
 }  
// 4
 override func drawRect(rect: CGRect) {
  let firstColumn = Int(CGRectGetMinX(rect) / sideLength)
  let lastColumn = Int(CGRectGetMaxX(rect) / sideLength) 
  let firstRow = Int(CGRectGetMinY(rect) / sideLength) 
  let lastRow = Int(CGRectGetMaxY(rect) / sideLength) 
   for row in firstRow...lastRow { 
      for column in firstColumn...lastColumn {
         if let tile = imageForTileAtColumn(column, row: row) {

         let x = sideLength * CGFloat(column)
         let y = sideLength * CGFloat(row)
         let point = CGPoint(x: x, y: y)
         let size = CGSize(width: sideLength, height: sideLength)
         var tileRect = CGRect(origin: point, size: size)
         tileRect = CGRectIntersection(bounds, tileRect) 
        tile.drawInRect(tileRect) 
      } 
    } 
  }
 }  
func imageForTileAtColumn(column: Int, row: Int) -> UIImage? {
  let filePath = "\(cachesPath)/\(fileName)_\(column)_\(row)" 
  return UIImage(contentsOfFile: filePath) 
  } 
}

上面代碼:
1.創(chuàng)建邊長,圖片的文件名和方塊切割的緩存的路徑這三個屬性.
2.重寫 layerClass()返回 CATiledLayer.
3.實現(xiàn)init(_:)
4.重寫drawRect(),讓它根據(jù)行列位置進(jìn)行繪制

然后把該子類的視圖的大小調(diào)到圖片大小,添加到一個scrollview:
[圖片上傳失敗...(image-efdf6-1587374045881)]
然后你就有了一個大圖像的平滑滾動了:
[圖片上傳失敗...(image-9ff033-1587374045881)]
快速拖動時,你能明顯地看到是一塊一塊出現(xiàn)的.要減弱這個效果的話,可以把繪制塊的大小變小,創(chuàng)建一個CATiledLayer子類,重寫fadeDuration()方法返回0:

class TiledLayer: CATiledLayer {  
  override class func fadeDuration() -> CFTimeInterval {
     return 0.0
   }
 }

Example #8: CAShapeLayer

CAShapeLayer利用可伸縮矢量路徑進(jìn)行繪制,它比使用圖片快得多祈噪。更好的是,你不用再提供圖片的正常 @2x @3x版本了.
另外掂为,你有各種各樣的屬性用來定義自定義線的粗細(xì),顏色瞄崇,如何加入其他的線呻粹,如果線相交形成一個封閉的區(qū)域,應(yīng)填什么顏色苏研。下面是一個例子:

import UIKit 
class ViewController: UIViewController { 
   @IBOutlet weak var someView: UIView!  

    // 1
     let rwColor = UIColor(red: 11/255.0, green: 86/255.0, blue:   14/255.0, alpha: 1.0)
     let rwPath = UIBezierPath() 
     let rwLayer = CAShapeLayer() 

     // 2
    func setUpRWPath() {
    rwPath.moveToPoint(CGPointMake(0.22, 124.79))           
    rwPath.addLineToPoint(CGPointMake(0.22, 249.57))   
    rwPath.addLineToPoint(CGPointMake(124.89, 249.57)) 
    rwPath.addLineToPoint(CGPointMake(249.57, 249.57)) 
    rwPath.addLineToPoint(CGPointMake(249.57, 143.79))   
    rwPath.addCurveToPoint(CGPointMake(249.37, 38.25), controlPoint1: CGPointMake(249.57, 85.64), controlPoint2: CGPointMake(249.47, 38.15)) 
    rwPath.addCurveToPoint(CGPointMake(206.47, 112.47), controlPoint1: CGPointMake(249.27, 38.35), controlPoint2: CGPointMake(229.94, 71.76)) 
    rwPath.addCurveToPoint(CGPointMake(163.46, 186.84), controlPoint1: CGPointMake(182.99, 153.19), controlPoint2: CGPointMake(163.61, 186.65)) 
    rwPath.addCurveToPoint(CGPointMake(146.17, 156.99), controlPoint1: CGPointMake(163.27, 187.03), controlPoint2: CGPointMake(155.48, 173.59))
    rwPath.addCurveToPoint(CGPointMake(128.79, 127.08), controlPoint1: CGPointMake(136.82, 140.43), controlPoint2: CGPointMake(129.03, 126.94)) 
    rwPath.addCurveToPoint(CGPointMake(109.31, 157.77), controlPoint1: CGPointMake(128.59, 127.18), controlPoint2: CGPointMake(119.83, 141.01)) 
    rwPath.addCurveToPoint(CGPointMake(89.83, 187.86), controlPoint1: CGPointMake(98.79, 174.52), controlPoint2: CGPointMake(90.02, 188.06)) 
    rwPath.addCurveToPoint(CGPointMake(56.52, 108.28), controlPoint1: CGPointMake(89.24, 187.23), controlPoint2: CGPointMake(56.56, 109.11)) 
    rwPath.addCurveToPoint(CGPointMake(64.02, 102.25), controlPoint1: CGPointMake(56.47, 107.75), controlPoint2: CGPointMake(59.24, 105.56)) 
    rwPath.addCurveToPoint(CGPointMake(101.42, 67.57), controlPoint1: CGPointMake(81.99, 89.78), controlPoint2: CGPointMake(93.92, 78.72)) 
    rwPath.addCurveToPoint(CGPointMake(108.38, 30.65), controlPoint1: CGPointMake(110.28, 54.47), controlPoint2: CGPointMake(113.01, 39.96))
    rwPath.addCurveToPoint(CGPointMake(10.35, 0.41), controlPoint1: CGPointMake(99.66, 13.17), controlPoint2: CGPointMake(64.11, 2.16)) 
    rwPath.addLineToPoint(CGPointMake(0.22, 0.07)) 
    rwPath.addLineToPoint(CGPointMake(0.22, 124.79)) 
    rwPath.closePath() }  

// 3 
   func setUpRWLayer() { 
    rwLayer.path = rwPath.CGPath
    rwLayer.fillColor = rwColor.CGColor
    rwLayer.fillRule = kCAFillRuleNonZero
    rwLayer.lineCap = kCALineCapButt 
    rwLayer.lineDashPattern = nil 
    rwLayer.lineDashPhase = 0.0 
    rwLayer.lineJoin = kCALineJoinMiter 
    rwLayer.lineWidth = 1.0 
    rwLayer.miterLimit = 10.0 
    rwLayer.strokeColor = rwColor.CGColor
   }  
    override func viewDidLoad() {
       super.viewDidLoad()  

    // 4 
        setUpRWPath() 
        setUpRWLayer() 
        someView.layer.addSublayer(rwLayer) 
    }
}

解釋代碼:
1.創(chuàng)建顏色路徑shapelayer對象
2.繪制路徑
3.設(shè)置layer,它的路徑被設(shè)為第二步的路徑,填充色設(shè)為第一步里設(shè)置的顏色,填充規(guī)則被顯式地設(shè)置為非零的默認(rèn)值等浊。
唯一的其他選擇是even-odd,并且對于這個形狀摹蘑,沒有交叉的路徑填充規(guī)則讓差異變得很小筹燕。
非零規(guī)則把從左至右的路徑記為+ 1從右至左的路徑為1,它總和了所有的路徑衅鹿,如果和大于0撒踪,那他就根據(jù)路徑填充顏色.
從本質(zhì)上講,非零填充這個形狀的所有點大渤。
even-odd規(guī)則計數(shù)形成一個形狀的路徑的總數(shù)制妄,如果最后是奇數(shù),則該形狀被填充泵三。當(dāng)一個圖片是一千字時,這絕對是個列子耕捞。
形成這個五角星的路徑交點的總數(shù)是偶數(shù),所以沒有被填充.而形成每個三角形的路徑的交點是奇數(shù),所以該三角形被填充切黔。
[圖片上傳失敗...(image-68bbe5-1587374045881)]


[圖片上傳失敗...(image-6f229b-1587374045881)]


我們跳過了Layer Player里的下一個demo.因為CAEAGLLayer已經(jīng)被CAMetalLayer替代了.這里有相關(guān)教程.

Example #9: CATransformLayer

CATransformLayer不平鋪它的子layer,它很適合3D結(jié)構(gòu)的視圖.CATransformLayer實際是子layer的一個容器,每個子layer有自己的形變和透明度,但,類似線框?qū)挾群皖伾膌ayer屬性會被忽略掉.
因為CATransformLayer沒有2維坐標(biāo)系,所以沒辦法檢測點擊.但是在獨立的子layer里可以檢測到.舉個栗子:

import UIKit
 class ViewController: UIViewController { 
 @IBOutlet weak var someView: UIView!  

// 1 
let sideLength = CGFloat(160.0) 
var redColor = UIColor.redColor()
 var orangeColor = UIColor.orangeColor() 
var yellowColor = UIColor.yellowColor() 
var greenColor = UIColor.greenColor() 
var blueColor = UIColor.blueColor() 
var purpleColor = UIColor.purpleColor() 
var transformLayer = CATransformLayer()  

// 2 
func setUpTransformLayer() { 
var layer = sideLayerWithColor(redColor)         
transformLayer.addSublayer(layer) 

 layer = sideLayerWithColor(orangeColor) 
var transform = CATransform3DMakeTranslation(sideLength / 2.0, 0.0, sideLength / -2.0)
 transform = CATransform3DRotate(transform, degreesToRadians(90.0), 0.0, 1.0, 0.0) 
layer.transform = transform 
transformLayer.addSublayer(layer)  
layer = sideLayerWithColor(yellowColor) 
layer.transform = CATransform3DMakeTranslation(0.0, 0.0, -sideLength) 
transformLayer.addSublayer(layer)  
layer = sideLayerWithColor(greenColor) 
transform = CATransform3DMakeTranslation(sideLength / -2.0, 0.0, sideLength / -2.0)
transform = CATransform3DRotate(transform, degreesToRadians(90.0),   0.0, 1.0, 0.0) 
layer.transform = transform
   transformLayer.addSublayer(layer)  
layer = sideLayerWithColor(blueColor)
 transform = CATransform3DMakeTranslation(0.0, sideLength / -2.0,       sideLength / -2.0) 
transform = CATransform3DRotate(transform, degreesToRadians(90.0), 1.0, 0.0, 0.0)
 layer.transform = transform
 transformLayer.addSublayer(layer) 
 layer = sideLayerWithColor(purpleColor) 
transform = CATransform3DMakeTranslation(0.0, sideLength / 2.0, sideLength / -2.0) 
transform = CATransform3DRotate(transform,     degreesToRadians(90.0), 1.0, 0.0, 0.0) 
layer.transform = transform 
transformLayer.addSublayer(layer) 
 transformLayer.anchorPointZ = sideLength / -2.0 
 applyRotationForXOffset(16.0, yOffset: 16.0) 

}  

// 3
 func sideLayerWithColor(color: UIColor) -> CALayer { 
let layer = CALayer() 
layer.frame = CGRect(origin: CGPointZero, size: CGSize(width:     sideLength, height: sideLength))
 layer.position = CGPoint(x: CGRectGetMidX(someView.bounds), y: CGRectGetMidY(someView.bounds)) 
layer.backgroundColor = color.CGColor return layer } 
 func degreesToRadians(degrees: Double) -> CGFloat { 
return CGFloat(degrees * M_PI / 180.0)
 } 

 // 4 
func applyRotationForXOffset(xOffset: Double, yOffset: Double) {

 let totalOffset = sqrt(xOffset * xOffset + yOffset * yOffset)
 let totalRotation = CGFloat(totalOffset * M_PI / 180.0) let     xRotationalFactor = CGFloat(totalOffset) / totalRotation
 let yRotationalFactor = CGFloat(totalOffset) / totalRotation 

 let currentTransform = CATransform3DTranslate(transformLayer.sublayerTransform, 0.0, 0.0, 0.0)
 let rotationTransform = CATransform3DRotate(transformLayer.sublayerTransform, totalRotation, xRotationalFactor * currentTransform.m12 - yRotationalFactor * currentTransform.m11, xRotationalFactor * currentTransform.m22 - yRotationalFactor * currentTransform.m21, xRotationalFactor * currentTransform.m32 - yRotationalFactor * currentTransform.m31) 

transformLayer.sublayerTransform = rotationTransform 

}  

// 5
 override func touchesBegan(touches: NSSet, withEvent event: UIEvent)               {
 if let location = touches.anyObject()?.locationInView(someView) { 
  for layer in transformLayer.sublayers {
      if  let hitLayer = layer.hitTest(location) {
          println("Transform layer tapped!") break } 
      } 
   } 
}  
override func viewDidLoad() {
 super.viewDidLoad()  
// 6 
setUpTransformLayer()
 someView.layer.addSublayer(transformLayer)
 } 
}

解釋代碼:
1.為邊長,顏色,方塊的每個面創(chuàng)建屬性和一個transform layer
2.通過創(chuàng)建旋轉(zhuǎn)面并添加到這個transform layer來產(chǎn)生方塊.然后設(shè)置transform layerz軸的錨點,旋轉(zhuǎn)方塊,添加到視圖繼承樹里.
3.創(chuàng)建幫助器代碼來用指定的顏色創(chuàng)建每個方塊面layer并將度轉(zhuǎn)換為弧度砸脊。為什么是弧度具篇?因為我覺得它更直觀纬霞。
4.應(yīng)用一個基于x和y偏移的旋轉(zhuǎn).注意看下,代碼把變形設(shè)到了子layerTransform上,而且應(yīng)用于transform layer的子layer。
5.檢查transform layer里的觸摸.
6.設(shè)置并添加到view里

note:currentTransform.m##這些是什么?這些都是矩陣代表包括矩形數(shù)組的行和列的元素的 CATransform3D 屬性.想了解更多的話點我.

運行程序,看下這個250x250的view:
[圖片上傳失敗...(image-ec6bf5-1587374045881)]

Layer Player :
[圖片上傳失敗...(image-7ce231-1587374045881)]

Example #10: CAEmitterLayer

總算到最后一個了.

CAEmitterLayer 渲染 CAEmitterCell 實例的具有動畫性的粒子驱显。CAEmitterLayer 和 CAEmitterCell 有更改渲染速率诗芜、 大小瞳抓、 形狀、 顏色伏恐、 速度孩哑、 生命周期和更多的屬性。
栗子:

  import UIKit 
  class ViewController: UIViewController { 

 // 1
         let emitterLayer = CAEmitterLayer() 
         let emitterCell = CAEmitterCell()  

 // 2
 func setUpEmitterLayer() { 
          emitterLayer.frame = view.bounds 
          emitterLayer.seed = UInt32(NSDate().timeIntervalSince1970)                 
          emitterLayer.renderMode = kCAEmitterLayerAdditive   
          emitterLayer.drawsAsynchronously = true 
          setEmitterPosition() 

        }  

    // 3 
  func setUpEmitterCell() { 
        emitterCell.contents = UIImage(named: "smallStar")?.CGImage  

        emitterCell.velocity = 50.0 
        emitterCell.velocityRange = 500.0  

        emitterCell.color = UIColor.blackColor().CGColor     
        emitterCell.redRange = 1.0
        emitterCell.greenRange = 1.0 
        emitterCell.blueRange = 1.0 
        emitterCell.alphaRange = 0.0 
        emitterCell.redSpeed = 0.0 
        emitterCell.greenSpeed = 0.0 
        emitterCell.blueSpeed = 0.0 
        emitterCell.alphaSpeed = -0.5 

        let zeroDegreesInRadians = degreesToRadians(0.0)       
        emitterCell.spin = degreesToRadians(130.0)     
        emitterCell.spinRange = zeroDegreesInRadians   
        emitterCell.emissionRange = degreesToRadians(360.0)  


        emitterCell.lifetime = 1.0 
        emitterCell.birthRate = 250.0 
        emitterCell.xAcceleration = -800.0 
        emitterCell.yAcceleration = 1000.0 
  }  

// 4 
 func setEmitterPosition() { 
     emitterLayer.emitterPosition = CGPoint(x:CGRectGetMidX(view.bounds), y: CGRectGetMidY(view.bounds))       
    } 

 func degreesToRadians(degrees: Double) -> CGFloat { 
                  return CGFloat(degrees * M_PI / 180.0) 
     } 
 override func viewDidLoad() {
   super.viewDidLoad() 

   // 5 
  setUpEmitterLayer() 
  setUpEmitterCell()
  emitterLayer.emitterCells = [emitterCell]            
  view.layer.addSublayer(emitterLayer) }  

  // 6 
  
    override func traitCollectionDidChange(previousTraitCollection:UITraitCollection?) { 
          setEmitterPosition() 
   } 
}

解釋代碼:
1.創(chuàng)建 emitter layer 和cell
2.通過以下操作設(shè)置emitter layer

1. 為圖層用來給cell的屬性(比如速度)值隨機(jī)化的隨機(jī)數(shù)發(fā)生器提供了一個seed翠桦。下一條進(jìn)一步解釋了這個問題横蜒。
2. 上面的渲染粒子cell(Renders emitter cell),他們的背景色 ,線寬是根據(jù)renderMode按順序確定的.
   注意點---蘋果文檔目前不正確地指出,此屬性的值定義在Emitter Modes模式下销凑。事實上丛晌,renderMode定義在Emitter Render Order下。默認(rèn)值是unordered, 其他的值有oldest first(最老的優(yōu)先), oldest last, back to front 和 additive.
3.drawsAsynchronously設(shè)為true.這個操作能夠提高性能,發(fā)射器是持續(xù)不斷地重繪它的cell的.
4.通過setEmitterPosition()來設(shè)置emitter的位置

3.這一塊代碼有很多操作:

  1.設(shè)置emitter cell的內(nèi)容為一張圖片(這張圖片Layer Player里有)
  2.然后它指定初始速度和最大的方差 (velocityRange).emitter layer使用上面的種子創(chuàng)建了一個在這個范圍內(nèi)隨機(jī)生成值的隨機(jī)數(shù)發(fā)生器.
  3.把顏色設(shè)置為黑色斗幼,允許從默認(rèn)的白色開始變化澎蛛,因為白色太過于明亮。
  4.下一步是用相同的隨機(jī)器設(shè)置一系列顏色范圍,這一次是把變化的范圍指定給每個顏色.速度表示顏色變化得有多快.
  5.設(shè)置cell的旋轉(zhuǎn)速率和發(fā)射范圍.發(fā)射范圍決定了emitter cells如何在給定的emissionRange里分布
  6.cell生命周期改為1,表示1秒,默認(rèn)是0.所以你如果不改的話就看不到效果了.還有生出速率,默認(rèn)也是0.這里也要改成正數(shù),否則也是沒效果的.

4.將度轉(zhuǎn)換為弧度蜕窿,將發(fā)射器cell的位置設(shè)置為視圖的中點谋逻。
5.全部設(shè)置好,加到該加的地方
6.這個方法是iOS8的新方法,提供了一個方法來處理當(dāng)前的特征集合,比如旋轉(zhuǎn)設(shè)備桐经。不熟悉特征集合嗎毁兆?查閱iOS 8 by Tutorials.

運行:
[圖片上傳失敗...(image-fd09c0-1587374045881)]

Layer Player:
[圖片上傳失敗...(image-b56960-1587374045881)]


干貨上完啦

[圖片上傳失敗...(image-6d1a9e-1587374045881)]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市阴挣,隨后出現(xiàn)的幾起案子荧恍,更是在濱河造成了極大的恐慌,老刑警劉巖屯吊,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件送巡,死亡現(xiàn)場離奇詭異,居然都是意外死亡盒卸,警方通過查閱死者的電腦和手機(jī)骗爆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔽介,“玉大人摘投,你說我怎么就攤上這事『缧睿” “怎么了犀呼?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長薇组。 經(jīng)常有香客問我外臂,道長,這世上最難降的妖魔是什么律胀? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任宋光,我火速辦了婚禮貌矿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘罪佳。我一直安慰自己逛漫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布赘艳。 她就那樣靜靜地躺著酌毡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蕾管。 梳的紋絲不亂的頭發(fā)上阔馋,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機(jī)與錄音娇掏,去河邊找鬼呕寝。 笑死,一個胖子當(dāng)著我的面吹牛婴梧,可吹牛的內(nèi)容都是我干的下梢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼塞蹭,長吁一口氣:“原來是場噩夢啊……” “哼孽江!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起番电,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤岗屏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后漱办,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體这刷,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年娩井,在試婚紗的時候發(fā)現(xiàn)自己被綠了暇屋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡洞辣,死狀恐怖撼泛,靈堂內(nèi)的尸體忽然破棺而出恋脚,到底是詐尸還是另有隱情煞檩,我是刑警寧澤稿存,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站著瓶,受9級特大地震影響联予,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一躯泰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧华糖,春花似錦麦向、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至兼搏,卻和暖如春卵慰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背佛呻。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工裳朋, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吓著。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓鲤嫡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绑莺。 傳聞我的和親對象是個殘疾皇子暖眼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 在iOS中隨處都可以看到絢麗的動畫效果,實現(xiàn)這些動畫的過程并不復(fù)雜纺裁,今天將帶大家一窺ios動畫全貌诫肠。在這里你可以看...
    每天刷兩次牙閱讀 8,489評論 6 30
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現(xiàn)這些動畫的過程并不復(fù)雜欺缘,今天將帶大家一窺iOS動畫全貌栋豫。在這里你可以看...
    F麥子閱讀 5,111評論 5 13
  • 轉(zhuǎn)載:http://www.reibang.com/p/32fcadd12108 每個UIView有一個伙伴稱為l...
    F麥子閱讀 6,200評論 0 13
  • 一、CAShapelayer 我們知道可以不使用圖片情況下利用CGpath去構(gòu)建任意形狀的陰影谚殊。其實我們也可...
    小貓仔閱讀 1,461評論 0 5
  • >復(fù)雜的組織都是專門化的 >Catharine R. Stimpson 到目前為止笼才,我們已經(jīng)探討過`CALayer...
    夜空下最亮的亮點閱讀 1,018評論 0 2