心跳包 + 图文混排 + Core Graphics
Socket 服务器加入心跳包
Timer 和 Runloop
Timer只负责计时,Timer所绑定的动作,是由Runloop来执行的,Runloop运行的时候需要指定运行模式,一个timer同一时间只可以注册到一个runloop中,但是可以添加到这个runloop的多个运行模式中
两个创建Timer的区别
test1 //计时器会在当前循环最后进行 先输出haha才开始计数
override func viewDidLoad() {
super.viewDidLoad()
// 创建 timer
timer = Timer(timeInterval: 1, target: self, selector: #selector(test), userInfo: nil, repeats: true)
/*
创建一个 timer 加入到当前 runloop 默认模式
timer = Timer(timeInterval: 1, target: self, selector: #selector(test), userInfo: nil, repeats: true)
*/
// 将 timer 加入到 Runloop 中
RunLoop.current.add(timer, forMode: .commonModes)
print("haha")
}
@objc func test() {
count += 1
print("\(count)")
}
test2 //计时器手动开始执行 fire() 先开始第一次计数再输出haha
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer(fireAt: Date(), interval: 1, target: self, selector: #selector(test), userInfo: nil, repeats: true)
// 将 timer 加入到 Runloop 中
RunLoop.current.add(timer, forMode: .commonModes)
timer.fire()
print("哈哈")
}
@objc func test() {
count += 1
print("\(count)")
}
添加聊天内容
正则表达式
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码
- 常用元字符
\ba\w*\b匹配以字母a开头的单词——先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)。
\d+匹配1个或更多连续的数字。这里的+是和*类似的元字符,不同的是*匹配重复任意次(可能是0次),而+则匹配重复1次或更多次。
\b\w{6}\b 匹配刚好6个字符的单词。
代码 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线或汉字 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$
- 字符转义
如果你想查找元字符本身的话,比如你查找.,或者*,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。因此,你应该使用\.和\*。当然,要查找\本身,你也得用\
在swift中 转义字符是 \\
- 重复
代码/语法 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
- 字符类 [ ]
[aeiou]匹配任何一个英文元音字母
[.?!]匹配标点符号(.或?或!)
- 分歧条件 |
\d{5}-\d{4}|\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了
富文本(NSAttributedString)
- 给字符串特定字符换颜色
let str = "小明:哈哈哈哈"
label1.text = str
let attr = NSMutableAttributedString(string: str)
// NSAttributedStringKey里还有更多可修改的属性
attr.addAttributes([NSAttributedStringKey.foregroundColor: UIColor.orange], range: NSRange(location: 0, length: 2))
label2.attributedText = attr
- 图文混排
let str = "小明:[鄙视]你怎么这样[呲牙]"
label1.text = str
let attr = NSMutableAttributedString(string: str)
// 正则表达式 匹配表情
let pattern = "\\[.*?\\]"
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let results = regex.matches(in: str, options: [], range: NSRange(location: 0, length: str.count))
// 获取表情的结果 从后往前匹配 避免打乱前面字符串的range
for i in (0..<results.count).reversed() {
let result = results[i]
let emoticonName = (str as NSString).substring(with: result.range)
let image = UIImage(named: emoticonName)
let attachment = NSTextAttachment()
attachment.image = image
let font = UIFont.systemFont(ofSize: 15)
attachment.bounds = CGRect(x: 0, y: -3, width: font.lineHeight, height: font.lineHeight)
let imageAttrStr = NSAttributedString(attachment: attachment)
attr.replaceCharacters(in: result.range, with: imageAttrStr)
}
label2.attributedText = attr
礼物动画展示
Core Graphics
1. 什么是Core Graphics
Core Graphics Framework是一套基于C的API框架,使用了Quartz作为绘图引擎。它提供了低级别、轻量级、高保真度的2D渲染。该框架可以用于基于路径的绘图、变换、颜色管理、脱屏渲染,模板、渐变、遮蔽、图像数据管理、图像的创建、遮罩以及PDF文档的创建、显示和分析
2. 绘图的一般步骤
(1)获取绘图上下文
(2)创建并设置路径
(3)将路径添加到上下文
(4)设置上下文状态(如笔触颜色、宽度、填充色等等)
(5)绘制路径
3. 绘制图形
override func draw(_ rect: CGRect) {
super.draw(rect)
let context = UIGraphicsGetCurrentContext() // 获取上下文 创建画布
let path = CGMutablePath() // 创建路径
path.move(to: CGPoint(x: 20, y: 50)) // 移动到指定位置(设置路径起点)
path.addLine(to: CGPoint(x: 20, y: 100)) // 绘制直线(从起始位置开始)
context?.addPath(path) // 把路径添加到上下文(画布)中
// Core Graphics中还提供了很多预先设置好的路径
// CGContext.add()
// 设置图形上下文状态属性
context?.setStrokeColor(UIColor.blue.cgColor) // 设置笔触颜色
// 阴影 虚线 填充色 顶点 连接点 ...
// 填充类型中可以选择只绘制边框、只填充、同时绘制边框和填充内部区域、奇偶规则填充等
context?.setTextDrawingMode(.stroke) // 填充类型
// 绘制路径
context?.strokePath()
// OC的ARC机制并不会对它进行内存管理,但是Swift对它自动进行了,所以在Swift中不需要写这个代码 CGPathRelease(path)
}
Keyframe动画
Keyframe动画可以让我们有效的拆分由若干段动画连接而成的复杂动画,可以较为精准的定义每段动画的起始点及持续时间,并且在代码组织方面也非常清晰
UIView.animateWithDuration(1, animations: {
view.center.x += 200.0
}, completion: { _ in
UIView.animateWithDuration(1, animations: {
view.center.y += 100.0
}, completion: { _ in
UIView.animateWithDuration(1, animations: {
view.center.x -= 200.0
}, completion: { _ in
UIView.animateWithDuration(1, animations: {
view.center.y -= 100.0
}, completion: nil)
})
})
})
// or
UIView.animateKeyframesWithDuration(2, delay: 0, options: [], animations: {
// relativeDuration 是总动画时长的百分比
UIView.addKeyframeWithRelativeStartTime(0, relativeDuration: 0.25, animations: {
view.center.x += 200.0
})
UIView.addKeyframeWithRelativeStartTime(0.25, relativeDuration: 0.25, animations: {
view.center.y += 100.0
})
UIView.addKeyframeWithRelativeStartTime(0.5, relativeDuration: 0.25, animations: {
view.center.x -= 200.0
})
UIView.addKeyframeWithRelativeStartTime(0.75, relativeDuration: 0.25, animations: {
view.center.y -= 100.0
})
}, completion: nil)
- 礼物连击动画
UIView.animateKeyframes(withDuration: 0.25, delay: 0, options: [], animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.5, animations: {
self.transform = CGAffineTransform(scaleX: 3.0, y: 3.0)
})
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5, animations: {
self.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)
})
}, completion: { isFinished in
UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 10, options: [], animations: {
self.transform = CGAffineTransform.identity
}, completion: { (isFinished) in
complection()
})
})
取消延迟执行函数
// 取消一个对象在当前Run Loop中的所有未执行的
NSObject.cancelPreviousPerformRequests(withTarget: self)
self.perform(#selector(self.test), with: self, afterDelay: 3.0)
// 调用取消执行函数后 test方法不会执行
NSObject.cancelPreviousPerformRequests(withTarget: self)