3

iOS直播:评论框与粒子系统点赞动画

 3 years ago
source link: https://zackzheng.info/2016/07/31/ios-live-comment-praise/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

iOS直播:评论框与粒子系统点赞动画

2016-07-31

| iOS

| 2453

主要介绍了iOS直播的评论框、粒子系统点赞动画的实现
  • 评论框
    • 从下往上显示
    • 支持昵称颜色
    • 给出NSAttributedString

最近做了直播功能,其实难度不是说很大,主要是方案和SDK的选择、整个直播流程的异常处理和优化,还有第三方SDK的填坑。不过本文只是记录下评论框和点赞效果的实现,其他的是用第三方SDK,觉得没什么好分享的,只是了解了直播流程和开发中会遇到的问题。
但看到效果还是蛮激动和蛮有成就感的,这个主要是技术本身带来的。

2016-07-31-ios-live-comment-praise-1.gif

细化需求:

  1. 显示评论内容
  2. 从下往上显示
  3. 最大支持1000条
  4. 不同人昵称显示颜色随机分配,同一个人颜色保持不变。
  5. 评论插入有动画
  • 新的类MessageChatView,对外接口add
1
func add(message: String) {}
  • 存放评论数组
1
2
private let maxMessageCount: Int = 1000
private var messages: [String] = []
  • UITableViewDelegate & UITableViewDataSource
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
extension MessageChatView: UITableViewDataSource {
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return messages.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
return cell
}
}
extension MessageChatView: UITableViewDelegate {
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return ...
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 5.0
}
}

此时显示了数组里面的评论,最多1000条。

1
2
3
4
5
6
7
8
func add(message: String) {
messages.insert(message, atIndex: 0)
tableView.insertSections(NSIndexSet(index: 0), withRowAnimation: .Top)
if messages.count > maxMessageCount {
messages.removeLast()
tableView.deleteSections(NSIndexSet(index: messages.count), withRowAnimation: .None)
}
}

使用UITableView自带的方法可以有动画效果。插入动画使用.Top

从下往上显示

  • iOS在tableView和tableViewCell里调用下面语句:
1
tableView.transform = CGAffineTransformMakeScale (1,-1);
1
label.transform = CGAffineTransformMakeScale (1,-1);

两条语句就可以实现了。

  • Android可以调用ListView自带的属性stackFromBottom
1
android:stackFromBottom="true"

网上有文章将数据 append到数据源,在获取数据源时从后往前读的方式(即messages.count-1-indexPath.section),显然插入在0位置比那样更方便:insert(message, atIndex: 0)

支持昵称颜色

  • 使用NSAttributedString,且由外界设置。messages类型改为NSAttributedString数组。
1
private var messages: [NSAttributedString] = []
  • add改为NSAttributedString。
1
func add(message: NSAttributedString) {}
  • 设置Label的时候设置label.attributedText。

给出NSAttributedString

  • 一个新的类ChatColorText,对外接口colorText,参数nickName、text。
1
func colorText(nickName: String?, text: String?) -> NSAttributedString?{}
  • 随机颜色数组。
1
2
3
4
5
6
7
8
9
private var colors = [
UIColor(hex: .RGB00AEFF)!,
UIColor(hex: .RGB00A61C)!,
UIColor(hex: .RGB5400E6)!,
UIColor(hex: .RGBFF3377)!,
UIColor(hex: .RGBFF8800)!,
UIColor(hex: .RGBFF5E00)!,
UIColor(hex: .RGBCA2EE6)!,
]
  • 记录当前取颜色的Index,使得不同人给不同颜色。
1
private var colorIndex: Int = 0
  • 记录昵称对应的颜色值,保证同一个昵称同一种颜色。
1
private var dicOfNameAndColor = [String: UIColor]()
  • 对外接口colorText实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func colorText(nickName: String?, text: String?) -> NSAttributedString? {
guard let nickName = nickName, text = text else {return nil}
let nickNameColor: UIColor = {
if let color = dicOfNameAndColor[nickName] {
return color
}else {
let color = colors[colorIndex]
dicOfNameAndColor[nickName] = color
colorIndex = (colorIndex + 1) % colors.count
return color
}
}()
let attributedString = NSAttributedString.attributedStringWithTextsAndColors([nickName, text], colors: [nickNameColor, UIColor(hex: .RGB333333)!])
return attributedString
}

NSAttributedString.attributedStringWithTextsAndColors是自己扩展的一个方法,传入多串文字和对应的字符返回匹配的NSAttributedString

主要逻辑是:先判断是否已经有保存过昵称对应的颜色值,有则直接返回;没有则根据index获取颜色值,然后保存起来,并改变index

iOS自带了粒子引擎的类CAEmitterLayer,是一个粒子发射器系统,每个粒子都是CAEmitterCell的实例。可以查看它们分别有什么属性。

有两个小点,一个是CAEmitterLayer一些属性对CAEmitterCell有成倍作用,如birthRate;另一个是没有明确的停止动画的方法,包括它的父类也没提供。可以想到的方法,除了把layer抹除掉之外,还可以将CAEmitterLayerbirthRate设置为0,这样每个CAEmitterCell的诞生速率都为0,就不会有动画了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class PraiseEmitterView: UIView {

private var timer: NSTimer?
private let emitter: CAEmitterLayer! = {
let emitter = CAEmitterLayer()
return emitter
}()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
emitter.frame = bounds
emitter.birthRate = 0
emitter.emitterShape = kCAEmitterLayerLine
emitter.emitterPosition = CGPointMake(0,CGRectGetHeight(bounds))
emitter.emitterSize = bounds.size
emitter.emitterCells = [getEmitterCell(UIImage(named: "comment")!.CGImage!), getEmitterCell(UIImage(named: "flower_15")!.CGImage!)]
self.layer.addSublayer(emitter)
}
func timeoutSelector() {
emitter.birthRate = 0
}
func emit() {
emitter.birthRate = 2
timer?.invalidate()
timer = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: #selector(timeoutSelector), userInfo: nil, repeats: false)
}
private func getEmitterCell(contentImage: CGImage) -> CAEmitterCell {

let emitterCell = CAEmitterCell()
emitterCell.contents = contentImage
emitterCell.lifetime = 2
emitterCell.birthRate = 2

emitterCell.yAcceleration = -70.0
emitterCell.xAcceleration = 0

emitterCell.velocity = 20.0
emitterCell.velocityRange = 200.0

emitterCell.emissionLongitude = CGFloat(0)
emitterCell.emissionRange = CGFloat(M_PI_4)

emitterCell.scale = 0.8
emitterCell.scaleRange = 0.8
emitterCell.scaleSpeed = -0.15

emitterCell.alphaRange = 0.75
emitterCell.alphaSpeed = -0.15

return emitterCell
}
}

-END-
欢迎到我的博客交流:https://zackzheng.info


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK