将 IndexPath 行值传递给委托函数而不是 Sender.tag 以删除图像项 CollectionView Swift

Posted

技术标签:

【中文标题】将 IndexPath 行值传递给委托函数而不是 Sender.tag 以删除图像项 CollectionView Swift【英文标题】:Pass IndexPath row value to delegate function instead of Sender.tag to delete Image item CollectionView Swift 【发布时间】:2019-12-24 11:33:45 【问题描述】:

我正在使用 collectionView 来显示照片并委托函数来查看自定义警报。 在照片上,我有用于删除照片的十字标记。我的委托功能和显示项目都工作正常。 但是当我必须从服务器中删除照片时,我遇到了问题。因为我需要将确切的图像 ID 传递给 Web 服务才能将其从服务器中删除。如果我使用 cell.tag 的东西,它给我的行值为 1,但实际的 imgID 是 40992。我怎样才能将此值传递给我的删除委托函数?

结构:

单元格项目显示 --tap 手势调用 removeImage func --- 自定义警报 -- 在 Delete 按钮上 -- didDeleteButtonClicked 调用。

我在 cellForItem 中需要的主要值:

let imgId = AppData?.imageList?[indexPath.row].projectUnitImageId

照片视图控制器:

public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellName, for: indexPath) as! PhotoCollectionViewCell

            if(indexPath.row < (AppData?.imageList?.count ?? 0))
                cell.imageView.isHidden = false
                cell.closeIcon.isHidden = false
                cell.addIcon.isHidden = true
                let dic = AppData?.imageList?[indexPath.row].url ?? " "
                cell.imageView.image =  UIImage(url: URL(string: dic))


                let imgId = AppData?.imageList?[indexPath.row].projectUnitImageId
                print(imgId)

                cell.closeIcon.isUserInteractionEnabled = true
                cell.closeIcon.tag = imgId ?? 0
                deleteAlertView.delegate = self
                let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(removeImage(_:)))
                cell.closeIcon.addGestureRecognizer(tapGestureRecognizer)

             else 
                cell.imageView.isHidden = true
                cell.closeIcon.isHidden = true
                cell.addIcon.isHidden = false
            

            return cell
        


        @objc func removeImage(_ sender: AnyObject)

            print(imgId)
            let fotoXib = Bundle.main.loadNibNamed("FotoDeleteAlert", owner: self, options: nil)
            let alertView = fotoXib?.first as! FotoDeleteAlert
            alertView.delegate = self
            self.view.addSubview(alertView)

        


          //MARK: - Delegate Function

extension PhotoCollectionViewController: handleDeleteAction 

        func didDeleteButtonClicked(_ sender: UIButton) 

            print("delegate")
            let row = sender.tag
            print(row)

            // I have to call my delete webServices here and have to pass imgId
            deleteWebservices(imgId)


        
    

FotoAlert Xib 自定义提醒:

protocol handleDeleteAction 
    func didDeleteButtonClicked(_: UIButton)


@IBDesignable class FotoDeleteAlert: UIView 

    var delegate: handleDeleteAction?

    @IBOutlet weak var deleteBtn: UIButton!

    override func awakeFromNib() 
        super.awakeFromNib()
        layoutIfNeeded()
        deleteBtn.addTarget(self, action: #selector(didDelete(_:)), for: .touchUpInside)
    

    @IBAction func didCancel(_ sender: Any) 
        removeFromSuperview()
    

    @IBAction func didDelete(_ sender: UIButton) 

        self.delegate?.didDeleteButtonClicked(sender)
        removeFromSuperview()


    

【问题讨论】:

请添加此行并检查 propar indexpath 行 cell.deleteBtn.tag = indexpath.row 单元格是可重复使用的,因此您无法使用单元格标签删除,因为创建的单元格数量有限,这些单元格在屏幕上可见,下次单元格重用时,如果只有 10 个单元格可见,那么您获得的最大单元格标签为 9甚至项目数以百万计。 【参考方案1】:

TL;DR;

您设置了cell.closeIcon 的标签,但随后您使用了FotoDeleteAlert 按钮的标签。

要解决您需要添加的问题


class FotoDeleteAlert: ... 
   ...

   func setButtonTag(imageId: Int) 
       button.tag = imageId
   



 @objc func removeImage(_ sender: UIView)

    print(imgId)
    let fotoXib = Bundle.main.loadNibNamed("FotoDeleteAlert", owner: self, options: nil)
    let alertView = fotoXib?.first as! FotoDeleteAlert
    alertView.setButtonTag(imageId: sender.tag
    alertView.delegate = self
    self.view.addSubview(alertView)



extension PhotoCollectionViewController: handleDeleteAction 

        func didDeleteButtonClicked(_ sender: UIButton) 

            print("delegate")
            let imgId = sender.tag


            // I have to call my delete webServices here and have to pass imgId
            deleteWebservices(imgId)


        
    

现在让我们清理你的意大利面条代码

您的大部分collectionView(_:, cellForItemAt: ) 可以移动到PhotoCollectionViewCell。 我不会通过tag 发送 id,而是您可以创建自己的委托

最后我会把它改写成这样的:

class Controller 
    func displayAlert(for info: PhotoInfo) 
        // I bet this code is invalid (view isn't correctly aligned

        let fotoXib = Bundle.main.loadNibNamed("FotoDeleteAlert", owner: self, options: nil)
        let alertView = fotoXib?.first as! FotoDeleteAlert
        alertView.delegate = self
        self.view.addSubview(alertView)
    


extension Controller: UICollectionViewDelegate 
    func collectionView(
        _ collectionView: UICollectionView, 
        cellForItemAt indexPath: IndexPath
    ) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCell(
            withReuseIdentifier: cellName, for: indexPath
        ) as! PhotoCollectionViewCell

        let imgInfo: PhotoInfo? = AppData?.imageList?[indexPath.row]
        cell.display(info: imgInfo)
        cell.delegate = self

        return cell
    


extension Controller: PhotoCollectionViewCellDelegate 
    func photoCollectionViewCell(didPressCloseBtn cell: PhotoCollectionViewCell) 
        guard let indexPath = collectionView.indexPath(for: cell) else  return 
        if let imgInfo: PhotoInfo = AppData?.imageList?[indexPath.row] 
            displayAlert(for: imgInfo)
        
    


extension Controller: FotoDeleteAlertDelegate 
    func fotoDeleteAlert(
        didPressDelete view: FotoDeleteAlert, for item: PhotoInfo?
    ) 
        guard let item: PhotoInfo = item else  return 
        deleteWebservices(item.projectUnitImageId)
    



protocol PhotoCollectionViewCellDelegate: class 
    func photoCollectionViewCell(didPressCloseBtn: PhotoCollectionViewCell)


class PhotoCollectionViewCell: UICollectionViewCell 
    weak var delegate: PhotoCollectionViewCellDelegate?

    var closeIcon: UIButton! 
        didSet 
            button.addTarget(
                self, action: #selector(closeTap), for: .touchUpInside
            )
        
    


    func display(info: PhotoInfo?) 
        imageView.isHidden = info == nil
        closeIcon.isHidden = info == nil
        addIcon.isHidden = info != nil

        if let info = info 
            imageView.image =  UIImage(url: URL(string: info.url))
        
    

    @objc func closeTap() 
        delegate?.photoCollectionViewCell(didPressCloseBtn: self)
    



protocol FotoDeleteAlertDelegate: class 
    func fotoDeleteAlert(
        didPressDelete view: FotoDeleteAlert, for item: PhotoInfo?
    )



class FotoDeleteAlert 
    weak var delegate: FotoDeleteAlertDelegate?

    var deleteButton: UIButton! 
        didSet 
            button.addTarget(
                self, action: #selector(deleteTap), for: .touchUpInside
            )
        
    

    private var item: PhotoInfo?

    func display(item: PhotoInfo) 
        self.item = item
    

    @objc func deleteTap() 
        delegate?.fotoDeleteAlert(didPressDelete: self, for: item)
    


【讨论】:

再次感谢您提供如此有用和优化的代码。【参考方案2】:

你需要

guard let id = AppData?.imageList?[row].projectUnitImageId else  return   
deleteWebservices(id)

【讨论】:

完美。我已经实现了这一点并且工作得很好。【参考方案3】:

标签是查找用户与之交互的特定单元格的一种脆弱方式。相反,我建议使用按钮的坐标。

我给这个帖子写了一个答案:swift: how to get the indexpath.row when a button in a cell is tapped?

在那个答案中,我写了一个 UITableView 的扩展,indexPathForView(_:) 该函数接受一个视图(包含在表格视图单元格中)并返回包含该视图的单元格的 IndexPath。

您可以对集合视图使用完全相同的方法。表视图具有函数indexPathForRow(at:),集合视图具有等效函数indexPathForItem(at:)

UICollection 视图的扩展看起来像这样:(未测试)

import UIKit

public extension UICollectionView 

    /**
     This method returns the indexPath of the cell that contains the specified view
     - Parameter view: The view to find.
     - Returns: The indexPath of the cell containing the view, or nil if it can't be found
     */

    func indexPathForView(_ view: UIView) -> IndexPath? 
        let center = view.center

        //The center of the view is a better point to use, but we can only use it if the view has a superview
        guard let superview = view.superview else 
            //The view we were passed does not have a valid superview.
            //Use the view's bounds.origin and convert from the view's coordinate system
            let origin = self.convert(view.bounds.origin, from: view)
            let indexPath = self.indexPathForItem(at: origin)
            return indexPath
        
        let viewCenter = self.convert(center, from: superview)
        let indexPath = self.indexPathForItem(at: viewCenter)
        return indexPath
    

重构您的 FotoDeleteAlert 以具有 imgID 属性。让它的 didDeleteButtonClicked 方法传回图像 ID,而不是点击的按钮:

protocol handleDeleteAction 
    func didDeleteButtonClickedForImageID(_: Integer)

然后你需要重写你的 removeImage(_:) 函数来获取手势识别器并使用它来找到 IndexPath:

    @objc func removeImage(_ tapper: UIGetstureRecognizer) 
        //Find the view associated with the tap gesture recognizer
        guard let view = tapper.view, 
        //use the view to find the indexPath of the cell
        let indexPath = collectionView. indexPathForView(view) else  
            return 
        
        let imgId = AppData?.imageList[indexPath.row].projectUnitImageId
        let fotoXib = Bundle.main.loadNibNamed("FotoDeleteAlert", owner: self, options: nil)
        let alertView = fotoXib?.first as! FotoDeleteAlert
        alertView.delegate = self

        //Pass the image ID to the FotoDeleteAlert
        alertView.imgID = imgID
        self.view.addSubview(alertView)
    

然后在 FotoDeleteAlert 的删除处理程序中,您可以获取图像 ID 并使用它向您的服务器发出删除命令。

【讨论】:

以上是关于将 IndexPath 行值传递给委托函数而不是 Sender.tag 以删除图像项 CollectionView Swift的主要内容,如果未能解决你的问题,请参考以下文章

将单元格从委托传递到 UITableViewController 时 indexPath 为零

如何将 uitableview 选择与 indexpath 值居中

将数据表行值传递给模态

将 Array 传递给 XLPagerTabBar 委托函数以打开 VC

如何将 crud 表中的行值传递给自定义提交按钮

Swift:如何将从委托接收到的值传递给函数的完成块?