簡(jiǎn)單結(jié)構(gòu)分析:
在做禮物點(diǎn)擊的時(shí)候,我們先把需要顯示在屏幕上的禮物放入到一個(gè)容器containerView中如下圖的紅色框,將一個(gè)禮物當(dāng)做成一個(gè)通道channelView,當(dāng)屏幕或者容器顯示不了這么多通道的時(shí)候,我們將還未執(zhí)行的通道禮物3放入一個(gè)緩存數(shù)組中等待執(zhí)行

Snip20170811_3.png

01.gif
一 :DigitLabel基本效果
1.數(shù)字的描邊效果: 重寫(xiě)override func drawText(in rect: CGRect)方法,先畫(huà)出一條橙色的外邊然后再畫(huà)一條白色的 這樣顯示的效果就很描邊一樣。
class HJGiftDigitLabel: UILabel {
override func drawText(in rect: CGRect) {
// 1.獲取當(dāng)前上下文
let content = UIGraphicsGetCurrentContext()
content?.setLineJoin(.round)
content?.setLineWidth(5.0)
content?.setTextDrawingMode(.stroke)
textColor = UIColor.orange
super.drawText(in: rect)
content?.setTextDrawingMode(.fill)
textColor = UIColor.white
super.drawText(in: rect)
}
2.數(shù)字的動(dòng)畫(huà)效果:首先使得數(shù)字動(dòng)畫(huà)放大多倍,然后再縮小,再回到原來(lái)的大小。
func ShowDigitAnimation(_ complection: @escaping () -> ()) {
UIView.animateKeyframes(withDuration: 0.25, delay: 0, options: [], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5, animations: {
self.transform = CGAffineTransform(scaleX: 3.0, y: 3.0)
})
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5, animations: {
self.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)
})
}) { (isFinished) in
UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 10, options: [], animations: {
self.transform = CGAffineTransform.identity
}, completion: { (isFinifshed) in
complection()
})
}
二 :禮物通道ChannelView的設(shè)計(jì)與實(shí)現(xiàn)
1.通過(guò)xib描述好View,并使用代碼完成基本的UI設(shè)置。
// MARK:- 設(shè)置UI界面
extension HJGiftChannelView {
override func layoutSubviews() {
super.layoutSubviews()
bgView.layer.cornerRadius = frame.height * 0.5
iconImageView.layer.cornerRadius = frame.height * 0.5
bgView.layer.masksToBounds = true
iconImageView.layer.masksToBounds = true
iconImageView.layer.borderWidth = 1
iconImageView.layer.borderColor = UIColor.green.cgColor
}
}
2.我們的用戶(hù)名稱(chēng)(userName) 用戶(hù)頭像(userIcon) 禮物名稱(chēng)(GiftName) 禮物圖片(GiftURL)基本都是由外部的參數(shù)傳入進(jìn)來(lái),所以我們定義一個(gè)GiftChannelModel
class HJGiftChannelModel: NSObject {
var senderName : String = ""
var senderUrl : String = ""
var GiftName : String = ""
var GiftUrl : String = ""
init(_ senderName : String,_ senderUrl : String,_ GiftName : String,_ GiftUrl : String) {
self.senderName = senderName
self.senderUrl = senderUrl
self.GiftName = GiftName
self.GiftUrl = GiftUrl
}
// 重寫(xiě)isEqual方法
override func isEqual(_ object: Any?) -> Bool {
guard let object = object as? HJGiftChannelModel else {
return false
}
guard object.senderName == senderName && object.GiftName == GiftName else {
return false
}
return true
}
}
- 通過(guò)定義好的
GiftChannelModel給我們的GiftChannelView的UI屬性設(shè)置好基本的信息
var giftModel : HJGiftChannelModel? {
didSet{
// 1. 模型校驗(yàn)
guard let giftModel = giftModel else {
return
}
// 2. 設(shè)置基本信息
iconImageView.image = UIImage(named: giftModel.senderUrl)
senderLabel.text = giftModel.senderName
giftDescLabel.text = "送出禮物:【\(giftModel.GiftName)】"
giftImageView.image = UIImage(named: giftModel.GiftUrl)
4. 執(zhí)行動(dòng)畫(huà),我們的動(dòng)畫(huà)效果是直接從屏幕的直接進(jìn)入到屏幕上 并且在屏幕上顯示三秒之后才開(kāi)始消失動(dòng)畫(huà),所以在我們需要知道禮物通道(ChannelView)的狀態(tài)是在animating //正在執(zhí)行動(dòng)畫(huà) idle //閑置的 willEnd //將要結(jié)束動(dòng)畫(huà) endAnimating //已經(jīng)結(jié)束動(dòng)畫(huà)所以定義一個(gè)枚舉 enum HJGiftChannelState
enum HJGiftChannelState {
case idle //閑置的
case animating //正在執(zhí)行動(dòng)畫(huà)
case willEnd //將要結(jié)束動(dòng)畫(huà)
case endAnimating //已經(jīng)結(jié)束動(dòng)畫(huà)
}
動(dòng)畫(huà)顯示的時(shí)候我們需要第一步:當(dāng)顯示在屏幕上的時(shí)候則調(diào)用DigitLabel的動(dòng)畫(huà),如果動(dòng)畫(huà)的狀態(tài)即將的消失的時(shí)候則我們讓ChannelView在屏幕上停留3秒再消失
//MARK: -執(zhí)行動(dòng)畫(huà)
extension HJGiftChannelView {
func performAnimation(){
digitLabel.alpha = 1.0
digitLabel.text = " x1 "
UIView.animate(withDuration: 0.25, animations: {
self.alpha = 1.0
self.frame.origin.x = 0
}) { (isFinished) in
self.performDigitAnimation()
}
}
func performDigitAnimation(){
currentNumber += 1
digitLabel.text = " x\(currentNumber) "
digitLabel.ShowDigitAnimation {
if self.currentCacheNumber > 0 {
self.currentCacheNumber -= 1
self.performDigitAnimation()
}else {
self.channelViewState = .willEnd
self.perform(#selector(self.performEndAnimation), with: nil, afterDelay: 3.0)
}
}
}
如果禮物在即將消失self.channelViewState = .willEnd的狀態(tài)下,用戶(hù)又點(diǎn)擊了贈(zèng)送禮物,則需要重新啟動(dòng)動(dòng)畫(huà)并將之前的停留3秒狀態(tài)取消
if channelViewState == .willEnd {
performAnimation()
// 取消延遲3秒
NSObject.cancelPreviousPerformRequests(withTarget: self)
如果不是的上述的狀態(tài)則讓其加入我們的緩存中等待執(zhí)行
extension HJGiftChannelView {
//添加到緩存池
func addOneToCache(){
if channelViewState == .willEnd {
performAnimation()
// 取消延遲3秒
NSObject.cancelPreviousPerformRequests(withTarget: self)
} else {
currentCacheNumber += 1
}
}
三 : 將ChannelView 添加到容器GiftContainerView中
import UIKit
private let kChannelCount = 2
private let kChannelViewH : CGFloat = 40
private let kChannelMargin : CGFloat = 10
class HJGiftContainerView: UIView {
fileprivate lazy var channelViews : [HJGiftChannelView] = [HJGiftChannelView]()
fileprivate lazy var cacheGiftModels : [HJGiftChannelModel] = [HJGiftChannelModel]()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//MARK: -設(shè)置UI
extension HJGiftContainerView {
fileprivate func setupUI(){
let w : CGFloat = frame.width
let h : CGFloat = kChannelViewH
let x : CGFloat = 0
for i in 0..<kChannelCount {
let y : CGFloat = (h + kChannelMargin) * CGFloat(i)
let channelView = HJGiftChannelView.loadFormNib()
channelView.frame = CGRect(x: x, y: y, width:w, height: h)
channelView.alpha = 0
addSubview(channelView)
channelViews.append(channelView)
判斷正在忙的ChanelView和新贈(zèng)送的新禮物的用戶(hù)名稱(chēng) 和 禮物名稱(chēng) (username && giftname)是否相同
extension HJGiftContainerView {
func showModel(_ giftModel : HJGiftChannelModel){
// 1.判斷正在忙的ChanelView和贈(zèng)送的新禮物的(username/giftname)
if let channelView = checkUsingChanelView(giftModel) {
channelView.addOneToCache()
return
}
// 2. 判斷有沒(méi)有閑置的channelView
if let channelView = checkIdleChannelView(){
channelView.giftModel = giftModel
return
}
// 3. 將數(shù)據(jù)加入緩存中
cacheGiftModels.append(giftModel)
}
//檢查正在使用的channelView
private func checkUsingChanelView(_ giftModel : HJGiftChannelModel) -> HJGiftChannelView? {
for channelView in channelViews {
if giftModel.isEqual(channelView.giftModel)
&& channelView.channelViewState != .endAnimating {
return channelView
}
}
return nil
}
//檢查有沒(méi)有閑置的channel
private func checkIdleChannelView() -> HJGiftChannelView? {
for channelView in channelViews {
if channelView.channelViewState == .idle {
return channelView
}
}
return nil
}
}
監(jiān)聽(tīng)ChannelView什么時(shí)候完成動(dòng)畫(huà),判斷緩存中是否有內(nèi)容,通過(guò)reversed()反序遍歷的方式來(lái)確定我們的firstModel相同的模型放入到ChanelView緩存中繼續(xù)執(zhí)行動(dòng)畫(huà)
channelView.complectionCallback = { channelView in
// 1.取出緩存中的模型
guard self.cacheGiftModels.count != 0 else {
return
}
// 2.取出緩存中的第一個(gè)模型數(shù)據(jù)
let firstModel = self.cacheGiftModels.first!
self.cacheGiftModels.removeFirst()
// 3.讓閑置的channelView執(zhí)行動(dòng)畫(huà)
channelView.giftModel = firstModel
// 4.將數(shù)組中剩余有和firstModel相同的模型放入到ChanelView緩存中
for i in (0..<self.cacheGiftModels.count).reversed() {
let giftModel = self.cacheGiftModels[i]
if giftModel .isEqual(firstModel) {
channelView.addOneToCache()
self.cacheGiftModels.remove(at: i)
}
}
}
}
}
}