你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> Swift語言iOS開發:CALayer十則示例

Swift語言iOS開發:CALayer十則示例

編輯:IOS開發基礎

LayersScreenshot-316x320.png

作者:Scott Gardner   譯者:TurtleFromMars
原文:CALayer in iOS with Swift: 10 Examples

如你所知,我們在iOS應用中看到的都是視圖(view),包括按鈕視圖、表視圖、滑動條視圖,還有可以容納其他視圖的父視圖等。

但你或許不知道在iOS中支撐起每個視圖的是一個叫做"圖層(layer)"的類,確切地說是CALayer。

本文中您會了解CALayer及其工作原理,還有應用CALayer打造酷炫效果的十則示例,比如繪制矢量圖形、漸變色,甚至是粒子系統。

本文要求讀者熟悉iOS應用開發和Swift語言的基礎知識,包括利用Storyboard構建用戶界面。

注:如果您尚未掌握這些基礎,不必擔心,我們有不少相關教程,例如使用Swift語言編寫iOS應用和iOS學徒。

准備開始

要理解圖層是什麼,最簡便的方式就是"實地考察"。我們這就創建一個簡單的項目,從頭開始玩轉圖層。

准備好寫代碼了嗎?好!啟動Xcode,然後:

1.選擇File\New\Project菜單項。

2.在對話框中選擇iOS\Application\Single View Application。

3.點擊Next,Product Name填寫CALayerPlayground,然後輸入你自己的Organization Name和Identifier。

4.Language選Swift,Devices選Universal。

5.取消選擇Core Data,點擊Next。

6.把項目保存到合適的位置(個人習慣把項目放在用戶目錄下建立的Source文件夾),點擊Create。

好,文件准備就緒,接下來就是創建視圖了:

7.在項目導航欄(Project navigator)中選擇Main.storyboard。

8.選擇View\Assistant Editor\Show Assistant Editor菜單項,如果沒有顯示對象庫(Object Library),請選擇View\Utilities\Show Object Library。

9.然後選擇Editor\Canvas\Show Bounds Rectangles,這樣在向場景添加視圖時就可以看到輪廓了。

10.把一個視圖(View)從對象庫拖入視圖控制器場景,保持選中狀態,在尺寸檢查器(View\Utilities\Show Size Inspector)中將x和y設為150,Width和Height設為300。

11.視圖保持選中,點擊自動布局工具欄(Storyboard右下角)的Align按鈕,選中Horizontal Center in Container和Vertical Center in Container,數值均為0,然後點擊Add 2 Constraints。

12.點擊Pin按鈕,選中Width和Height,數值均設為300,點擊Add 2 Constraints。

最後按住control從剛剛創建的視圖拖到ViewController.swift文件中viewDidLoad()方法的上方,在彈框中將outlet命名為viewForLayer,如圖:

014.png

點擊Connect創建outlet。

將ViewController.swift中的代碼改寫為:

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
  }
 
}

之前提到iOS中的每個視圖都擁有一個關聯的圖層,你可以通過yourView.layer訪問圖層。這段代碼首先創建了一個叫"l"(小寫L)的計算屬性,方便訪問viewForLayer的圖層,可讓你少寫一些代碼。

這段代碼還調用了setUpLayer方法設置圖層屬性:陰影,藍色背景,紅色粗邊框。你馬上就可以了解這些東西,不過現在還是先構建App,在iOS模擬器中運行(我選了iPhone 6),看看自定義的圖層如何。

015.png

幾行代碼,效果還不錯吧?還是那句話,每個視圖都由圖層支撐,所以你也可以對App中的任何視圖做出類似修改。我們繼續深入。

CALayer基本屬性

CALayer有幾個屬性可以用來自定外觀,想想剛才做的:

  • 把圖層背景色從默認的無色改為藍色

  • 通過把邊框寬度從默認的0改為100來添加邊框

  • 把邊框顏色從默認的黑色改為紅色

  • 最後把陰影透明度從0(全透明)改為0.7,產生陰影效果,此外還把陰影半徑從默認的3改為10。

以上只是CALayer中可以設置的部分屬性。我們再試兩個,在setUpLayer()中追加以下代碼:

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

CALayer的contents屬性可以把圖層的內容設為圖片,這裡我們要設置一張"星星"的圖片,為此你需要把圖片添加到項目中,請下載圖片並添加到項目中。

構建,運行,欣賞一下效果:

016.png

注意星星居中,這是因為contentsGravity屬性被設為kCAGravityCenter,如你所想,重心也可以設為上、右上、右、右下、下、左下、左、左上。

更改圖層外觀

僅供娛樂,我們來添加幾個手勢識別器來控制圖層外觀。在Xcode中,向viewForLayer對象上拖一個輕觸手勢識別器(tap gesture recognizer),見下圖:

017.png

注:如果你對手勢識別器比較陌生,請參閱Using UIGestureRecognizer with Swift。

以此類推,再添加一個捏合手勢識別器(pinch gesture recognizer)。

然後按住control依次將兩個手勢識別器從Storyboard場景停靠欄拖入ViewController.swift,放在setUpLayer()和類自身的閉合花括號之間。

在彈框中修改連接為Action,命名輕觸識別操作為tapGestureRecognized,捏合識別操作為pinchGestureRecognized,例如:

018.png

如下改寫tapGestureRecognized(_:):

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

當令視圖識別出輕觸手勢時,代碼告知viewForLayer圖層在0.7和0之間切換陰影透明度。

你說視圖?嗯,沒錯,重寫CALayer的hitTest(_:)也可以實現相同效果,本文後面也會看到這個方法,不過我們這裡用的方法也有道理:圖層本身並不能響應手勢識別,只能響應點擊測試,所以我們在視圖上設置了輕觸手勢識別器。

然後如下修改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
  }
}

此處基於用戶的捏合手勢創建正負偏移值,借此調整圖層框架大小、邊緣寬度和邊角半徑。

圖層的邊角半徑默認值為0,意即標准的90度直角。增大半徑會產生圓角,如果想將圖層變成圓形,可以設邊角半徑為寬度的一半。

注意:調整邊角半徑並不會裁剪圖層內容(星星圖片),除非圖層的masksToBounds屬性被設為true。

構建運行,嘗試在視圖中使用輕觸和捏合手勢:

019.png

嘿,再好好裝扮一下都能當頭像用了! :]

CALayer體驗

CALayer中的屬性和方法琳琅滿目,此外還有幾個包含特有屬性和方法的子類。

要遍歷如此酷炫的API,Raywenderlich.com導游先生最好不過了。

接下來,你需要以下材料:

  • Layer Player App

  • Layer Player 源代碼

該App包含十種不同的CALayer示例,本文後面會依次介紹,十分方便。先來吊吊大家的胃口:

020.png

下面在講解每個示例的同時,我建議在CALayer演示應用中親自動手試驗,還可以讀讀代碼。不用寫,只要深呼吸,輕松閱讀就可以了。 :]

我相信這些酷炫的示例會啟發您利用不同的CALayer為自己的App錦上添花,希望大家喜歡!

示例 #1:CALayer

023.png

前面我們看過使用CALayer的示例,也就是設置各種屬性。

關於CALayer還有幾點沒提:

  • 圖層可以包含子圖層。就像視圖可以包含子視圖,圖層也可以有子圖層,稍加利用就能打造漂亮的效果!

  • 圖層屬性自帶動畫效果。修改圖層屬性時,存在默認的動畫效果,你也可以自定義動畫行為。

  • 圖層是輕量概念。相對視圖而言,圖層更加輕量,因此圖層可以幫助提升性能。

  • 圖層有大量實用屬性。前面你已經看過幾條了,我們繼續探索!

剛剛說CALayer圖層有很多屬性,我們來看一批實用屬性:有些屬性你可能第一次見,但真的很方便!

// 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
someView.layer.addSublayer(layer)

在以上代碼中:

  • 創建一個CALayer實例,並把框架設為someView邊框。

  • 將圖層內容設為一張圖片,並使其在圖層內居中,注意賦值的類型是底層的Quartz圖像數據(CGImage)。

  • 使用過濾器,過濾器在圖像利用contentsGravity放大時發揮作用,可用於改變大小(縮放、比例縮放、填充比例縮放)和位置(中心、上、右上、右等等)。以上屬性的改變沒有動畫效果,另外如果geometryFlipped未設為true,幾何位置和陰影會上下顛倒。繼續:

  • 把背景色設為Ray最愛的深綠色。:] 然後讓圖層透明、可見。同時令圖層不要遮罩內容,意思是如果圖層尺寸小於內容(星星圖片),圖像不會被裁減。

  • 圖層邊角半徑設為圖層寬度的一半,使邊緣變為圓形,注意圖層顏色賦值類型為Quartz顏色引用(CGColor)。

  • 創建陰影,設shouldRasterize為true(後文還會提到),然後將圖層加入視圖結構樹。

結果如下:

021.png

CALayer還有兩個附加屬性有助於改善性能:shouldRasterize和drawsAsynchronously。

shouldRasterize默認為false,設為true可以改善性能,因為圖層內容只需要一次渲染。相對畫面中移動但自身外觀不變的對象效果拔群。

drawsAsynchronously默認值也是false。與shouldRasterize相對,該屬性適用於圖層內容需要反復重繪的情況,此時設成true可能會改善性能,比如需要反復繪制大量粒子的粒子發射器圖層(可以參考後面的CAEmitterLayer示例)。

謹記:如果想將已有圖層的shouldRasterize或drawsAsynchronously屬性設為true,一定要三思而後行,考慮可能造成的影響,對比true與false的性能差異,辨明屬性設置是否有積極效果。設置不當甚至會導致性能大幅下降。

無論如何還是先回到圖層演示應用,其中有些控件可以用來調整CALayer的屬性:

024.png

調節試試看,感受一下,利用CALayer可以實現怎樣的效果。

注:圖層不屬於響應鏈(responder chain),無法像視圖一樣直接響應觸摸和手勢,我們在CALayerPlayground中見識過。不過圖層有點擊測試,後面的CATransformLayer會提到。你也可以向圖層添加自定義動畫,CAReplicatorLayer中會出現。

示例 #2:CAScrollLayer

CAScrollLayer顯示一部分可滾動圖層,該圖層十分基礎,無法直接響應用戶的觸摸操作,也不能直接檢查可滾動圖層的邊界,故可避免越界無限滾動。 

UIScrollView用的不是CAScrollLayer,而是直接改動圖層邊界。

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)
  }
 
}

以上代碼:

  • 定義一個繼承UIView的類,重寫layerClass()返回CAScrollLayer,該方法等同於創建一個新圖層作為子圖層(CALayer示例中做過)。

  • 一個用以方便簡化訪問自定義視圖滾動圖層的計算屬性。

  • 設滾動模式為二維滾動。

  • 識別出輕觸手勢時,讓滾動圖層在UIView動畫中滾到新建的點。(注:scrollToPoint(_:)和scrollToRect(_:)不會自動使用動畫效果。)

案例研究:如果ScrollingView實例包含大於滾動視圖邊界的圖片視圖,在運行上述代碼並點擊視圖時結果如下:

025.gif

圖層演示應用中有可以鎖定滾動方向(水平或垂直)的開關。

以下經驗規律用於決定是否使用CAScrollLayer:

  • 如果想使用輕量級的對象,只需用代碼操作滾動:可以考慮CAScrollLayer。

  • 如果想讓用戶操作滾動,UIScrollView大概是更好的選擇。要了解更多,請參考我們的視頻教程。

  • 如果是滾動大型圖片:考慮使用CATiledLayer(見後文)。

示例 #3:CATextLayer

CATextLayer能夠對普通文本或屬性字串進行簡單快速的渲染。與UILabel不同,CATextLayer無法指定UIFont,只能使用CTFontRef或CGFontRef。

像下面這樣的代碼完全可以掌控文本的字體、字體大小、顏色、對齊、折行(wrap)和截斷(truncation)規則,也有動畫效果:

// 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)

以上代碼解釋如下:

  • 創建一個CATextLayer實例,令邊界與someView相同。

  • 重復一段文本,創建字符串並賦給文本圖層。

  • 創建一個字體,賦給文本圖層。

  • 將文本圖層設為折行、左對齊,你也可以設自然對齊(natural)、右對齊(right)、居中對齊(center)或兩端對齊(justified),按屏幕設置contentsScale屬性,然後把圖層添加到視圖結構樹。

不僅是CATextLayer,所有圖層類的渲染縮放系數都默認為1。在添加到視圖時,圖層自身的contentsScale縮放系數會自動調整,適應當前畫面。你需要為手動創建的圖層明確指定contentsScale屬性,否則默認的縮放系數1會在Retina顯示屏上產生部分模糊。

如果創建的文本圖層添加到了方形的someView,效果會像這樣:

026.png

你可以設置截斷(Truncation)屬性,生效時被截斷的部分文本會由省略號代替顯示。默認設定為無截斷,位置可設為開頭、末尾或中間截斷:

027.png

圖層演示應用中,你可以隨心所欲地修改很多CATextLayer屬性:

028.png

示例 #4:AVPlayerLayer

AVPlayerLayer是建立在AVFoundation基礎上的實用圖層,持有一個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 AVPlayerItem
  playerItem.seekToTime(kCMTimeZero)
}

上述代碼解釋:

  1. 新建一個播放器圖層,設置框架。

  2. 使用AV asset資源創建一個播放器。

  3. 告知命令播放器在播放完成後停止。其他選項還有暫停或自動播放下一個媒體資源。

  4. 注冊AVPlayer通知,在一個文件播放完畢後發送通知,並在析構函數中刪除作為觀察者的控制器。

  5. 點擊播放按鈕時,觸發控件播放AV asset並設置按鈕文字。

注意這只是個入門示例,在實際項目中往往不會采用文字按鈕控制播放。

AVPlayerLayer和其中創建的AVPlayer會像這樣顯示為AVPlayerItem實例的第一幀:

029.png

AVPlayerLayer還有一些附加屬性:

  • videoGravity設置視頻顯示的縮放行為。

  • readyForDisplay檢測是否准備好播放視頻。

另一方面,AVPlayer也有不少附加屬性和方法,有一個值得注意的是rate屬性,對於0到1之間的播放速率,0代表暫停,1代表常速播放(1x)。

不過rate屬性的設置是與播放行為聯動的,也就是說調用pause()方法和把rate設為0是等價的,調用play()與把rate設為1也一樣。

那快進、慢動作和反向播放呢?交給AVPlayerLayer把。rate大於1時會令播放器以相應倍速進行播放,例如rate設為2就是二倍速。

如你所想,rate為負時會讓播放器以相應倍速反向播放。

然而,在以非常規速率播放之前,AVPlayerItem上會調用適當方法,驗證是否能夠以相應速率進行播放:

  • canPlayFastForward()對應大於1

  • canPlaySlowForward()對應0到1之間

  • canPlayReverse()對應-1

  • canPlaySlowReverse()對應-1到0之間

  • canPlayFastReverse()對應小於-1

絕大多數視頻都支持以不同速率正向播放,可以反向播放的視頻相對少一些。演示應用也包含了播放控件:

030.png

示例 #5:CAGradientLayer

CAGradientLayer簡化了混合兩種或更多顏色的工作,尤其適用於背景。要配置漸變色,你需要分配一個CGColor數組,以及標識漸變圖層起止點的startPoint和endPoint。

注意:startPoint和endPoint並不是明確的點,而是用單位坐標空間定義,在繪制時映射到圖層邊界。也就是說x值為1表示點在圖層右邊緣,y值為1表示點在圖層下邊緣。

CAGradientLayer包含type屬性,雖說該屬性只有kCAGradientLayerAxial一個選擇,由數組中的各顏色產生線性過渡漸變。

具體含義是漸變過渡沿startPoint到endPoint的向量A方向產生,設B與A垂直,則各條B平行線上的所有點顏色相同。

031.gif

此外,locations屬性可以使用一個數組(元素取值范圍0到1),指定漸變圖層參照colors順序取用下一個過渡點顏色的位置。

未設定時默認會平均分配過渡點。一旦設定就必須與colors的數量保持一致,否則會出錯。 :[

下面是創建漸變圖層的例子:

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
}

上述代碼創建一個漸變圖層,框架設為someView邊界,指定顏色數組,設置起止點,添加圖層到視圖結構樹。效果如下:

032.png

五彩缤紛,姹紫嫣紅! 

圖層演示應用中,你可以隨意修改起止點、顏色和過渡點:

033.png

示例 #6:CAReplicatorLayer

CAReplicatorLayer能夠以特定次數復制圖層,可以用來創建一些很棒的效果。

每個圖層復件的顏色和位置都可以改動,而且可以在總復制圖層之後延遲繪制,營造一種動畫效果。還可以利用深度,創造三維效果。舉個例子

// 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")

以上代碼:

  • 創建一個CAReplicatorLayer實例,設框架為someView邊界。

  • 設復制圖層數instanceCount和繪制延遲,設圖層為2D(preservesDepth = false),實例顏色為白色。

  • 為陸續的實例復件設置RGB顏色偏差值(默認為0,即所有復件保持顏色不變),不過這裡實例初始顏色為白色,即RGB都為1.0,所以偏差值設紅色為0,綠色和藍色為相同負數會使其逐漸現出紅色,alpha透明度偏差值的變化也與此類似,針對陸續的實例復件。

  • 創建旋轉變換,使得實例復件按一個圓排列。

  • 創建供復制圖層使用的實例圖層,設置框架,使第一個實例在someView邊界頂端水平中心處繪制,另外設置實例顏色,把實例圖層添加到復制圖層。

  • 創建一個透明度由1(不透明)過渡為0(透明)的淡出動畫。

  • 設實例圖層透明度為0,使得每個實例在繪制和改變顏色與alpha前保持透明。

這段代碼會實現這樣的東西:

034.gif

圖層演示應用中,你可以改動這些屬性:

035.png

示例 #7:CATiledLayer

CATiledLayer以圖塊(tile)為單位異步繪制圖層內容,對超大尺寸圖片或者只能在視圖中顯示一小部分的內容效果拔群,因為不用把內容完全載入內存就可以看到內容。

處理繪制有幾種方法,一種是重寫UIView,使用CATiledLayer繪制圖塊填充視圖背景,如下:

// 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)
  }
 
}

代碼解釋:

  • tiledBackgroundView位於 (150, 150) ,寬高均為300。

  • 重寫layerClass(),令該視圖創建的圖層實例為CATiledLayer。

  • 設置rand48()的隨機數種子,用於在drawRect()中生成隨機顏色。CATiledLayer類型轉換,縮放圖層內容,設置圖塊尺寸,適應屏幕。

  • 重寫drawRect(),以隨機色塊填充視圖。

代碼繪制6×6隨機色塊方格,最終效果如下:

036.png

圖層演示應用中除此之外還可以在圖層背景上繪制軌跡:

037.png

在視圖中放大時,上述截圖中的星星圖案會變得模糊:

038.png

產生模糊的根源是圖層的細節層次(level of detail,簡稱LOD),CATiledLayer有兩個相關屬性:levelsOfDetail和levelsOfDetailBias。

levelsOfDetail顧名思義,指圖層維護的LOD數目,默認值為1,每進一級會對前一級分辨率的一半進行緩存,圖層的levelsOfDetail最大值,也就是最底層細節,對應至少一個像素點。

而levelsOfDetailBias指的是該圖層緩存的放大LOD數目,默認為0,即不會額外緩存放大層次,每進一級會對前一級兩倍分辨率進行緩存。

例如,設上述分塊圖層的levelsOfDetailBias為5會緩存2x、4x、8x、16x和32x的放大層次,放大的圖層效果如下:

039.png

不錯吧?別著急,還沒講完呢。

CATiledLayer裁刀,買不了吃虧,買不了上當,只要998…(譯注:此處內容稍作本地化處理,原文玩的是1978年美國Ginsu刀具的梗,堪稱詢價型電視購物廣告的萬惡之源。) :]

開個玩笑。CATiledLayer還有一個更實用的功能:異步繪制圖塊,比如在滾動視圖中顯示一張超大圖片。

在用戶滾動畫面時,要讓分塊圖層知道哪些圖塊需要繪制,寫代碼在所難免,不過換來性能提升也值了。

圖層演示應用的UIImage+TileCutter.swift中包含一個UIImage擴展,教程編纂組成員Nick Lockwood在著作iOS Core Animation: Advanced Techniques的一個終端應用程序中利用了這段代碼。

代碼的職責是把原圖片拆分成指定尺寸的方塊,按行列位置命名圖塊,比如第三行第七列的圖塊windingRoad62.png(索引從零開始)。

041.png

有了這些圖塊,我們可以自定義一個UIView子類,繪制分塊圖層:

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)
  }
 
}

以上代碼:

  • 創建屬性,分別是圖塊邊長、原圖文件名、供TileCutter擴展保存圖塊的緩存文件夾路徑。

  • 重寫layerClass()返回CATiledLayer。

  • 實現init(_:),把視圖的圖層轉換為分塊圖層,設置圖塊大小。注意此處不必設置contentsScale適配屏幕,因為是直接修改視圖自身的圖層,而不是手動創建子圖層。

  • 重寫drawRect(),按行列繪制各個圖塊。

像這樣,原圖大小的自定義視圖就可以塞進一個滾動視圖:

042.png

多虧CATiledLayer,滾動5120 x 3200的大圖也會這般順滑:

043.gif

如你所見,快速滾動時繪制圖塊的過程還是很明顯,你可以利用更小的分塊(上述例子中分塊為640 x 640),或者自己創建一個CATiledLayer子類,重寫fadeDuration()返回0:

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

示例 #8:CAShapeLayer

CAShapeLayer利用可縮放的矢量路徑進行繪制,繪制速度比使用圖片快很多,還有個好處是不用分別提供常規、@2x和@3x版本的圖片,好用。

另外還有各種屬性,讓你可以自定線粗、顏色、虛實、線條接合方式、閉合線條是否形成閉合區域,還有閉合區域要填充何種顏色等。舉例如下:

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)
  }
 
}

代碼解釋:

  • 創建顏色、路徑、圖形圖層對象。

  • 繪制圖形圖層路徑。如果不喜歡編寫生硬的繪圖代碼的話,你可以嘗試PaintCode這款軟件,可以利用簡便的工具進行可視化繪制,支持導入現有的矢量圖(SVG)和Photoshop(PSD)文件,並自動生成代碼。

  • 設置圖形圖層。路徑設為第二步中繪制的CGPath路徑,填充色設為第一步中創建的CGColor顏色,填充規則設為非零(non-zero),即默認填充規則。

  • 填充規則共有兩種,另一種是奇偶(even-odd)。不過示例代碼中的圖形沒有相交路徑,兩種填充規則的結果並無差異。

  • 非零規則記從左到右的路徑為+1,從右到左的路徑為-1,累加所有路徑值,若總和大於零,則填充路徑圍成的圖形。

  • 從結果上來講,非零規則會填充圖形內部所有的點。

  • 奇偶規則計算圍成圖形的路徑交叉數,若結果為奇數則填充。這樣講有些晦澀,還是有圖有真相:

右圖圍成中間五邊形的路徑交叉數為偶數,故中間沒有填充,而圍成每個三角的路徑交叉數為奇數,故三角部分填充顏色。

CAShapeLayerFillRules-480x256.png

  • 調用路徑繪制和圖層設置代碼,並把圖層添加到視圖結構樹。

上述代碼繪制raywenderlich.com的圖標:

RayWenderlichLogo-250x250.png

順便看看使用PaintCode的效果圖:

PaintCodeRayWenderlichLogo-480x265.png

圖層演示應用中,你可以隨意修改很多CAShapeLayer屬性:

CAShapeLayer_5.5.png

注:我們先跳過演示應用中的下一個示例,因為CAEAGLLayer多少顯得有些過時了,iOS 8 Metal框架有更先進的CAMetalLayer。在此推薦iOS 8 Metal入門教程。

示例 #9:CATransformLayer

CATransformLayer不像其他圖層類一樣把子圖層結構平面化,故適宜繪制3D結構。變換圖層本質上是一個圖層容器,每個子圖層都可以應用自己的透明度和空間變換,而其他渲染圖層屬性(如邊寬、顏色)會被忽略。

變換圖層本身不支持點擊測試,因為無法直接在觸摸點和平面坐標空間建立映射,不過其中的子圖層可以響應點擊測試,例如:

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)
  }
 
}

上述代碼解釋:

  • 創建屬性,分別為立方體的邊長、每個面的顏色,還有一個變換圖層。

  • 創建六個面,旋轉後添加到變換圖層,構成立方體,然後設置變換圖層的z軸錨點,旋轉立方體,將其添加到視圖結構樹。

  • 輔助代碼,用來創建指定顏色的面,還有角度和弧度的轉換。在變換代碼中利用弧度轉換函數在某種程度上可以增加代碼可讀性。 :]

  • 基於指定xy偏移的旋轉,注意變換應用對象設為sublayerTransform,即變換圖層的子圖層。

  • 監聽觸摸,遍歷變換圖層的子圖層,對每個圖層進行點擊測試,一旦成功相應立即跳出循環,不用繼續遍歷。

  • 設置變換圖層,添加到視圖結構樹。

注:currentTransform.m##是啥?問得好,是CATransform3D屬性,代表矩陣元素。想學習如上代碼中的矩陣變換,請參考RW教程組成員Rich Turton的三維變換娛樂教學,還有Mark Pospesel的初識矩陣項目。

在250 x 250的someView視圖中運行上述代碼結果如下:

CATransformLayer-250x250.png

再試試點擊立方體的任意位置,控制台會輸出“Transform layer tapped!”信息。

圖層演示應用中可以調整透明度,此外Bill Dudney軌跡球工具, Swift移植版可以基於簡單的用戶手勢應用三維變換。

CATransformLayer_5.5.png

示例 #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.創建粒子發射器圖層和粒子胞(Creates an emitter layer and cell.)。

2.按照下方步驟設置粒子發射器圖層:

  • 為隨機數生成器提供種子,隨機調整粒子胞的某些屬性,如速度。

  • 在圖層背景色和邊界之上按renderMode指定的順序渲染粒子胞。

注:渲染模式默認為無序(unordered),其他模式包括舊粒子優先(oldest first),新粒子優先(oldest last),按z軸位置從後至前(back to front)還有疊加式渲染(additive)。

  • 由於粒子發射器需要反復重繪大量粒子胞,設drawsAsynchronously為true會提升性能。

  • 然後借助第四條中會提到的輔助方法設置發射器位置,這個例子有助於理解把drawsAsynchronously設為true為何能夠提升性能和動畫流暢度。

3.這段代碼設了不少東西。

  • 配置粒子胞,設內容為圖片(圖片在圖層演示項目中)。

  • 指定初速及其變化量范圍(velocityRange),發射器圖層利用上面提到的隨機數種子創建隨機數生成器,在范圍內產生隨機值(初值+/-變化量范圍),其他以“Range”結尾的相關屬性的隨機化規則類似。

  • 設顏色為黑色,使自變色(variance)與默認的白色形成對比,白色形成的粒子亮度過高。

  • 利用隨機化范圍設置顏色,指定自變色范圍,顏色速度值表示粒子胞生命周期內顏色變化快慢。

  • 接下來這幾行代碼指定粒子胞分布范圍,一個全圓錐。設置粒子胞轉速和發射范圍,發射范圍emissionRange屬性的弧度值決定粒子胞分布空間。

  • 設粒子胞生命周期為1秒,默認值為0,表示粒子胞不會出現。birthRate也類似,以秒為單位,默認值為0,為使粒子胞顯示出來,必須設成正數。

  • 最後設xy加速度,這些值會影響已發射粒子的視角。

4.把角度轉換成弧度的輔助方法,還有設置粒子胞位置為視圖中點。

5.設置發射器圖層和粒子胞,把粒子胞添加到圖層,然後把圖層添加到視圖結構樹。

6.iOS 8的新方法,處理當前設備形態集(trait collection)的變化,比如設備旋轉。不熟悉形態集的話可以參閱iOS 8教程。 

總算說完了!信息量很大,但相信各位聰明的讀者可以高效吸收。

上述代碼運行效果如下:

CAEmitterLayer2.gif

圖層演示應用中,你可以隨意調節很多屬性:

CAEmitterLayer_5.5.png

 

何去何從?

victorious-e1419192669236.png

恭喜,看完十則示例和各種圖層子類,CALayer之旅至此告一段落。

但現在才剛剛開始!新建一個項目,或者打開已有項目,嘗試利用圖層提升性能或營造酷炫效果!實踐出真知。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved