XZ_iOS 之 加入购物车bounce效果实现

效果图:

动画类 XZAddToCartAnimation 的实现:


import UIKit
import Foundation

let screenWidth = UIScreen.main.bounds.size.width
let screenHeight = UIScreen.main.bounds.size.height

class XZAddToCartAnimation: NSObject {
    
    private var layer: CALayer?
    
    typealias animationFinisnBlock = (_ finish: Bool) -> Void
    
    private var completion: animationFinisnBlock?
    
    override init() {
        super.init()
    }

    /// 开始动画
    func startAnimationWithView(addView: UIView, rect: CGRect, finishPoint: CGPoint, completion: @escaping animationFinisnBlock) {
        
        layer = CALayer()
        layer!.contents = addView.layer.contents
        layer!.contentsGravity = CALayerContentsGravity.resizeAspectFill
        // 重置图层尺寸
        var rect = rect
        rect.size = CGSize(width: 60, height: 60)
        layer!.bounds = rect
        layer!.cornerRadius = rect.size.width / 2.0
        layer!.masksToBounds = true // 圆角
        
        // 添加到window上
        let keyWindow = UIApplication.shared.keyWindow
        keyWindow?.layer.addSublayer(layer!)
        // 开始点
        layer!.position = CGPoint(x: rect.origin.x + addView.frame.size.width / 2.0, y: rect.midY)
        
        // 路径
        createAnimationWithRect(rect: rect, finishPoint: finishPoint)
        
        // 回调
        self.completion = completion
    }
    
    /// 上下 bounces 的动画
    func shakeAnimation(shakeView: UIView) {
        let shakeAnimation = CABasicAnimation(keyPath: "transform.translation.y")
        shakeAnimation.duration = 0.25
        shakeAnimation.fromValue = NSNumber(value: -5)
        shakeAnimation.toValue = NSNumber(value: 5)
        shakeAnimation.autoreverses = true
        shakeView.layer.add(shakeAnimation, forKey: nil)
    }
    
    /// 创建动画
    private func createAnimationWithRect(rect: CGRect, finishPoint: CGPoint) {
        
        // 路径动画
        let path = UIBezierPath()
        path.move(to: layer!.position)
        path.addQuadCurve(to: finishPoint, controlPoint: CGPoint(x: screenWidth / 2.0, y: rect.origin.y - 80))
        
        let pathAnimation = CAKeyframeAnimation(keyPath: "position")
        pathAnimation.path = path.cgPath
        
        // 旋转动画
        let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
        rotateAnimation.isRemovedOnCompletion = true
        rotateAnimation.fromValue = NSNumber(value: 0)
        rotateAnimation.toValue = NSNumber(value: 12)
        rotateAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
        
        // 添加动画组合
        let groups = CAAnimationGroup()
        groups.animations = [pathAnimation, rotateAnimation]
        groups.duration = 1.2
        groups.isRemovedOnCompletion = false
        groups.fillMode = CAMediaTimingFillMode.forwards
        groups.delegate = self
        layer!.add(groups, forKey: "group")
    }
    
}

// MARK: - CAAnimationDelegate
extension XZAddToCartAnimation: CAAnimationDelegate {
   
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        
        if anim == layer!.animation(forKey: "group") {
            layer!.removeFromSuperlayer()
            layer = nil
            
            completion?(true)
        }
    }
    
}

 列表页的代码实现

// 列表页

import UIKit

class XZListViewController: UIViewController {
    
    private var iconNumber:Int = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    
}

extension XZListViewController: UITableViewDelegate, UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return 10
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "XZListViewCell", for: indexPath) as! XZListViewCell
        
        cell.addActionCompleted = { imageView in
            
            var rect = tableView.rectForRow(at: indexPath)
            rect.origin.y = rect.origin.y - tableView.contentOffset.y
            var imageRect = imageView.frame
            imageRect.origin.y = rect.origin.y + imageRect.origin.y
            XZAddToCartAnimation().startAnimationWithView(addView: imageView, rect: imageRect, finishPoint: CGPoint(x: screenWidth / 4 * 3, y: screenHeight - 49), completion: { (finish) in
                
               if let tabBarBtn = self.tabBarController?.tabBar.subviews[3]
              {
                XZAddToCartAnimation().shakeAnimation(shakeView: tabBarBtn)
                
                    self.iconNumber += 1
                   self.tabBarController?.tabBar.xz_showBadges(in: 2, num: self.iconNumber)
                
                // 测试移除功能
                    if self.iconNumber == 5 {
                        self.tabBarController?.tabBar.xz_hiddenBadges(in: 2)
                    }
                }
            })
        }
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        
        return 100
    }
    
}

XZListViewCell的实现:


class XZListViewCell: UITableViewCell {
    
    @IBOutlet weak var xz_imageView: UIImageView!
    
    var addActionCompleted: ((_ imageView: UIImageView)->Void)?
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
    }
    
    /// 加入购物车
    @IBAction func addToCartAction(_ sender: UIButton) {
        
        addActionCompleted?(xz_imageView)
    }
    
}

tabBar的效果修改:


import UIKit

/// 底部tabBar的总数量
let tabBarCount = 3

/// 直接添加 UILabel
extension UITabBar {
   
    /// 显示 badge
    func xz_showBadge(in tabBarIndex: Int, num: Int) {
        
        removeBadge(in: tabBarIndex)
        
        let labelBadge = UILabel()
        // 创建
        labelBadge.layer.cornerRadius = 10
        labelBadge.layer.masksToBounds = true
        labelBadge.backgroundColor = .red
        labelBadge.text = "\(num)"
        labelBadge.font = UIFont.systemFont(ofSize: 10)
        labelBadge.tag = 800 + tabBarIndex
        labelBadge.textAlignment = .center
        
        let tabFrame = self.frame
        
        // 位置
        let percentX = (CGFloat(tabBarIndex) + 0.6) / CGFloat(tabBarCount)
        let x = ceilf(Float(percentX * tabFrame.size.width))
        let y = ceilf(Float(0.1 * tabFrame.size.height))
        labelBadge.frame = CGRect(x: CGFloat(x), y: CGFloat(y), width: 20, height: 20)
        
        // 添加红点
        self.addSubview(labelBadge)
    }
    
    /// 隐藏 badge
    func xz_hiddenBadge(in tabBarIndex: Int) {
        
        removeBadge(in: tabBarIndex)
    }
    
    /// 移除 badge
    private func removeBadge(in tabBarIndex: Int) {
        
        for sub in subviews {
            if sub.tag == 800 + tabBarIndex {
                sub.removeFromSuperview()
            }
        }
    }
    
}

/// 使用关联属性添加 UILabel
extension UITabBar {
    
    struct associatedKey {
        static var key = "xz_badge"
    }
    
    private var xz_labelBadge: UILabel? {
        set {
            if let newValue = newValue {
                
                objc_setAssociatedObject(self, &(associatedKey.key), newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
        }
        
        get {
            return objc_getAssociatedObject(self, &(associatedKey.key)) as? UILabel
        }
    }
    
    /// 显示 badge
    func xz_showBadges(in tabBarIndex: Int, num: Int) {
        
        /// 防止重复添加
        var hasBadge = false
        
        for sub in subviews {
            if sub == xz_labelBadge {
                hasBadge = true
            }
        }
        
        if hasBadge == false {
            
            xz_labelBadge = UILabel()
            
            // 创建
            xz_labelBadge?.layer.cornerRadius = 10
            xz_labelBadge?.layer.masksToBounds = true
            xz_labelBadge?.backgroundColor = .red
            xz_labelBadge?.font = UIFont.systemFont(ofSize: 10)
            xz_labelBadge?.textAlignment = .center
            
            let tabFrame = self.frame
            
            // 位置
            let percentX = (CGFloat(tabBarIndex) + 0.6) / CGFloat(tabBarCount)
            let x = ceilf(Float(percentX * tabFrame.size.width))
            let y = ceilf(Float(0.1 * tabFrame.size.height))
            xz_labelBadge?.frame = CGRect(x: CGFloat(x), y: CGFloat(y), width: 20, height: 20)
            // 添加红点
            self.addSubview(xz_labelBadge!)
        }
        
        xz_labelBadge?.text = "\(num)"
    }
    
    /// 隐藏 badge
    func xz_hiddenBadges(in tabBarIndex: Int) {
        
        removeBadges(in: tabBarIndex)
    }
    
    /// 移除 badge
    private func removeBadges(in tabBarIndex: Int) {
        
        for sub in subviews {
            if sub == xz_labelBadge {
                sub.removeFromSuperview()
            }
        }
    }
    
}
发布了208 篇原创文章 · 获赞 52 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/understand_XZ/article/details/90718770