您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
Swift语言iOS开发:CALayer十则示例(下)
 
作者:Raywenderlich 来源:51CTO 发布于 2015-04-08
   次浏览      
 

示例 #5:CAGradientLayer

CAGradientLayer简化了混合两种或更多颜色的工作,尤其适用于背景。要配置渐变色,你需要分配一个CGColor数组,以及标识渐变图层起止点的startPoint和endPoint。

注意:startPoint和endPoint并不是明确的点,而是用单位坐标空间定义,在绘制时映射到图层边界。也就是说x值为1表示点在图层右边缘,y值为1表示点在图层下边缘。

CAGradientLayer包含type属性,虽说该属性只有kCAGradientLayerAxial一个选择,由数组中的各颜色产生线性过渡渐变。

具体含义是渐变过渡沿startPoint到endPoint的向量A方向产生,设B与A垂直,则各条B平行线上的所有点颜色相同。

 

此外,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边界,指定颜色数组,设置起止点,添加图层到视图结构树。效果如下:

五彩缤纷,姹紫嫣红!

图层演示应用中,你可以随意修改起止点、颜色和过渡点:

示例 #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前保持透明。

这段代码会实现这样的东西:

图层演示应用中,你可以改动这些属性:

示例 #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随机色块方格,最终效果如下:

图层演示应用中除此之外还可以在图层背景上绘制轨迹:

在视图中放大时,上述截图中的星星图案会变得模糊:

产生模糊的根源是图层的细节层次(level of detail,简称LOD),CATiledLayer有两个相关属性:levelsOfDetail和levelsOfDetailBias。

levelsOfDetail顾名思义,指图层维护的LOD数目,默认值为1,每进一级会对前一级分辨率的一半进行缓存,图层的levelsOfDetail最大值,也就是最底层细节,对应至少一个像素点。

而levelsOfDetailBias指的是该图层缓存的放大LOD数目,默认为0,即不会额外缓存放大层次,每进一级会对前一级两倍分辨率进行缓存。

例如,设上述分块图层的levelsOfDetailBias为5会缓存2x、4x、8x、16x和32x的放大层次,放大的图层效果如下:

不错吧?别着急,还没讲完呢。

CATiledLayer裁刀,买不了吃亏,买不了上当,只要998…(译注:此处内容稍作本地化处理,原文玩的是1978年美国Ginsu刀具的梗,堪称询价型电视购物广告的万恶之源。) :]

开个玩笑。CATiledLayer还有一个更实用的功能:异步绘制图块,比如在滚动视图中显示一张超大图片。

在用户滚动画面时,要让分块图层知道哪些图块需要绘制,写代码在所难免,不过换来性能提升也值了。

图层演示应用的UIImage+TileCutter.swift中包含一个UIImage扩展,教程编纂组成员Nick Lockwood在著作iOS Core Animation: Advanced Techniques的一个终端应用程序中利用了这段代码。

代码的职责是把原图片拆分成指定尺寸的方块,按行列位置命名图块,比如第三行第七列的图块windingRoad62.png(索引从零开始)。

有了这些图块,我们可以自定义一个UIView子类,绘制分块图层:

mport 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(),按行列绘制各个图块。

像这样,原图大小的自定义视图就可以塞进一个滚动视图:

多亏CATiledLayer,滚动5120 x 3200的大图也会这般顺滑:

如你所见,快速滚动时绘制图块的过程还是很明显,你可以利用更小的分块(上述例子中分块为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,累加所有路径值,若总和大于零,则填充路径围成的图形。

从结果上来讲,非零规则会填充图形内部所有的点。

奇偶规则计算围成图形的路径交叉数,若结果为奇数则填充。这样讲有些晦涩,还是有图有真相:

右图围成中间五边形的路径交叉数为偶数,故中间没有填充,而围成每个三角的路径交叉数为奇数,故三角部分填充颜色。

调用路径绘制和图层设置代码,并把图层添加到视图结构树。

上述代码绘制raywenderlich.com的图标:

顺便看看使用PaintCode的效果图:

图层演示应用中,你可以随意修改很多CAShapeLayer属性:

注:我们先跳过演示应用中的下一个示例,因为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视图中运行上述代码结果如下:

再试试点击立方体的任意位置,控制台会输出“Transform layer tapped!”信息。

图层演示应用中可以调整透明度,此外Bill Dudney轨迹球工具, Swift移植版可以基于简单的用户手势应用三维变换。

示例 #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教程。

总算说完了!信息量很大,但相信各位聪明的读者可以高效吸收。

上述代码运行效果如下:

 

图层演示应用中,你可以随意调节很多属性:

   
次浏览       
 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]

android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...