iOS直播:評論框與粒子系統點贊動畫

本文始發于我的博文iOS直播:評論框與粒子系統點贊動畫,現轉發至此。

目錄

  • 前言
  • 效果預覽
  • 評論框
    • 列表
    • 添加評論
    • 從下往上顯示
    • 支持昵稱顏色
    • 給出NSAttributedString
  • 點贊動畫

前言

最近做了直播功能,其實難度不是說很大,主要是方案和SDK的選擇、整個直播流程的異常處理和優化,還有第三方SDK的填坑。不過本文只是記錄下評論框和點贊效果的實現,其他的是用第三方SDK,覺得沒什么好分享的,只是了解了直播流程和開發中會遇到的問題。
但看到效果還是蠻激動和蠻有成就感的,這個主要是技術本身帶來的。

效果預覽

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

評論框

細化需求:

  1. 顯示評論內容
  2. 從下往上顯示
  3. 最大支持1000條
  4. 不同人昵稱顯示顏色隨機分配,同一個人顏色保持不變。
  5. 評論插入有動畫

列表

  • 新的類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抹除掉之外,還可以將CAEmitterLayerbirthRate設置為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
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容