直播礼物动画
1礼物展示的View礼物展示View上需要!通道
创建所需要的View
GiftContainerView.swift 展示礼物的View
GiftChannelView.swift 通道View
GiftModel.swift 通道所需要的Model
GiftDigitLabel.swift自定义Label
- 1.自定义GiftDigitLabel
* 有描边效果
* 画两次文字
* 需要执行动画(弹动)- 2.自定义GiftChannelView
* 使用Xib描述View
* 定义模型GiftModel
* 开始执行弹动动画(从左侧弹出来)
* 开始执行GiftDigitLabel动画
* 延迟三秒钟,执行消失动画
* 定义一个外界也可添加缓存方法:如果是正在登台消失,则执行执行GiftDigitLabel弹动动画/添加到缓存中
* 需要给ChannelView定义状态
* 给状态进行赋值- 3定义GiftCintainerView
* 给GiftCintainerView添加ChannelView
* 让外界可以给我传入模型
* 检测是否有正执行动画ChannelView和新传入的模型师userName/giftName相同
* 检测是有限制的ChannelView
* 将模型添加到缓存中
* 监听ChannelView的动画完成回调,判断缓存中是否有内容,从缓存中取出所有相同内存,继续执行动画
1.自定义Label设置以及接口
重写drawText方法绘制描边
override func drawText(in rect: CGRect) {
//获取上下文
let context = UIGraphicsGetCurrentContext()
//2给上下文线段设置一个宽度,通过该宽度话出文本
context?.setLineWidth(5)
context?.setLineJoin(.round) //添加模式 .round 圆角线
context?.setTextDrawingMode(.stroke)
textColor = UIColor.orange
super.drawText(in: rect)
context?.setTextDrawingMode(.fill)
textColor = UIColor.white
super.drawText(in: rect)
}
编写动画开始接口
func showDigitAnimation( _ complection : @escaping() -> () ){
UIView.animateKeyframes(withDuration: 0.25, delay: 0, options: [], animations: {
//一 开始时间 ,二 执行时长 0.25的 0.5倍
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.5, animations: {
self.transform = CGAffineTransform(scaleX: 3.0, y: 3.0)
})
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5, animations: {
self.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)
})
}) { (isFinished) in
UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 10, options: [], animations: {
self.transform = CGAffineTransform.identity
}, completion: { (inFinished) in
complection()
})
}
/*
UIView.animate(withDuration: 0.1, animations: {
self.transform = CGAffineTransform(scaleX: 3.0, y: 3.0)
}) { (inFinished) in
UIView.animate(withDuration: 0.1, animations: {
self.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)
}, completion: { (inFinished) in
UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 10, options: [], animations: {
self.transform = CGAffineTransform.identity
}, completion: { (inFinished) in
complection()
})
})
}
*/
}
2.创建GiftModel
class GiftModel: NSObject {
var senderName : String = "" // 谁送的礼物 (谁)
var senderURL : String = "" //头像 (谁)
var giftName : String = "" //礼物名
var giftURL : String = "" //礼物URL
init(senderName : String ,senderURL : String ,giftName :String ,giftURL : String) {
self.senderName = senderName
self.senderURL = senderURL
self.giftName = giftName
self.giftURL = giftURL
}
override func isEqual(_ object: Any?) -> Bool {
guard let objec = object as? GiftModel else { return false }
guard objec.giftName == giftName && objec.senderName == senderName else { return false }
return true
}
}
3.编写GiftChannelView代码(通道)
大致思路
给GiftChannelView 设置Model 利用Model的didSet方法监听礼物的改变进行礼物连击动画
设置状态
enum GiftChannelViewState {
case idle //闲置状态
case animating //正在执行动画
case willEnd //正在等待
case endAnimating //正在消失动画
}
设置属性以及监听礼物的改变
class GiftChannelView: 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: GiftDigitLabel!
fileprivate var cacheNumber : Int = 0 //缓存 计数
fileprivate var currentNumber : Int = 0 //当前连击数
var state : GiftChannelViewState = .idle
/*参数 返回值 */
/*var complectionCallback : ) -> Void 保证初始化所以是Optional*/
/*var complectionCallback : ((channel : GiftChannelView) ->Void)? 3.0不能写外部参数*/
var complectionCallback : ((GiftChannelView) ->Void)?
var giftModel : GiftModel? {
didSet{
//1对模型校验
guard let giftModel = giftModel else { return }
//2给空间设置 数据
iconImageView.image = UIImage(named:giftModel.senderURL)
senderLabel.text = giftModel.senderName
giftDescLabel.text = "送出礼物: 【\(giftModel.giftName)】"
giftImageView.image = UIImage(named : giftModel.giftURL)
//将ChannelView弹出
state = .animating
performAnimation()
}
}
}
设置UI
extension GiftChannelView {
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
}
}
动画
// MARK: - 动画
extension GiftChannelView {
// MARK: - 弹出动画
fileprivate 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()
}
}
// MARK: - 连击动画
fileprivate func performDigitAnimation(){
currentNumber += 1
digitLabel.text = " x\(currentNumber) "
digitLabel.showDigitAnimation {
if self.cacheNumber > 0 {
self.cacheNumber -= 1
self.performAnimation() //递归调用
}else{
self.state = .willEnd
self.perform(#selector(self.performEndAnimation), with: self, afterDelay: 3)
}
}
}
// MARK: - 消失动画
@objc fileprivate func performEndAnimation(){
state = .endAnimating
UIView.animate(withDuration: 0.25, animations: {
self.frame.origin.x = UIScreen.main.bounds.width
self.alpha = 0
}) { (isFinished) in
self.giftModel = nil
self.frame.origin.x = -self.frame.width
self.state = .idle
self.currentNumber = 0
self.cacheNumber = 0
self.digitLabel.alpha = 0.0
/*
//block调用*/
if let complectionCallback = self.complectionCallback{
complectionCallback(self)
}
}
}
}
对外提供的方法
extension GiftChannelView {
func addOnceToCache(){
if state == .willEnd{
performDigitAnimation()
//取消之前的任务3秒等待
NSObject.cancelPreviousPerformRequests(withTarget: self)
}else{
cacheNumber += 1
}
}
class func loadChannelView() -> GiftChannelView{
return Bundle.main.loadNibNamed("GiftChannelView", owner: nil, options: nil)?.first as! GiftChannelView
}
}
4编写GiftContainerView代码(最外面的View)
属性
private let kChannelCount = 2
private let kChannelViewH : CGFloat = 40
private let kChannelMargin : CGFloat = 10
class GiftContainerView: UIView {
fileprivate lazy var channelViews : [GiftChannelView] = [GiftChannelView]()
fileprivate lazy var cacheGiftModels : [GiftModel] = [GiftModel]() //缓存中的Model
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
UI设置
extension GiftContainerView{
fileprivate func setupUI(){
//1根据当前的取代数,创建ChannelView
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 = GiftChannelView.loadChannelView()
channelView.frame = CGRect(x: x, y: y, width: w, height: h)
channelView.alpha = 0.0
channelView.state = .idle
// channelView.delegate = self
channelViews.append(channelView)
addSubview(channelView)
channelView.complectionCallback = { channelView in
// 取出缓存中的Model第一个
guard self.cacheGiftModels.count != 0 else { return }
let firstGiftModel = self.cacheGiftModels.first!
self.cacheGiftModels.removeFirst()
channelView.giftModel = firstGiftModel
// .将数组中剩余有和firstGiftModel相同的模型放入到ChanelView缓存中
for i in (0..<self.cacheGiftModels.count).reversed() {
let giftModel = self.cacheGiftModels[i]
if giftModel.isEqual(firstGiftModel) {
channelView.addOnceToCache()
self.cacheGiftModels.remove(at: i)
}
}
}
}
}
}
对外接口
extension GiftContainerView {
func showGiftModel(_ giftModel : GiftModel){
//1 判断正在ChannelView和赠送的新礼物的名称是否相同 (userName / giftName)
if let channelView = checkUsingChannelView(giftModel) {
channelView.addOnceToCache() //缓存加一
return
}
//2判断有没有闲置的ChannelView ]
if let channelView = chackIdleChannelView() {
channelView.giftModel = giftModel //设置Model
return
}
// 将数据放到缓存中
cacheGiftModels.append(giftModel)
}
private func checkUsingChannelView(_ giftModel : GiftModel) -> GiftChannelView?{
for channelView in channelViews{
if giftModel.isEqual(channelView.giftModel) && channelView.state != .endAnimating{
return channelView
}
}
return nil
}
private func chackIdleChannelView() -> GiftChannelView?{
for channelView in channelViews {
if channelView.state == .idle {
return channelView
}
}
return nil
}
}









网友评论