import UIKit
protocol Emitterable {
}
//这里的 where Self : UIViewController 意思是对使用该效果必须继承自UIViewController
extension Emitterable where Self : UIViewController {
func start() {
// 1.创建发射器
let emitter = CAEmitterLayer()
// 2.设置发射器的位置
emitter.emitterPosition = CGPoint(x: view.bounds.width * 0.5, y: -60)
// 3.开启三维效果
emitter.preservesDepth = true
// 4.创建例子, 并且设置例子相关的属性
// 4.1.创建例子Cell
let cell = CAEmitterCell()
// 4.2.设置粒子速度
cell.velocity = 150
cell.velocityRange = 100
// 4.3.设置例子的大小
cell.scale = 0.7
cell.scaleRange = 0.3
// 4.4.设置粒子方向
cell.emissionLongitude = CGFloat(M_PI_2)
cell.emissionRange = CGFloat(M_PI_2 / 2)
// 4.5.设置例子的存活时间
cell.lifetime = 6
cell.lifetimeRange = 1.5
// 4.6.设置粒子旋转
cell.spin = CGFloat(M_PI_2)
cell.spinRange = CGFloat(M_PI_2 / 2)
// 4.6.设置例子每秒弹出的个数
cell.birthRate = 20
// 4.7.设置粒子展示的图片
cell.contents = UIImage(named: "good6_30x30")?.cgImage
// 5.将粒子设置到发射器中
emitter.emitterCells = [cell]
// 6.将发射器的layer添加到父layer中
view.layer.addSublayer(emitter)
}
func stop() {
/*
for layer in view.layer.sublayers! {
if layer.isKind(of: CAEmitterLayer.self) {
layer.removeFromSuperlayer()
}
}
*/
view.layer.sublayers?.filter({ $0.isKind(of: CAEmitterLayer.self)}).first?.removeFromSuperlayer()
}
}
调用很简单:
class ViewController: UIViewController , Emitterable {
override func viewDidLoad() {
super.viewDidLoad()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.start()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.stop()
}
}
}
效果:
import UIKit
/************************** 字典定义 *****************************/
// Swift中字典的类型Dictionary
// 不可变字典使用let修饰
// 可变字典使用var修饰
// 注意:字典在创建时使用[]
let dict : Dictionary = ["name" : "JJCoder", "age" : 18] as [String : Any]
// dict = ["name" : "lmj", "age" : 20]
let dict1 : Dictionary<String, AnyObject> = ["name" : "jack" as AnyObject, "age" : 20 as AnyObject]
// 开发中常见写法
let dict2 : [String : AnyObject] = ["name" : "jack" as AnyObject, "age" : 22 as AnyObject]
// 类型推导
let dict3 = ["name" : "jjcoder", "age" : 26] as [String : Any]
// 可变字典
var dict4 = [String : AnyObject]()
/************************** 对可变字典的操作 *****************************/
// 1.在字典中添加元素
dict4["name"] = "lmj" as AnyObject?
dict4
dict4["age"] = 26 as AnyObject?
dict4
dict4["height"] = 1.74 as AnyObject?
dict4
// 2.从字典中移除元素
let age = dict4.removeValue(forKey: "age")
// 3.修改字典中的值
// 注意:通过一个键来修改字典中的值,如果存在这个键则修改.如果不存在就会添加新的键值对
dict4["weight"] = 62.0 as AnyObject?
dict4
dict4["height"] = 1.74 as AnyObject?
dict4
// 4.获取值
// let height = dict4["height"]! as! Double
let height = dict4["height"]!
print(height)
/************************** 对可变字典的遍历 *****************************/
// 1.遍历字典中所有的键
for key in dict4.keys {
print(key)
}
// 2.遍历字典中所有的值
for value in dict4.values {
print(value)
}
// 3.遍历字典中的键值对
for (key, value) in dict4 {
print(key)
print(value)
}
/************************** 字典的合并 *****************************/
var d1 = ["name" : "jjcoder", "age" : 20] as [String : Any]
var d2 = ["height" : 174, "phoneNum" : "+86 15210100335", "name" : "majianjie"] as [String : Any]
// 两个字典,即时类型一致也不可以彼此相加
// var d3 = d1 + d2
// 合并过程中,如果没有对应的键,添加对应的键值对
// 如果有队要你管的键,则修改原有的值
for (key, value) in d1 {
d2[key] = value
}
>Swift中的元组
import UIKit
// 元祖也是一个数据集合,可以在集合中定义一组数据
// 元祖的定义使用:(元素1,元素2)
("1001", "JJCoder", 19, 1.74)
(id : "1001", name : "张三", age : 26, height : 1.74)
// 用一个数据类型来描述网络请求错误
// errorCode/errorInfo
[404, "Not Found"] as [Any]
["errorCode" : 404, "errorInfo" : "Not Found"] as [String : Any]
// 用元祖描述错误信息
let error = (404, "Not Found")
error.0
error.1
let error1 = (errorCode : 404, errorInfo : "Not Found")
error1.errorCode
error1.errorInfo
switch (error1) {
case (404, "Not Found"):
print("没有找到host")
default :
print("其他错误")
}
let (errorCode2, errorInfo2) = (404, "Not Found")
errorCode2
errorInfo2
map : 可以对数组中的每一个元素做一次处理
如果求一个数组中字符串的长度可以用 map来处理
//方法1
let array = ["Objective-C", "HTML", "CSS"]
//定义方法 返回传入的字符串的长度
func calcuCount(string: String) -> Int {
return string.characters.count
}
//使用 打印
print(array.map(stringCount))
// 方法2
print(fruits.map{return $0.characters.count})
reduce:可以对数组中的元素进行计算
// 将数组中的每个字符串用‘-’拼接
let array = ["Objective-C", "HTML", "CSS"]
func appendString(string1: String, string2: String) -> String {
return string1 == "" ? string2 : string1 + "-" + string2
}
// reduce方法中的第一个参数是初始值
print(array.reduce("第一个字符串", appendString))//打印第一个字符串-Objective-C-HTML-CSS
// $0表示计算后的结果, $1表示数组中的每一个参数
print(array.reduce("", {
return $0 == "" ? $1 : $0 + "-" + $1
}))
//打印 Objective-C-HTML-CSS
filer:过滤,可以对数组中的元素按照某种规则进行一次过滤
let stringArray = ["MJ-C", "Swift", "Cocos2d-x", "Python", "JavaScript"]
func stringCountLess10(string: String) -> Bool {
return string.characters.count < 10
}
stringArray.filter(stringCountLess10)
stringArray.filter({string -> Bool in
return string.characters.count < 10
})
// $0表示数组中的每一个元素
stringArray.filter{
return $0.characters.count < 10
}
后期会陆续更新....
flatmap 可以把数组继续 '切割'
// 用flatMap
let arrayFlatMap = arraya.flatMap { $0 }
print(arrayFlatMap)
// [1, 2, 3, 4, 5, 6, 7, 8, 9]
>Swift类构造函数
import UIKit
/*
1.自定义构造函数时会覆盖原有的构造函数
如果不希望覆盖,则可以明确的将原来的构造函数写出
*/
class Person {
var name : String = ""
var age : Int = 0
init() {
}
init(name : String, age : Int) {
self.name = name
self.age = age
}
init(dict : [String : AnyObject]) {
if let name = dict["name"] as? String {
self.name = name
}
if let age = dict["age"] as? Int {
self.age = age
}
}
}
// 1.使用init()构造函数创建对象
let p = Person()
p.name = "jjcoder"
p.age = 18
// 2.使用init(name : String, age : Int)创建对象
let p1 = Person(name: "mjj", age: 19)
// 3.通过init(dict : [String : AnyObject])方法创建对象
let p2 = Person(dict: ["name" : "mjj" as AnyObject, "age" : 26 as AnyObject])
>Swift中的可选类型
import UIKit
// nil 只能赋值给可选类型,不能赋值给其他的任意类型
// var name : String = "why"
// name = nil
var view : UIView = UIView()
// view = nil
/*
// 1.可选类型如何定义
var name : Optional<String> = nil
// 2.给可选类型进行赋值
name = Optional("123")
*/
// 1.可选类型如何定义
var name : String? = nil
// 2.给可选类型进行赋值
name = "123"
// 3.取出可选类型中值 : 需要对可选类型进行解包
// print(name)
print(name!)
// 4.可选类型 + ! --> 强制解包
// 如果可选可选类型为nil,那么强制解包程序就会崩溃
// 5.可选绑定 : 1> 判断可选类型有没有值, 如果没有值,那么{}不执行 2> 如果有值, 会先对name进行解包, 并且将解包后的结果赋值给前面的标识符
if let name = name {
print(name)
}
Swift中的类型分为两种 :
- 值类型
每个值类型的实例都拥有各自唯一的数据,通常它们是结构体,枚举或元组
Array、String和Dictionary都是值类型
- 引用类型
引用类型的实例共享它们的数据,通常是一个类。
class 是引用类型
区别?
值类型最基本的特征就是复制在赋值、初始化和传递参数过程中的数据,并为这个数据创建一个独立
的实例
下面举2个粟子:🙄
// 值类型例子
struct MJJ { var data: Int = 1 }
var a = MJJ()
var b = a // 把a赋值给b
a.data = 42 // a被改变了, b却没有
println("\(a.data), \(b.data)") // prints "42, 1"
// 引用类型的例子
class JJCoder { var data: Int = -1 }
var x = JJCoder()
var y = x // x被复制给了y
x.data = 4 // x指向的数据被修改了 (同时y也被修改了)
println("\(x.data), \(y.data)") // prints "4, 4"
其实是隐式地创建了一个共享的实例。在赋值后,两个实例指向了同一块数据,所以当修改其中一个实例数据的时候,另一个实例的数据也被修改了
安全性方面考虑😀
选择值类型而不是引用类型的一个主要原因是能让你的代码变得更加简单。你在任何情况下用一个值类型,都能够假设你的其他代码不会使它改变,这通常在多线程环境中很有用,如果一个线程中使用的数据被另一个线程给意外的修改了,这通常会产生非常严重的Bug,且相当难以调试。
由于只有当你需要修改数据时两者的区别才会得到体现,所以当你的实例不会对数据进行修改的时候,值类型和引用类型看起来是完全相同的。
你也许会想,写一个完全不可变的类,这或许是有价值的,使用Cocoa的NSObject能简化这个过程,并且能很好地保持原有的语义。现在,你能通过使用不可变的存储属性,以及避免暴露修改数据的接口,从而在Swift里实现一个不可变的类。事实上,大多数的Cocoa类,比如NSURL等,都被设计为不可变的类,然而,Swift当前并没有提供任何语言机制去强制申明一个类不可改变(比如子类化就能修改一个类的实现),只有结构体和枚举才是强制不可变的。
如何抉择:😝
使用值类型:
1. 通过使用==去比较实例的数据
2. 想得到一个实例的独立副本
3. 数据在多线程环境下被修改
使用引用类型(比如使用一个类):
1. 通过使用===去判断两个实例是否恒等
2. 你想要创建一个共享的,可变的对象
iOS默认是不支持播放GIF图片的,但是系统也没有禁止播放它,该有的API还是有的,下面就 来一发吧!
一定要导入ImageIO 这个 库
//首先你得有个imageview吧~~~~~
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// 1️⃣.在本地加载Gif图片(不要放在imageassert中,其实也放不进去~~`), 并且转成Data类型(这里不再写swift语法,guard 做一下校验...)
guard let path = Bundle.main.path(forResource: "demo.gif", ofType: nil) else { return }
guard let data = NSData(contentsOfFile: path) else { return }
// 2️⃣. 将data转成CGImageSource对象,返回CGImageSource
guard let imageSource = CGImageSourceCreateWithData(data, nil) else { return }
//3️⃣通过CGImageSource可以拿到分解了多少张图片(看成是一帧一帧的)
let imageCount = CGImageSourceGetCount(imageSource)
// 4️⃣遍历所有的图片
var images = [UIImage]()
var totalDuration : TimeInterval = 0
for i in 0..<imageCount {
// 3️⃣.1️⃣取出图片
guard let cgImage = CGImageSourceCreateImageAtIndex(imageSource, i, nil) else { continue }
let image = UIImage(cgImage: cgImage)
if i == 0 {
imageView.image = image
}
//3️⃣.2️⃣加入到一个images的数组中
images.append(image)
// 3️⃣.3️⃣取出持续的时间(这里记住就可以了)
guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil) as? NSDictionary else { continue }
guard let gifDict = properties[kCGImagePropertyGIFDictionary] as? NSDictionary else { continue }
guard let frameDuration = gifDict[kCGImagePropertyGIFDelayTime] as? NSNumber else { continue }
totalDuration += frameDuration.doubleValue
}
// 4️⃣.给imageView设置属性
imageView.animationImages = images
imageView.animationDuration = totalDuration
imageView.animationRepeatCount = 0
// 5️⃣.开始播放
imageView.startAnimating()
}