本文始發(fā)于我的博文iOS直播:評(píng)論框與粒子系統(tǒng)點(diǎn)贊動(dòng)畫(huà)雳刺,現(xiàn)轉(zhuǎn)發(fā)至此掖桦。
目錄
- 前言
- 效果預(yù)覽
- 評(píng)論框
- 列表
- 添加評(píng)論
- 從下往上顯示
- 支持昵稱(chēng)顏色
- 給出NSAttributedString
- 點(diǎn)贊動(dòng)畫(huà)
前言
最近做了直播功能枪汪,其實(shí)難度不是說(shuō)很大,主要是方案和SDK的選擇宿稀、整個(gè)直播流程的異常處理和優(yōu)化祝沸,還有第三方SDK的填坑越庇。不過(guò)本文只是記錄下評(píng)論框和點(diǎn)贊效果的實(shí)現(xiàn)卤唉,其他的是用第三方SDK,覺(jué)得沒(méi)什么好分享的竭恬,只是了解了直播流程和開(kāi)發(fā)中會(huì)遇到的問(wèn)題萍聊。
但看到效果還是蠻激動(dòng)和蠻有成就感的寿桨,這個(gè)主要是技術(shù)本身帶來(lái)的。
效果預(yù)覽
評(píng)論框
細(xì)化需求:
- 顯示評(píng)論內(nèi)容
- 從下往上顯示
- 最大支持1000條
- 不同人昵稱(chēng)顯示顏色隨機(jī)分配,同一個(gè)人顏色保持不變墨微。
- 評(píng)論插入有動(dòng)畫(huà)
列表
- 新的類(lèi)
MessageChatView
扁掸,對(duì)外接口add
谴分。
func add(message: String) {}
- 存放評(píng)論數(shù)組
private let maxMessageCount: Int = 1000
private var messages: [String] = []
- UITableViewDelegate & UITableViewDataSource
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
}
}
此時(shí)顯示了數(shù)組里面的評(píng)論牺蹄,最多1000條沙兰。
添加評(píng)論
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
自帶的方法可以有動(dòng)畫(huà)效果鼎天。插入動(dòng)畫(huà)使用.Top
斋射。
從下往上顯示
-
iOS
在tableView和tableViewCell里調(diào)用下面語(yǔ)句:
tableView.transform = CGAffineTransformMakeScale (1,-1);
label.transform = CGAffineTransformMakeScale (1,-1);
兩條語(yǔ)句就可以實(shí)現(xiàn)了绩鸣。
-
Android
可以調(diào)用ListView自帶的屬性stackFromBottom
:
android:stackFromBottom="true"
網(wǎng)上有文章將數(shù)據(jù)
append
到數(shù)據(jù)源纱兑,在獲取數(shù)據(jù)源時(shí)從后往前讀的方式(即messages.count-1-indexPath.section)潜慎,顯然插入在0位置比那樣更方便:insert(message, atIndex: 0)
支持昵稱(chēng)顏色
- 使用NSAttributedString铐炫,且由外界設(shè)置。messages類(lèi)型改為NSAttributedString數(shù)組科贬。
private var messages: [NSAttributedString] = []
-
add
改為NSAttributedString榜掌。
func add(message: NSAttributedString) {}
- 設(shè)置Label的時(shí)候設(shè)置label.attributedText憎账。
給出NSAttributedString
- 一個(gè)新的類(lèi)ChatColorText胞皱,對(duì)外接口colorText,參數(shù)nickName雾鬼、text于颖。
func colorText(nickName: String?, text: String?) -> NSAttributedString?{}
- 隨機(jī)顏色數(shù)組森渐。
private var colors = [
UIColor(hex: .RGB00AEFF)!,
UIColor(hex: .RGB00A61C)!,
UIColor(hex: .RGB5400E6)!,
UIColor(hex: .RGBFF3377)!,
UIColor(hex: .RGBFF8800)!,
UIColor(hex: .RGBFF5E00)!,
UIColor(hex: .RGBCA2EE6)!,
]
- 記錄當(dāng)前取顏色的
Index
同衣,使得不同人給不同顏色。
private var colorIndex: Int = 0
- 記錄昵稱(chēng)對(duì)應(yīng)的顏色值浪秘,保證同一個(gè)昵稱(chēng)同一種顏色耸携。
private var dicOfNameAndColor = [String: UIColor]()
- 對(duì)外接口colorText實(shí)現(xiàn)夺衍。
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
是自己擴(kuò)展的一個(gè)方法沟沙,傳入多串文字和對(duì)應(yīng)的字符返回匹配的NSAttributedString
壁榕。
主要邏輯是:先判斷是否已經(jīng)有保存過(guò)昵稱(chēng)對(duì)應(yīng)的顏色值矛紫,有則直接返回;沒(méi)有則根據(jù)index
獲取顏色值牌里,然后保存起來(lái)颊咬,并改變index
。
點(diǎn)贊動(dòng)畫(huà)
iOS自帶了粒子引擎的類(lèi)CAEmitterLayer
,是一個(gè)粒子發(fā)射器系統(tǒng)喳篇,每個(gè)粒子都是CAEmitterCell
的實(shí)例缓呛。可以查看它們分別有什么屬性杭隙。
有兩個(gè)小點(diǎn)哟绊,一個(gè)是CAEmitterLayer
一些屬性對(duì)CAEmitterCell
有成倍作用痰憎,如birthRate
票髓;另一個(gè)是沒(méi)有明確的停止動(dòng)畫(huà)的方法,包括它的父類(lèi)也沒(méi)提供铣耘∏⒐担可以想到的方法,除了把layer
抹除掉之外蜗细,還可以將CAEmitterLayer
的birthRate
設(shè)置為0裆操,這樣每個(gè)CAEmitterCell
的誕生速率都為0,就不會(huì)有動(dòng)畫(huà)了炉媒。
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
}
}