直播项目笔记(四)

心跳包 + 图文混排 + 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)

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

推荐阅读更多精彩内容