二零年六一特刊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 UIKit
private let kChannelCount = 2
private let kChannelViewH : CGFloat = 40
private let kChannelMargin : CGFloat = 10
class 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: 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 = 0
for 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 = images
imageView.animationDuration = totalDuration
imageView.animationRepeatCount = 0
// 5.开始播放
imageView.startAnimating()