二零年六一特刊Swift5高仿直播项目之给主播送礼物
一.首先是Label的描边效果和大小缩放动画
二是礼物模型的简单设计
三是礼物展示航道的设计
import UIKitenum 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 = 0fileprivate var currentNumber: Int = 0var complectionCallback: ((LDGiftChannelView)->Void)?var state: LDGiftChannelViewState = .idlevar giftModel: LDGiftModel? {didSet{//校验模型guard let giftModel = giftModel else {return}//给控件设置信息iconImageView.image = UIImage(named: giftModel.senderURL)senderLabel.text = giftModel.senderNamegiftDescLabel.text = "送出礼物:【\(giftModel.giftName)】"giftImageView.image = UIImage(named: giftModel.giftURL)//将ChanelView弹出state = .animationgperformAnimation()}}}// MARK:- 设置UI界面extension LDGiftChannelView {override func layoutSubviews() {super.layoutSubviews()bgView.layer.cornerRadius = frame.height * 0.5iconImageView.layer.cornerRadius = frame.height * 0.5bgView.layer.masksToBounds = trueiconImageView.layer.masksToBounds = trueiconImageView.layer.borderWidth = 1iconImageView.layer.borderColor = UIColor.green.cgColor}}//MARK: -执行动画的代码extension LDGiftChannelView{fileprivate func performAnimation(){digitLabel.alpha = 1.0digitLabel.text = "x1"UIView.animate(withDuration: 0.25, animations: {self.alpha = 1.0self.frame.origin.x = 0}) { (isFinished) inself.performDigitAnimation()}}fileprivate func performDigitAnimation(){currentNumber += 1digitLabel.text = "x\(currentNumber)"digitLabel.showDigitAnimation {if self.cacheNumber > 0 {self.cacheNumber -= 1self.performDigitAnimation()}else{self.state = .willEndself.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.widthself.alpha = 0.0}) { (isFinished) inself.currentNumber = 0self.cacheNumber = 0self.giftModel = nilself.frame.origin.x = -self.frame.widthself.state = .idleself.digitLabel.alpha = 0.0if 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.根据当前的渠道数,创建HYGiftChannelViewlet w : CGFloat = frame.widthlet h : CGFloat = kChannelViewHlet x : CGFloat = 0for 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.0addSubview(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}//判断有没有闲置的ChaneViewif let channelView = checkIdleChannelView() {channelView.giftModel = giftModelreturn}//将数据放入缓存中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: nil) else { return }guard let data = NSData(contentsOfFile: path) else { return }// 2.从data中读取数据: 将data转成CGImageSource对象guard let imageSource = CGImageSourceCreateWithData(data, nil) else { 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] as? NSDictionary else { continue }guard let frameDuration = gifDict[kCGImagePropertyGIFDelayTime] as? NSNumber else { continue }totalDuration += frameDuration.doubleValue}// 4.设置imageView的属性imageView.animationImages = imagesimageView.animationDuration = totalDurationimageView.animationRepeatCount = 0// 5.开始播放imageView.startAnimating()
