vlambda博客
学习文章列表

二零年六一特刊Swift5高仿直播项目之给主播送礼物

一.首先是Label的描边效果和大小缩放动画

二是礼物模型的简单设计

三是礼物展示航道的设计

import UIKit
enum LDGiftChannelViewState { case idle //闲置 case animationg //执行动画中 case willEnd //将要结束 case endAnimating //执行结束动画中}class LDGiftChannelView: UIView { @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: LDGiftDigitLabel!
fileprivate var cacheNumber : Int = 0 fileprivate var currentNumber: Int = 0 var complectionCallback: ((LDGiftChannelView)->Void)?
var state: LDGiftChannelViewState = .idle var giftModel: LDGiftModel? { didSet{ //校验模型 guard let giftModel = giftModel else { return } //给控件设置信息 iconImageView.image = UIImage(named: giftModel.senderURL) senderLabel.text = giftModel.senderName giftDescLabel.text = "送出礼物:【\(giftModel.giftName)】" giftImageView.image = UIImage(named: giftModel.giftURL) //将ChanelView弹出 state = .animationg performAnimation() } }}// MARK:- 设置UI界面extension LDGiftChannelView { 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 LDGiftChannelView{ 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() } } fileprivate func performDigitAnimation(){ currentNumber += 1 digitLabel.text = "x\(currentNumber)" digitLabel.showDigitAnimation {
if self.cacheNumber > 0 { self.cacheNumber -= 1 self.performDigitAnimation() }else{ self.state = .willEnd self.perform(#selector(self.performEndAnimation), with: nil, afterDelay: 3.0) } } } @objc fileprivate func performEndAnimation(){ UIView.animate(withDuration: 0.25, animations: { self.frame.origin.x = UIScreen.main.bounds.width self.alpha = 0.0 }) { (isFinished) in self.currentNumber = 0 self.cacheNumber = 0 self.giftModel = nil self.frame.origin.x = -self.frame.width self.state = .idle self.digitLabel.alpha = 0.0 if let complectionCallback = self.complectionCallback { complectionCallback(self) } } }}//MARK: -对外提供的函数extension LDGiftChannelView{ class func loadFromNib() -> LDGiftChannelView{ return Bundle.main.loadNibNamed("LDGiftChannelView", owner: nil, options: nil)?.first as! LDGiftChannelView }
func addOnceToCache() { if state == .willEnd { performDigitAnimation() NSObject.cancelPreviousPerformRequests(withTarget: self) }else{ cacheNumber += 1 } }}


四是礼物航道的容器View的设计

import UIKitprivate let kChannelCount = 2private let kChannelViewH : CGFloat = 40private let kChannelMargin : CGFloat = 10class LDGiftContainerView: UIView {
// MARK: 定义属性 fileprivate lazy var channelViews : [LDGiftChannelView] = [LDGiftChannelView]() fileprivate lazy var cacheGiftModels : [LDGiftModel] = [LDGiftModel]() // MARK: 构造函数 override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }}// MARK:- 设置UI界面extension LDGiftContainerView { fileprivate func setupUI() { // 1.根据当前的渠道数,创建HYGiftChannelView 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 = LDGiftChannelView.loadFromNib() channelView.frame = CGRect(x: x, y: y, width: w, height: h) channelView.alpha = 0.0 addSubview(channelView) channelViews.append(channelView)
channelView.complectionCallback = { channelView in //1. 去除缓存中的模型 guard self.cacheGiftModels.count != 0 else { return } //2.取出缓存中的第一个模型 let firstGiftModel = self.cacheGiftModels.first! self.cacheGiftModels.removeFirst()
for i in (0..<self.cacheGiftModels.count).reversed() { let giftModel = self.cacheGiftModels[i] if giftModel.isEqual(firstGiftModel) { channelView.addOnceToCache() self.cacheGiftModels.remove(at: i) } } //3.让闲置的channelView执行动画 channelView.giftModel = firstGiftModel
} } }}

extension LDGiftContainerView{ func showGiftModel(_ giftModel: LDGiftModel) { //判断正在忙的chanelView和赠送的新礼物的(userName/giftName)是否相等 if let channelView = checkUsingChaneView(giftModel) { channelView.addOnceToCache() return } //判断有没有闲置的ChaneView if let channelView = checkIdleChannelView() { channelView.giftModel = giftModel return } //将数据放入缓存中 cacheGiftModels.append(giftModel) } private func checkUsingChaneView(_ giftModel: LDGiftModel) -> LDGiftChannelView?{ for channelView in channelViews { if giftModel.isEqual(channelView.giftModel) && channelView.state != .endAnimating { return channelView } } return nil } private func checkIdleChannelView()->LDGiftChannelView?{ for channelView in channelViews { if channelView.state == .idle { return channelView } } return nil }}

iOS中播放Gif的方案

// 1.加载Gif图片, 并且转成Data类型 guard let path = Bundle.main.path(forResource: "demo.gif", ofType: nilelse { return } guard let data = NSData(contentsOfFile: path) else { return } // 2.从data中读取数据: 将data转成CGImageSource对象 guard let imageSource = CGImageSourceCreateWithData(data, nilelse { return } let imageCount = CGImageSourceGetCount(imageSource) // 3.便利所有的图片var images = [UIImage]()var totalDuration : TimeInterval = 0for i in 0..<imageCount { // 3.1.取出图片 guard let cgImage = CGImageSourceCreateImageAtIndex(imageSource, i, nil) else { continue } let image = UIImage(cgImage: cgImage) if i == 0 { imageView.image = image } images.append(image) // 3.2.取出持续的时间 guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil) as? NSDictionary else { continue }  guard let gifDict = properties[kCGImagePropertyGIFDictionary] asNSDictionary else { continue } guard let frameDuration = gifDict[kCGImagePropertyGIFDelayTime] as? NSNumber else { continue }  totalDuration += frameDuration.doubleValue }  // 4.设置imageView的属性 imageView.animationImages = images imageView.animationDuration = totalDuration imageView.animationRepeatCount = 0  // 5.开始播放imageView.startAnimating()