禮物連擊動畫
基礎知識
digitalLable:1.畫文字 ?2.lable動畫回調
channelView:0.幾種狀態 1.當執行完(lable動畫回調)--檢測是否有緩存數量-有就遞歸執行-count==0 ? 2.單模型緩存--數量? 3.動畫 ?chanel回調 4.提供對外函數--增加正在執行動畫的數量---number
containnerView:0.當執行chanel回調-檢測多模型緩存--數量和類型--如果有就賦值數量和第一個模型--執行動畫 ?1.提供對外插入模型方法:1.1檢測是否和正在執行的相同--如果有增加chinel緩存數量--沒有執行1.2 ? 1.2檢測是否有空通道有就直接賦值執行動畫---沒有執行1.3 ?1.3前兩部沒有加入到 模型緩存中
try處理
return try! userInfo.build()
userInfo.build()返回值中有throw? 需要進行 try!處理
0.層級 關系 view? containView? channelView digitalview HYGiftModel
view: 只傳入model就可以
containView: 1.判斷是否有閑置通道 ?2.是否禮物模型相同 3.單通道的緩存數量
channelView: 對單個禮物的狀態 ?和 緩存數量的執行 和 給模型賦值后執行動畫
HYGiftModel: 判斷禮物是否相同(重寫isEqual 根據giftname 和發送者name )
1.HYDigitLabel ?
1.0畫出數字text ? 1.1設置開始動畫方法----結束并且有回調
func startScaleAnimating(_ complection : (() -> Void)? = nil) {
UIView.animateKeyframes(withDuration: 1, delay: 0, options: [], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.4, animations: {
self.transform = CGAffineTransform(scaleX: 3.0, y: 3.0)
})
UIView.addKeyframe(withRelativeStartTime: 0.4, relativeDuration: 0.8, animations: {
self.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)
})
UIView.addKeyframe(withRelativeStartTime: 0.8, relativeDuration: 1, animations: {
self.transform = CGAffineTransform.identity
})
}) { (_) in
complection?()
}
}
2.HYGiftChannelView ?通道view
class HYGiftChannelView: UIView {
// MARK: 控件屬性
@IBOutlet weak var bgView: UIView!
@IBOutlet weak var iconImageView: UIImageView!
@IBOutlet weak var senderLabel: UILabel!
@IBOutlet weak var giftDescLabel: UILabel!
@IBOutlet weak var giftImageView: UIImageView!
@IBOutlet weak var digitLabel: HYDigitLabel!
// MARK: 定義屬性
var finishedCallback : ((HYGiftChannelView) -> Void)?
var state : ChannerlViewState = .idle
var cacheNumber : Int = 0
fileprivate var currentNumber : Int = 0
var giftModel : HYGiftModel?? {
didSet {
// 1.校驗模型是否有值
guard let giftModel = giftModel else {
return
}
// 2.將giftModel中屬性設置到控件中
iconImageView.image = UIImage(named: giftModel.senderIcon)
senderLabel.text = giftModel.senderName
giftDescLabel.text = "送出禮物【\(giftModel.giftName)"
giftImageView.image = UIImage(named: giftModel.giftIcon)
// 3.將channelView展示出來
performShowAnimating()
}
}
}
enum ChannerlViewState {
case idle ?閑置
case animating ?正在執行
case endWating 等待結束
case ending 結束
}
2.1
fileprivate func performShowAnimating() {
狀態改變
state = .animating
UIView.animate(withDuration: 0.25, animations: {
self.frame.origin.x = 0
self.alpha = 1.0
}) { (_) in
橫條停到位置后----開始執行數字彈跳動畫
self.performDigitAnimating()
}}
數字彈跳動畫
fileprivate func performDigitAnimating() {
// 1.將digitLabel的alpha值設置為1
digitLabel.alpha = 1
// 2.設置digitLabel上面顯示的數字
currentNumber += 1
digitLabel.text = " x\(currentNumber)"
// 3.執行動畫
digitLabel.startScaleAnimating {
if self.cacheNumber > 0 {
self.cacheNumber -= 1
self.performDigitAnimating()
相當于遞歸執行 直到self.cacheNumber = 0
} else {
如果沒有self.cacheNumber 狀態就改變
self.state = .endWating
等待執行結束動畫
self.perform(#selector(self.performEndAnimating), with: nil, afterDelay: 3.0)
}
}
@objc fileprivate func performEndAnimating() {
狀態改變--正在結束 ?往右走的過程
self.state = .ending
UIView.animate(withDuration: 1, animations: {
self.frame.origin.x = UIScreen.main.bounds.width
}) { (_) in
self.alpha = 0.0
self.state = .idle
self.digitLabel.alpha = 0
self.currentNumber = 0
self.cacheNumber = 0
self.frame.origin.x = -self.frame.width
self.giftModel = nil
結束完成通知控制器,看是否還有任務需要執行
self.finishedCallback?(self)
}
}
// MARK:- 對外提供的函數---對單一通道的處理
extension HYGiftChannelView {
func addOnceToCache() {
若果正在等待結束----讓其取消等待--立即執行-改變狀態
if state == .endWating {
NSObject.cancelPreviousPerformRequests(withTarget: self)
self.state = .animating
performDigitAnimating()
} else {
其他情況就讓其緩存+1
cacheNumber += 1
}
}
}
對containView處理
extension HYGiftContainerView {
fileprivate func setupUI() {
// 1.根據當前的渠道數,創建HYGiftChannelView
let w : CGFloat = frame.width
let h : CGFloat = 40
let x : CGFloat = -w
for i in 0..<2 {
let y : CGFloat = (h + 10) * CGFloat(i)
let channelView = HYGiftChannelView.loadChannelView()
channelView.frame = CGRect(x: x, y: y, width: w, height: h)
channelView.alpha = 0.0
addSubview(channelView)
channelViews.append(channelView)
channelView.finishedCallback = {[unowned self] (channelView) in
// 1.檢查緩存中是否有內容
guard self.cacheGiftModels.count != 0 else {
return
}
// 2.取出模型
let firstGiftModel = self.cacheGiftModels.first!
self.cacheGiftModels.removeFirst()
// 3.取出和giftModel相同模型的個數
var cacheNumber = 0
for i in (0..<self.cacheGiftModels.count).reversed() {
if self.cacheGiftModels[i].isEqual(firstGiftModel) {
cacheNumber += 1
self.cacheGiftModels.remove(at: i)
}
}
// 4.讓閑置的channelView執行緩存中禮物模型的動畫
channelView.cacheNumber = cacheNumber
channelView.giftModel = firstGiftModel
}
}
}
}
對外提供的函數--如何調用
extension HYGiftContainerView {
func insertGiftModel(_ giftModel : HYGiftModel) {
// 1.判斷是否有正在執行動畫的渠道的模型和新插入的模型一致
if let channelView = checkModelInChannerView(giftModel) {
channelView.addOnceToCache()
return
}
// 2.有沒有閑置的channelView可以用于展示的禮物
if let channelView = checkIdleChannelView() {
channelView.giftModel = giftModel
return
}
// 3.將模型添加到緩存中
cacheGiftModels.append(giftModel)
}
fileprivate func checkModelInChannerView(_ newGiftModel : HYGiftModel) -> HYGiftChannelView? {
// return channelViews.filter({ newGiftModel.isEqual($0.giftModel) }).first
for channelView in channelViews {
if newGiftModel.isEqual(channelView.giftModel) && channelView.state != .ending {
return channelView
}
}
return nil
}
fileprivate func checkIdleChannelView() -> HYGiftChannelView? {
// return channelViews.filter({ $0.state == .idle }).first
for channelView in channelViews {
if channelView.state == .idle {
return channelView
}
}
return nil
}
}
模型的處理
class HYGiftModel : NSObject {
var senderName : String = ""
var senderIcon : String = ""
var giftIcon : String = ""
var giftName : String = ""
init(senderName : String, senderIcon : String, giftIcon : String, giftName : String) {
self.senderName = senderName
self.senderIcon = senderIcon
self.giftIcon = giftIcon
self.giftName = giftName
}
override func isEqual(_ object: Any?) -> Bool {
// 1.判斷傳入的內容是否有值
guard let object = object as? HYGiftModel else {
return false
}
// 2.判斷贈送者和贈送的禮物名稱是否相同
return object.senderName == senderName && object.giftName == giftName
}
}