【问题标题】:Reverse animation when button is pressed for second time in Table View在表格视图中第二次按下按钮时反转动画
【发布时间】:2021-01-20 04:45:10
【问题描述】:

我知道以前有人问过这个问题,但在我的情况下无法弄清楚如何以最短的方式实现这一点。

当我点击“添加”按钮(参见下面的 GIF)时,动画功能会为“imageView”(在本例中为银河图像)设置动画以飞到购物车,即“notificationButton”。此外,“添加”按钮更改为“删除”,按钮颜色从黑色变为红色(参见下面的 GIF)。没关系。

现在,当我第二次单击按钮时,即取消选择它,即使其变为默认状态,一切都会反转,但图像仍然会飞到购物车!

现在,当我第二次将按钮按回其默认位置时,我想将动画飞行 imageView 动画反转到其原始位置,如果再次按我想要的次数再次按下按钮,则再次返回原始飞行动画。

虽然我在这里添加了完整的 ProductViewController 代码,但是你跳过所有内容并查看最后一个扩展 ProductViewController

我知道这很可能有两个步骤 -

i) 识别第二次按下“buttonHandlerAddToCart”按钮,即从选定/被选定步骤到默认步骤。

ii) 在 ProductViewController 中反转动画函数“func animation”。

怎么办?

相关代码:

SSBadgeButton:-

导入 UIKit

class SSBadgeButton: UIButton {

  var badgeLabel = UILabel()

   var badge: String? {
    didSet {
        addBadgeToButon(badge: badge)
    }
   }

   public var badgeBackgroundColor = UIColor.red {
    didSet {
        badgeLabel.backgroundColor = badgeBackgroundColor
    }
   }

   public var badgeTextColor = UIColor.white {
    didSet {
        badgeLabel.textColor = badgeTextColor
    }
  }

public var badgeFont = UIFont.systemFont(ofSize: 12.0) {
    didSet {
        badgeLabel.font = badgeFont
    }
}

public var badgeEdgeInsets: UIEdgeInsets? {
    didSet {
        addBadgeToButon(badge: badge)
    }
}

override init(frame: CGRect) {
    super.init(frame: frame)
    addBadgeToButon(badge: nil)
}

func addBadgeToButon(badge: String?) {
    badgeLabel.text = badge
    badgeLabel.textColor = badgeTextColor
    badgeLabel.backgroundColor = badgeBackgroundColor
    badgeLabel.font = badgeFont
    badgeLabel.sizeToFit()
    badgeLabel.textAlignment = .center
    let badgeSize = badgeLabel.frame.size
    
    let height = max(18, Double(badgeSize.height) + 5.0)
    let width = max(height, Double(badgeSize.width) + 10.0)
    
    var vertical: Double?, horizontal: Double?
    if let badgeInset = self.badgeEdgeInsets {
        vertical = Double(badgeInset.top) - Double(badgeInset.bottom)
        horizontal = Double(badgeInset.left) - Double(badgeInset.right)
        
        let x = (Double(bounds.size.width) - 10 + horizontal!)
        let y = -(Double(badgeSize.height) / 2) - 10 + vertical!
        badgeLabel.frame = CGRect(x: x, y: y, width: width, height: height)
    } else {
        let x = self.frame.width - CGFloat((width / 2.0))
        let y = CGFloat(-(height / 2.0))
        badgeLabel.frame = CGRect(x: x, y: y, width: CGFloat(width), height: CGFloat(height))
       }
    
    badgeLabel.layer.cornerRadius = badgeLabel.frame.height/2
    badgeLabel.layer.masksToBounds = true
    addSubview(badgeLabel)
    badgeLabel.isHidden = badge != nil ? false : true
   }

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    self.addBadgeToButon(badge: nil)
    fatalError("init(coder:) has not been implemented")
   }
 }

ProductViewController 代码:

import UIKit

class ProductViewController: UIViewController, UITableViewDataSource,
                             UITableViewDelegate {
    let notificationButton = SSBadgeButton()
    let rightbarbuttonimage = UIImage(named:"ic_cart")
    fileprivate var cart = Cart()
    let scrollView = UIScrollView()
    let sections = ["Section A", "Section B","Section C", "Section D","Section   E","Section F","Section G","Section H", "Section I","Section J","Section K","Section L"]
    let rowspersection = [2,3,1,2,2,3,3,1,4,2,1,2]
    
    
    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
        self.tableView.backgroundColor = UIColor.gray
        
        //Add and setup scroll view
        self.tableView.addSubview(self.scrollView)
        self.scrollView.translatesAutoresizingMaskIntoConstraints = false;
        
        //Constrain scroll view
        self.scrollView.leadingAnchor.constraint(equalTo: self.tableView.leadingAnchor, constant: 20).isActive = true;
        self.scrollView.topAnchor.constraint(equalTo: self.tableView.topAnchor, constant: 20).isActive = true;
        self.scrollView.trailingAnchor.constraint(equalTo: self.tableView.trailingAnchor, constant: -20).isActive = true;
        self.scrollView.bottomAnchor.constraint(equalTo: self.tableView.bottomAnchor, constant: -20).isActive = true;
        
        // customising rightBarButtonItems as notificationbutton
        notificationButton.frame = CGRect(x: 0, y: 0, width: 44, height: 44)
        notificationButton.setImage(UIImage(named: "ic_cart")?.withRenderingMode(.alwaysTemplate), for: .normal)
        notificationButton.badgeEdgeInsets = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 15)
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: notificationButton)
        
        
        
        //following register is needed because I have rightbarbuttonitem customised as   uibutton i.e.  notificationbutton
        notificationButton.addTarget(self, action: #selector(self.registerTapped(_:)), for: .touchUpInside)
    }
    @objc func registerTapped(_ sender: UIButton) {
        self.performSegue(withIdentifier: "showCart", sender: nil)
    }
    
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        
        
        
        //Workaround to avoid the fadout the right bar button item
        self.navigationItem.rightBarButtonItem?.isEnabled = false
        self.navigationItem.rightBarButtonItem?.isEnabled = true
        
        //Update cart if some items quantity is equal to 0 and reload the product table and right button bar item
        cart.updateCart()
        
        
        //self.navigationItem.rightBarButtonItem?.title = "Checkout (\(cart.items.count))"
        notificationButton.badge = String(cart.items.count)// making badge equal to no.ofitems in cart
        
        tableView.reloadData()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    // this segue to transfer data
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showCart" {
            if let cartViewController = segue.destination as? CartViewController {
                cartViewController.cart = self.cart
            }
        }
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return productMap.count
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return productMap[section]?.count ?? 0
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let product = productMap[indexPath.section]![indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "ProductTableViewCell") as! ProductTableViewCell
        cell.imageView?.image =  product.imagename
        cell.delegate = self as CartDelegate
        cell.setButton(state: self.cart.contains(product: product))
        return cell
    }
    
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 44
    }
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        
        switch(section) {
        case 0: return "Section A"
        case 1: return "Section B"
        case 2: return "Section C"
        case 3: return "Section D"
        case 4: return "Section E"
        case 5: return "Section F"
        case 6: return "Section G"
        case 7: return "Section H"
        case 8: return "Section I"
        case 9: return "Section J"
        case 10: return "Section K"
        case 11: return "Section L"
        default: return ""
        }
    }
}

extension ProductViewController: CartDelegate {
    
    // MARK: - CartDelegate
    func updateCart(cell: ProductTableViewCell) {
        guard let indexPath = tableView.indexPath(for: cell) else { return }
        let product = productMap[indexPath.section]![indexPath.row]
        
        //Update Cart with product
        cart.updateCart(with: product)
        // self.navigationItem.rightBarButtonItem?.title = "Checkout (\(cart.items.count))"
        notificationButton.badge = String(cart.items.count) // making badge equal to noofitems in cart
        
    }
}

***// Most relevant code begins here -***

extension ProductViewController {
    
    @IBAction func buttonHandlerAddToCart(_ sender: UIButton) {
        
        let buttonPosition : CGPoint = sender.convert(sender.bounds.origin, to: self.tableView)
        
        let indexPath = self.tableView.indexPathForRow(at: buttonPosition)!
        
        let cell = tableView.cellForRow(at: indexPath) as! ProductTableViewCell
        
        let imageViewPosition : CGPoint = cell.imageView!.convert(cell.imageView!.bounds.origin, to: self.view)
        
        
        let imgViewTemp = UIImageView(frame: CGRect(x: imageViewPosition.x, y: imageViewPosition.y, width: cell.imageView!.frame.size.width, height: cell.imageView!.frame.size.height))
        
        imgViewTemp.image = cell.imageView!.image
        
        animation(tempView: imgViewTemp)
    }
    
    func animation(tempView : UIView)  {
        self.view.addSubview(tempView)
        UIView.animate(
            withDuration: 1.0,
            animations: {
                tempView.animationZoom(scaleX: 1.5, y: 1.5)
            }, completion: { _ in
                
                UIView.animate(withDuration: 0.5, animations: {
                    
                    
                    tempView.animationZoom(scaleX: 0.2, y: 0.2)
                    tempView.animationRoted(angle: CGFloat(Double.pi))
                    
                    tempView.frame.origin.x = self.notificationButton.frame.origin.x
                    tempView.frame.origin.y = self.notificationButton.frame.origin.y
                    
                }, completion: { _ in
                    
                    tempView.removeFromSuperview()
                    
                    UIView.animate(withDuration: 1.0, animations: {
                        
                        
                        self.notificationButton.animationZoom(scaleX: 1.4, y: 1.4)
                    }, completion: {_ in
                        self.notificationButton.animationZoom(scaleX: 1.0, y: 1.0)
                    })
                    
                })
                
            }
        )
    }
}

extension UIView{
    func animationZoom(scaleX: CGFloat, y: CGFloat) {
        self.transform = CGAffineTransform(scaleX: scaleX, y: y)
    }
    
    func animationRoted(angle : CGFloat) {
        self.transform = self.transform.rotated(by: angle)
    }
}

我还包含了 ProductTableViewCell 代码,以防万一:

import UIKit

protocol CartDelegate {
    func updateCart(cell: ProductTableViewCell)
}

class ProductTableViewCell: UITableViewCell {
    
    weak var myParent:ProductViewController?
    
    @IBOutlet weak var imagename: UIImageView!
    @IBOutlet weak var addToCartButton: UIButton!
    
    var delegate: CartDelegate?
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        
        addToCartButton.layer.cornerRadius = 5
        addToCartButton.clipsToBounds = true
    }
    
    func setButton(state: Bool) {
        addToCartButton.isUserInteractionEnabled = true
        addToCartButton.isSelected = state
        addToCartButton.backgroundColor = (!addToCartButton.isSelected) ? .blue : .red
    }
    
    @IBAction func addToCart(_ sender: Any) {
        setButton(state: !addToCartButton.isSelected)
        self.delegate?.updateCart(cell: self)
    }
}

编辑:应@aheze 的要求:

struct Product: Equatable {
    let imagename: UIImage
}

var productMap = [
    0: [ Product(imagename:#imageLiteral(resourceName: "blue")), Product( imagename:#imageLiteral(resourceName: "CakeImage")) ]
    1: [ Product(imagename:#imageLiteral(resourceName: "vectorlogo")), Product(imagename:#imageLiteral(resourceName: "PeasImge")), Product(imagename:#imageLiteral(resourceName: "castle"))],
    2: [ Product( imagename:#imageLiteral(resourceName: "scoobydoo")),Product(imagename:#imageLiteral(resourceName: "ufo"))] ,
    3: [ Product( imagename:#imageLiteral(resourceName: "wolfsky")),Product( imagename:#imageLiteral(resourceName: "universe")) ],
    4: [ Product(imagename:#imageLiteral(resourceName: "werewolf")),Product(  imagename:#imageLiteral(resourceName: "galaxy")) ]
]

编辑 2:Cart 类,应 @aheze 的要求:

import Foundation

class Cart {
    var items : [CartItem] = []
}

extension Cart {
    
    var totalQuantity : Int {
        get { return items.reduce(0) { value, item in
            value + item.quantity
        }
        }
    }
    func updateCart(with product: Product) {
        if !self.contains(product: product) {
            self.add(product: product)
        } else {
            self.remove(product: product)
        }
    }
    func updateCart() {
        
        for item in self.items {
            if item.quantity == 0 {
                updateCart(with: item.product)
            }
        }
    }
    
    func add(product: Product) {
        let item = items.filter { $0.product == product }
        
        if item.first != nil {
            item.first!.quantity += 1
        } else {
            items.append(CartItem(product: product))
        }
    }
    
    func remove(product: Product) {
        guard let index = items.firstIndex(where: { $0.product == product  })     else { return}
        items.remove(at: index)
    }
    
    
    func contains(product: Product) -> Bool {
        let item = items.filter { $0.product == product }
        return item.first != nil
    }
}

您需要更多信息,请随时...

【问题讨论】:

  • 如您所见,就声誉而言,我在镇上有点穷,但由于时间紧迫,我仍然提供了赏金。请调查此事。
  • 您能说明productMap 的定义位置吗?
  • 抱歉回复晚了。请参阅编辑。
  • 但据我所知,它与 productMap 无关。它必须与 ProductTableViewCell 和/或 ProductViewController 中的标记按钮,并在 ProductViewController 中反转完成汉斯勒。
  • 谢谢。为了跟踪选择了哪个产品,您应该创建一个新数组selectedProducts,并在选择产品时将产品附加到该数组中。然后,您可以检查其中有哪些产品,并调整动画。要反转,我建议只制作一个新的动画功能。我现在需要去,但我明天会尝试发布一些代码

标签: ios swift uitableview


【解决方案1】:

这行得通吗? (gif太大了) https://imgur.com/a/jrcwEWv

我为购物车中的addingremoving 分别创建了函数。

extension ProductViewController: CartDelegate {
    
    // MARK: - CartDelegate
    func updateCart(cell: ProductTableViewCell) {
        guard let indexPath = tableView.indexPath(for: cell) else { return }
        let product = productMap[indexPath.section]![indexPath.row]
        
/// `var selectedIndexPaths = [IndexPath]()` defined inside `ProductViewController`, to keep track of the selected products
        if selectedIndexPaths.contains(indexPath) {
            if let index = selectedIndexPaths.firstIndex(of: indexPath) {
                selectedIndexPaths.remove(at: index)
                removeProductFromCart(indexPath: indexPath)
            }
        } else {
            selectedIndexPaths.append(indexPath)
            addProductToCart(indexPath: indexPath)
        }
        
//        addProductToCart(indexPath: indexPath)
        ///  **I commented this out because I don't have the code for `Cart`**
        //Update Cart with product
//        cart.updateCart(with: product)
        // self.navigationItem.rightBarButtonItem?.title = "Checkout (\(cart.items.count))"
//        notificationButton.badge = String(cart.items.count) // making badge equal to noofitems in cart
        
    }
}

func addProductToCart(indexPath: IndexPath) {
    if let cell = tableView.cellForRow(at: indexPath) as? ProductTableViewCell {
        if let imageView = cell.imagename {
            
            let initialImageViewFrame = imageView.convert(imageView.frame, to: self.view)
            let targetImageViewFrame = self.notificationButton.frame
            
            let imgViewTemp = UIImageView(frame: initialImageViewFrame)
            imgViewTemp.clipsToBounds = true
            imgViewTemp.contentMode = .scaleAspectFill
            imgViewTemp.image = imageView.image
            
            self.view.addSubview(imgViewTemp)
            
            UIView.animate(withDuration: 1.0, animations: {
                imgViewTemp.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
            }) {  _ in
                UIView.animate(withDuration: 0.5, animations: {
                    imgViewTemp.transform = CGAffineTransform(scaleX: 0.2, y: 0.2).rotated(by: CGFloat(Double.pi))
                    imgViewTemp.frame = targetImageViewFrame
                }) { _ in
                    imgViewTemp.removeFromSuperview()

                    UIView.animate(withDuration: 1.0, animations: {
                        self.notificationButton.transform = CGAffineTransform(scaleX: 1.4, y: 1.4)
                    }, completion: {_ in
                        self.notificationButton.transform = CGAffineTransform.identity
                    })
                }
            }
        }
    }
}

func removeProductFromCart(indexPath: IndexPath) {
    if let cell = tableView.cellForRow(at: indexPath) as? ProductTableViewCell {
        if let imageView = cell.imagename {
            
            let initialImageViewFrame = self.notificationButton.frame
            let targetImageViewFrame = imageView.convert(imageView.frame, to: self.view)
            
            let imgViewTemp = UIImageView(frame: initialImageViewFrame)
            imgViewTemp.clipsToBounds = true
            imgViewTemp.contentMode = .scaleAspectFill
            imgViewTemp.image = imageView.image
            
            self.view.addSubview(imgViewTemp)
            
            var initialTransform = CGAffineTransform.identity
            initialTransform = initialTransform.scaledBy(x: 0.2, y: 0.2)
            initialTransform = initialTransform.rotated(by: CGFloat(Double.pi))
            
            UIView.animate(withDuration: 0.5, animations: {
                self.notificationButton.animationZoom(scaleX: 1.4, y: 1.4)
                imgViewTemp.transform = initialTransform
            }) {  _ in
                UIView.animate(withDuration: 1, animations: {
                    self.notificationButton.animationZoom(scaleX: 1, y: 1)
                    imgViewTemp.transform = CGAffineTransform.identity
                    imgViewTemp.frame = targetImageViewFrame
                }) { _ in
                    imgViewTemp.removeFromSuperview()
                }
            }
        }
    }
}
        

一些你应该解决的问题:

  • 您没有使用imagename(添加到表格视图单元格的图像视图),而是使用了cell.imageView!,这是所有单元格都具有的内置图像视图。不要使用这个。
  • ProductTableViewCell 内部,您应该创建一个单独的属性来跟踪选中/未选中状态,而不是使用UIButtonisSelected。这样,您在更改按钮颜色时就不会遇到不必要的行为(目前,按钮文本后面会出现一个红色矩形)
  • 如果您正在组合变换,您应该这样做:
var initialTransform = CGAffineTransform.identity
initialTransform = initialTransform.scaledBy(x: 0.2, y: 0.2)
initialTransform = initialTransform.rotated(by: CGFloat(Double.pi))
tempView.transform = initialTransform

代替:

tempView.animationZoom(scaleX: 0.2, y: 0.2)
tempView.animationRoted(angle: CGFloat(Double.pi))

这是full project(也添加了更多的 cmets)。

【讨论】:

  • 是的,我从github上运行了你的ProductViewController,经过仔细的匹配和纠正,它仍然没有后退动画,只有前进动画。此外,如果我将 ProductTableViewcell 代码从您复制到我的,它会在 AppDelegate 中显示 sigbart 错误,在控制台中显示此错误 - “由于未捕获的异常 'NSUnknownKeyException' 导致应用程序终止,原因:'[ setValue:forUndefinedKey:] : 这个类不符合键名的键值编码。'*** First throw call stack:"
  • 添加了“class Cart”,但我仍然觉得它只与切换 ProductTableViewCell 中的添加按钮和反转动画有关。
  • @askit Terminating app due to uncaught exception 可能是因为您需要设置单元格的模块。在情节提要中,只需删除当前类,然后再次输入paste it(它应该会自动完成)。
  • @askit 还是没有倒退动画?确保您已定义 selectedIndexPaths
  • 是的,我今天和你在线。好的,您的意思是 ProductViewcontroller 中的“var selectedIndexPaths = [IndexPath]()”?是的,它在那里。仍然没有反向动画。此外,每次单击“添加”按钮时,它都会在控制台中打印“向上”。休息,没关系,即从购物车中添加和删除。
猜你喜欢
  • 2020-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多