使用粒子动画,让你的APP下点儿雪

又是一年

废话

最近气温骤降,分分钟感觉要下雪的样子,一想到雪,就想到过年了,一想到过年我就...

然而都过年了,我还没挣到钱...

算了,还是想想雪吧…

如果能使用代码,让自己的App下点儿雪,想想都cool到没朋友了

唉,不废话了,先看看效果

效果

具体实现

代码实现方面其实没有啥技巧,咱们就直接上

要实现粒子动画需要使用一个叫CAEmitterLayer的类,这个东西在QuartzCore框架里面,是一个直接作用与layer层的框架

相关代码:

class SnowView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame) 
        
        let emitter = self.layer as! CAEmitterLayer // 修改view的layer
        emitter.emitterPosition = CGPoint(x: bounds.size.width/2, y: 0) // 发射粒子的位置
        emitter.emitterSize = bounds.size // 范围
        emitter.emitterShape = kCAEmitterLayerRectangle // 粒子形状
        
        let emitterCell = CAEmitterCell() // 创建粒子
        emitterCell.contents = UIImage(named: "flake")!.cgImage // 载入粒子图片
        emitterCell.birthRate = 200 // 每秒释放多少个粒子
        emitterCell.lifetime = 3.5 // 每个粒子的生命周期
        emitterCell.color = UIColor.white.cgColor // 粒子的颜色
        emitterCell.redRange = 0.0 // RGBA设置
        emitterCell.blueRange = 0.1
        emitterCell.greenRange = 0.0
        emitterCell.alphaRange = 0.5
        emitterCell.velocity = 9.8 // 重力加速度也就是物理里面G
        emitterCell.velocityRange = 350 // 加速范围
        emitterCell.emissionRange = CGFloat(M_PI_2) // 下落是旋转的角度
        emitterCell.emissionLongitude = CGFloat(-M_PI) //
        emitterCell.yAcceleration = 70 // 发射速度
        emitterCell.xAcceleration = 0  
        emitterCell.scale = 0.33 
        emitterCell.scaleRange = 1.25
        emitterCell.scaleSpeed = -0.25
        emitterCell.alphaRange = 0.5
        emitterCell.alphaSpeed = -0.15
        
        emitter.emitterCells = [emitterCell] // 载入
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override class var layerClass: AnyClass {
        return CAEmitterLayer.self
    }
}

上面这段代码,是雪花粒子动画的完整实现,注释写的很清楚,这里需要注意的就是后面这几句

override class var layerClass: AnyClass {
    return CAEmitterLayer.self
}

这里一定要重载layer属性,不然开头第一句的强转会崩溃,你懂的

let emitter = self.layer as! CAEmitterLayer

行了,下雪了...

根据需求改变

可如果代码就这么实现,就安心了,太不符合程序员的逼格了

在实际的开发中可能会遇到各种问题:

比如产品经理说:我要做到想在哪里下雪就在那里下雪...

WTF!!!

产品经理就是这么任性

怎么办?

通常的处理方法有以下几种:

  • 子类化
  • 写到分类/扩展里面

如果你能想到子类化,说明你是有面向对象思想的(当然有没有对象就不知道了😜)

如果你能想写到分类里面,说明你对iOS编程已经有些入门了

但是我想说,以上两种方法都是最佳的解决办法.

为啥不用继承,请出门左转看看这篇文章

为啥不写成分类/扩展,请出门右转...

嗯?那个同学你回来,这个不需要出门右转,直接跟你说为啥

其实,写到分类/扩展里面是个不错的选择,但是随着开发的进行,你会发现分类/扩展里面的东西会越来越多,慢慢的成了一个大熔炉,啥东西都放在里面,最后成了一个垃圾桶,难以维护,甚至到最后你自己都找不到自己之前写的东西在哪里,再最后,你连自己是否写过这个方法都不知道了...

这里最佳的实现方法是,使用面向协议的方法解决这个问题,这样不但清晰,而且更swift

面向协议

至于啥是面向协议,请看下图

百度和Google在向你招手

行了,直接上代码

import Foundation
import UIKit
import QuartzCore

protocol Snow {}

extension Snow where Self: UIView {
    
    func createSnow() {
        let emitter = layer as! CAEmitterLayer
        emitter.emitterPosition = CGPoint(x: bounds.size.width / 2, y: 0)
        emitter.emitterSize = bounds.size
        emitter.emitterShape = kCAEmitterLayerRectangle
        
        let emitterCell = CAEmitterCell()
        emitterCell.contents = UIImage(named: "flake.png")!.cgImage
        emitterCell.birthRate = 200
        emitterCell.lifetime = 3.5
        emitterCell.color = UIColor.white.cgColor
        emitterCell.redRange = 0.0
        emitterCell.blueRange = 0.1
        emitterCell.greenRange = 0.0
        emitterCell.velocity = 10
        emitterCell.velocityRange = 350
        emitterCell.emissionRange = CGFloat(M_PI_2)
        emitterCell.emissionLongitude = CGFloat(-M_PI)
        emitterCell.yAcceleration = 70
        emitterCell.xAcceleration = 0
        emitterCell.scale = 0.33
        emitterCell.scaleRange = 1.25
        emitterCell.scaleSpeed = -0.25
        emitterCell.alphaRange = 0.5
        emitterCell.alphaSpeed = -0.15
        
        emitter.emitterCells = [emitterCell]
    }
}

这样以后,你的view代码就变成下面这样简单明了

import UIKit

class SnowView: UIView, Snow {

    override init(frame: CGRect) {
        super.init(frame: frame)
        
        createSnow()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override class var layerClass: AnyClass {
        return CAEmitterLayer.self
    }
}

以后产品经理想在那里下雪,你就让那个view遵循Snow协议,然后调用creatSnow(),就行了,自豪的告诉产品,他的理想已经实现

呃...

是不是跑题了?咱们不是在聊如何下雪么 ?怎么跑面向协议编程上去了?

行吧,不管如何,已经可以愉快的下雪了,真的快过年了...

项目源码奉上

然而,还没有挣到钱...

生命不息,折腾不止...
I'm not a real coder, but i love it so much!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容