Recently, I made a gift animation written with swift. This animation was originally used in the project. Then when the project needs to be changed, I think I can use swift to encapsulate it for reference.
Want to see the effect picture:
thinking
1. First create an animated data model.
struct AnimationModel{ var userHead:String? var giftNum:Int? var giftHeadImage:String? var giftUserName:String? var giftName:String? init(dict:[String:String]) { if let userHead = dict["userhead"]{ self.userHead = userHead } if let giftName = dict["giftname"]{ self.giftName = giftName } if let giftNum = Int(dict["giftnum"] ?? "0") { self.giftNum = giftNum } if let giftHeadImage = dict["giftheadimage"]{ self.giftHeadImage = giftHeadImage } if let giftUserName = dict["giftusername"]{ self.giftUserName = giftUserName } } }
2. Customize an animation view that controls the number of gifts it has.
class AnimationLabel: UIView { var myTimer:Timer? var num:Int? var giftCount:Int? var model: AnimationModel?{ didSet{ self.headView.image = UIImage(named:model?.userHead ?? "head") self.giftImageView.image = UIImage(named:model?.giftHeadImage ?? "head") self.labelName.text = model?.giftUserName self.labelGift.text = "Give\(model?.giftName ?? "anonymous")" } } lazy var headView: UIImageView = { let frame = CGRect(x: 5, y: 5, width: self.frame.height - 10, height: self.frame.height - 10) return self.getImageView(frame: frame, stringImage: "head") }() lazy var labelName: UILabel = { let frame = CGRect(x: self.headView.frame.maxX+5, y: self.headView.frame.minY+5, width: self.frame.width - 10, height: 20) let label = self.getlabeView(frame: frame, title: "That's my name") label.textColor = UIColor.rgb(239, 202, 139, 1.0) return label }() lazy var labelGift: UILabel = { let frame = CGRect(x: self.labelName.frame.minX, y: self.labelName.frame.maxY+5, width: self.frame.width, height: 20) let label = self.getlabeView(frame: frame, title: "Give red lips") return label }() lazy var labelCount: UILabel = { let frame = CGRect(x: self.labelGift.frame.maxX + 5, y: self.labelGift.frame.minY, width: self.frame.width, height: 20) let label = self.getlabeView(frame: frame, title: "x1") label.font = UIFont.systemFont(ofSize: 15) label.textColor = UIColor.red return label }() lazy var giftImageView: UIImageView = { let frame = CGRect(x: self.labelName.frame.maxX, y: 0, width: 40, height: 40) return self.getImageView(frame: frame, stringImage: "gift") }() override init(frame: CGRect) { super.init(frame: frame) self.num = 1 addSubview(headView) addSubview(labelName) addSubview(labelGift) addSubview(labelCount) addSubview(giftImageView) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
3. The zoom-in and zoom-out animation is controlled by a timer, dividing the total number of gifts by a number, which is the number of overlays after each zoom-in and zoom-out animation is completed. Stop animating until the number of overlays equals the total number of gifts.And remove it from the parent view.
extension AnimationLabel{ func getImageView(frame:CGRect, stringImage:String) -> UIImageView { let imageView = UIImageView(frame: frame) imageView.image = UIImage(named:stringImage) return imageView } func getlabeView(frame:CGRect, title:String) -> UILabel { let label = UILabel(frame: frame) label.text = title label.font = UIFont.systemFont(ofSize: 12) return label } //Start animation of the number of gifts func startTimer(){ self.myTimer = Timer(timeInterval: 0.7, target: self, selector: #selector(mystartAnimation), userInfo: nil, repeats: true) RunLoop.current.add(self.myTimer!, forMode: .defaultRunLoopMode) } func stopTimer(){ self.myTimer?.invalidate() self.myTimer = nil } @objc func mystartAnimation(){ let add = floorf(Float(self.giftCount!) / 5.0) if self.num! >= self.giftCount! { self.num = self.giftCount self.stopTimer() UIView.animate(withDuration: 1.0, animations: { self.alpha = 0 }, completion: { finish in self.removeFromSuperview() }) } UIView.animateKeyframes(withDuration: 0.25, delay: 0, options: .allowUserInteraction, animations: { UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1.0, animations: { self.labelCount.transform = CGAffineTransform(scaleX: 3.0, y: 3.0) }) UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5, animations: { self.labelCount.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) }) }) { (finish) in UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 8, options: .curveEaseInOut, animations: { self.labelCount.text = "x\(self.num!)" self.num! += Int(add) self.labelCount.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) }, completion: { finish in }) } } }
4. When the custom view is complete, consider animations that pan from left to up.Pan right from the left and pan right through the UIView animation. Then pan up here, considering using scrollview's contentOffset to control it.Each time you add a gift view, pan up a fixed offset, then add the fixed offset to its y value when the next view is added.
class SendGiftAnimationScrollView: UIScrollView { var numHeight:CGFloat! var viewY:CGFloat! var margin:CGFloat! override init(frame: CGRect) { super.init(frame: frame) numHeight = 0 viewY = 350 margin = 85 } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func addAnimationLabel(dict: [String:String]){ let animate = AnimationLabel(frame: CGRect(x: -100, y: self.viewY, width: 100, height: 80)) let model = AnimationModel(dict: dict) animate.model = model animate.giftCount = model.giftNum self.addanimationView(sender: animate) self.viewY! += margin } private func addanimationView(sender:AnimationLabel) { var frame = sender.frame frame.origin.x = 0 self.addSubview(sender) UIView.animate(withDuration: 0.5, animations: { sender.frame = frame }) { (finish) in let point = CGPoint(x: 0, y: self.numHeight) self.setContentOffset(point, animated: true) sender.startTimer() } self.numHeight! += margin } }
5. Last look at the call
var scrollView: SendGiftAnimationScrollView? override func viewDidLoad() { super.viewDidLoad() self.scrollView = SendGiftAnimationScrollView(frame: CGRect(x: 0, y: 0, width: view.frame.width*0.7, height: view.frame.height)) self.scrollView?.backgroundColor = UIColor(white: 0, alpha: 0.7) view.addSubview(self.scrollView!) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { let dic = ["userhead":"Head portrait","giftnum":"99","giftheadimage":"Gift 1","giftusername":"Different fireworks","giftname":"An crown"] let dic2 = ["userhead":"Avatar 2","giftnum":"9","giftheadimage":"Gift 2","giftusername":"uncomplicated","giftname":"Red lips"] self.scrollView?.addAnimationLabel(dict: dic) self.scrollView?.addAnimationLabel(dict: dic2) }