【问题标题】:Show number of items selected in UICollectionView显示在 UICollectionView 中选择的项目数
【发布时间】:2016-09-01 07:30:38
【问题描述】:

我正在使用 UICollectionView 来显示图像。从该 collectionView 用户可以选择多个图像。现在我可以通过覆盖 UICollectionViewCell 的选定变量来更改选定图像的视图。

override var selected: Bool {
didSet {
    if self.selected {
        imageView.layer.borderWidth = 5.0
    } else {
        imageView.layer.borderWidth = 0.0
    }
}
}

我在单元格的 UIImageView 顶部放置了一个 UILabel 以显示计数。但我无法更新所选图像的数量。当用户取消选择图像时,应更新计数。我怎样才能做到这一点?它应该类似于 Facebook 图像选择器。我不想重新加载 collectionView。

【问题讨论】:

  • 创建一个模态类,用于存储被选中的 cellIndex 并使用它来计数并显示在标签中
  • 您只需要计数吗? var count = collectionView.indexPathsForSelectedItems().count
  • 取决于您如何获得计数。要么创建一个存储所有选定项索引的数组,要么获取选定计数的计数。
  • 感谢您的回答,但我需要在不重新加载 collectionView 的情况下更新每个单元格的所有计数。在每次选择和取消选择时,我都需要更新计数。
  • @sant05 这是你想做的事:github.com/zhangao0086/DKImagePickerController

标签: ios swift uicollectionview uicollectionviewcell


【解决方案1】:

正如您在 cmets 中阐明的那样,您希望单元格中的数字显示所选图像的顺序。

添加一个 singleton 类来保存一组选定的图像 ID。单例的特点是它的实例可以从你的应用程序的任何地方访问:

class ImageSelectionManager {

    /// The singleton instance
    static let instance = ImageSelectionManager()

    /// This is the array that holds the order of selected image IDs
    var selectedImageIds: [String] = []
}

在您的UICollectionViewController 中实现以下委托函数:

class YourViewController: UICollectionViewController {

    override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

        // Get the selected cell
        let cell = collectionView.cellForItemAtIndexPath(indexPath) as! YourCell

        // Get the selected image ID
        let imageId = cell.imageId

        // Add ID
        ImageSelectionManager.instance.selectedImageIds.append(imageId)

        // Notify cells that image array has changed
        NSNotificationCenter.defaultCenter().postNotificationName(
            "ImageSelectionChangedNotification",
            object: self)
    }

    override func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {

        // Get the deselected cell
        let cell = collectionView.cellForItemAtIndexPath(indexPath) as! YourCell

        // Get the deselected image ID
        let imageId = cell.imageId

        // Remove ID
        ImageSelectionManager.instance.selectedImageIds = ImageSelectionManager.instance.selectedImageIds.filter({ $0 != imageId })

        // Notify cells that image array has changed
        NSNotificationCenter.defaultCenter().postNotificationName(
            "ImageSelectionChangedNotification",
            object: self)
    }
}

在您的UICollectionViewCell 中添加一个观察者通知和一个更新计数器标签的功能:

class YourCell: UICollectionViewCell {

    /// The ID of your image that the cell is showing
    var imageId: String!

    /// The counter label
    var counterLabel: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()

        // Start listening to notifications
        registerForNotifications()
    }
    override func prepareForReuse() {
        super.prepareForReuse()

        counterLabel.text = nil
    }

    deinit {
        unregisterFromNotifications()
    }

    func registerForNotifications() {

        // Make the cell listen to changes in the selected images array
        NSNotificationCenter.defaultCenter().addObserver(
            self,
            selector: #selector(self.handleNotification(_:)),
            name: "ImageSelectionChangedNotification",
            object: nil)
    }

    func unregisterFromNotifications() {

        // Stop listening to notifications
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }

    func handleNotification(notification: NSNotification) {

        // If the notification is the one we are listening for
        if notification.name == "ImageSelectionChangedNotification" {

            // Execute on main thread because you are changing a UI element
            dispatch_async(dispatch_get_main_queue()) {

                // Get the position in the selected images array
                if let position = ImageSelectionManager.instance.selectedImageIds.indexOf({ $0 == self.imageId }) {

                    // Image position was found so set the label text
                    self.counterLabel.text = String(position)

                } else {
                    // Image was not found no remove the label text
                    self.counterLabel.text = nil
                }
            }
        }
    }
}

它的工作原理是这样的:

  1. 一个单元格被选中/取消选中。
  2. 从单例数组中添加/删除单元格的图像 ID。
  3. 向单元格发送通知。
  4. 每个单元格都会收到通知并检查其图像 ID 在单例数组中的位置。
  5. 如果图像 ID 在数组中有位置,则标签文本会随位置更新。如果未找到图像 ID,则表示未选择单元格,因此将删除标签文本。

【讨论】:

  • 感谢您的回答。但我需要不重新加载collectionView。我怎样才能做到这一点?
  • 我尝试了其他方法,但我无法让它发挥作用。当我取消选择时,它应该将所有先前选择的计数器减少 1,而无需重新加载 collectionView。你介意详细解释一下吗?谢谢。
  • @sant05 我不建议增加和减少计数器值,而是在每次选择或取消选择单元格时计算所选单元格的数量。
  • 例如:我选择了 5 张图像,然后它将显示 1,2,3,4,5 作为每个选定图像的计数器。当我取消选择带有标签 3 的图像时,4、5 应该减少到 3 和 4。如何在不重新加载 collectionView 的情况下更新这些计数标签。如果你能帮助我,我将不胜感激。谢谢
  • 谢谢伙计,如果图像数量较少,但如果图像数量更多,并且当我向下和向上滚动时,反标签设置为 nil,有时数字也会改变。我认为这是因为细胞被重复使用了。
【解决方案2】:

首先,您需要一个模型类来存储选择状态和其他属性。
例如,您正在为您的 collectionView 中的图像填充数据。

class MyImage : NSObject {
   var name : String?
   var selectionNumber : Int? // Using for selection update on UI 
  /*other properties below*/

}

现在在您的 Collection View Cell 中使用 hooked didSet 创建一个 MyImage 属性

var image : MyImage? {
didSet {
   imageView.image = UIImage(named:image.name)
   if image.selectionNumber != nil {
      imageView.layer.borderWidth = 5.0
      lblCount.text = String(selectionNumber + 1)
   }
   else {
      imageView.layer.borderWidth = 0.0
      lblCount.text = ""
   }
}

现在在您的视图控制器类或您正在实现 collectionView 的委托方法的类中。

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier(kYour_Cell_ID, forIndexPath: indexPath) as! YourCustomCell
    let myImage = self.dataSource[indexPath.row]
    myImage.selectionNumber = self.selectedIndexes.contains(indexPath)
    cell.image = myImage
    return cell
}

您的 dataSource 将是包含 MyImage 对象的数组,并创建一个用于存储所选 indexPaths 的数组。

   func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
    {
        let idx = self.selectedIndexes.indexOf(image)
        if (idx != nil) {
            self.selectedIndexes.remove(indexPath.row)
        }
        else
        {
           self.selectedIndexes.append(indexPath.row)
        }
       self.collectionView.reloadItemsAtIndexPaths([self.selectedIndexes])
    }

【讨论】:

  • 感谢@Umair 的回答,但我不想重新加载collectionView。
  • 您有显示每个单元格计数的标签吗?如果是这种情况,那么您将不得不重新加载 collectionView,因为每个可见单元格都必须使用新的计数值进行更新。但是,如果标签不在 Cell 中而是在 viewController 的视图中,那么您可以在 func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) 中将 collectionView.reloadData() 简单地替换为 self.collectionView.reloadItemsAtIndexPaths([indexPath])
  • 我将选定的 indexPath 存储在一个名为 selectedIndex 的数组中,然后我尝试使用 self.collectionView.reloadItemsAtIndexPaths([selectedIndex]) 重新加载项目。它适用于 collectionView 项目的顶部。但是当我滚动到底部部分并尝试选择和取消选择时,它会重新加载错误的项目。我认为这是因为重复使用了单元格。
  • 您不需要将选定的 indexPath 存储在任何地方。只需更新答案中给出的选定状态并在您的collectionView 的didSelect 方法中写入self.collectionView.reloadItemsAtIndexPaths([indexPath])。重新加载特定项目将再次为该特定单元格调用 cellForItemAtIndexPath,这将根据 yourImage.selected 更新选择。要重新使用单元格,collectionView 再次调用 cellForItemAtIndexPath 并且当您更新 yourImage.selected 时,它将显示/隐藏选择。
  • 例如:我选择了 5 张图像,然后它将显示 1,2,3,4,5 作为每个选定图像的计数器。当我取消选择带有标签 3 的图像时,应将 4、5 递减为 3 和 4。如何在不重新加载 collectionView 的情况下更新这些计数标签。如果您有工作代码,请更新您的答案,我将不胜感激。
猜你喜欢
  • 2022-12-11
  • 2021-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多