本文始發于我的博文iOS直播:評論框與粒子系統點贊動畫,現轉發至此。
目錄
- 前言
- 效果預覽
- 評論框
- 列表
- 添加評論
- 從下往上顯示
- 支持昵稱顏色
- 給出NSAttributedString
- 點贊動畫
前言
最近做了直播功能,其實難度不是說很大,主要是方案和SDK的選擇、整個直播流程的異常處理和優化,還有第三方SDK的填坑。不過本文只是記錄下評論框和點贊效果的實現,其他的是用第三方SDK,覺得沒什么好分享的,只是了解了直播流程和開發中會遇到的問題。
但看到效果還是蠻激動和蠻有成就感的,這個主要是技術本身帶來的。
效果預覽
評論框
細化需求:
- 顯示評論內容
- 從下往上顯示
- 最大支持1000條
- 不同人昵稱顯示顏色隨機分配,同一個人顏色保持不變。
- 評論插入有動畫
列表
- 新的類
MessageChatView
,對外接口add
。
func add(message: String) {}
- 存放評論數組
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
}
}
此時顯示了數組里面的評論,最多1000條。
添加評論
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里調用下面語句:
tableView.transform = CGAffineTransformMakeScale (1,-1);
label.transform = CGAffineTransformMakeScale (1,-1);
兩條語句就可以實現了。
-
Android
可以調用ListView自帶的屬性stackFromBottom
:
android:stackFromBottom="true"
網上有文章將數據
append
到數據源,在獲取數據源時從后往前讀的方式(即messages.count-1-indexPath.section),顯然插入在0位置比那樣更方便:insert(message, atIndex: 0)
支持昵稱顏色
- 使用NSAttributedString,且由外界設置。messages類型改為NSAttributedString數組。
private var messages: [NSAttributedString] = []
-
add
改為NSAttributedString。
func add(message: NSAttributedString) {}
- 設置Label的時候設置label.attributedText。
給出NSAttributedString
- 一個新的類ChatColorText,對外接口colorText,參數nickName、text。
func colorText(nickName: String?, text: String?) -> NSAttributedString?{}
- 隨機顏色數組。
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
,使得不同人給不同顏色。
private var colorIndex: Int = 0
- 記錄昵稱對應的顏色值,保證同一個昵稱同一種顏色。
private var dicOfNameAndColor = [String: UIColor]()
- 對外接口colorText實現。
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
抹除掉之外,還可以將CAEmitterLayer
的birthRate
設置為0,這樣每個CAEmitterCell
的誕生速率都為0,就不會有動畫了。
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
}
}