Search code examples
swiftuicollectionviewphassetphimagemanager

Clicking an image in collection view selects other images


I have the following code:

import UIKit
import Photos
import PhotosUI

private let reuseIdentifier = "Cell"

class CollectionVC: UICollectionViewController, UICollectionViewDelegateFlowLayout {

    var imageArray = [UIImage]()
    
    override func viewDidLoad() {
        super.viewDidLoad()

     grapPhotos()
       

        
    }


    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        
        return imageArray.count
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath as IndexPath)
        let imageView = cell.viewWithTag(1) as! UIImageView
        
        collectionView.allowsMultipleSelection = true
        cell.layer.cornerRadius = 4
        imageView.image = imageArray[indexPath.row]
    
        return cell
    }
    override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        
        if let cell = collectionView.cellForItem(at: indexPath) {
          cell.layer.borderColor = #colorLiteral(red: 0.1411764771, green: 0.3960784376, blue: 0.5647059083, alpha: 1)
          cell.layer.borderWidth = 5
        
        }
        
        
    }
    
    override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
        if let cell = collectionView.cellForItem(at: indexPath) {
          cell.layer.borderColor = #colorLiteral(red: 0.1411764771, green: 0.3960784376, blue: 0.5647059083, alpha: 1)
          cell.layer.borderWidth = 0
        }
    }
    
    func grapPhotos() {
        
        let imgManager = PHImageManager.default()
        let requestOptions = PHImageRequestOptions()
        requestOptions.isSynchronous = true
        requestOptions.deliveryMode = .highQualityFormat
        
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        fetchOptions.predicate = NSPredicate(format: "mediaType = %d || mediaType = %d", PHAssetMediaType.image.rawValue, PHAssetMediaType.video.rawValue)
        
        if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: fetchOptions) {
            
            if fetchResult.count > 0 {
                
                for i in 0..<fetchResult.count {
                    imgManager.requestImage(for: fetchResult.object(at: i), targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: requestOptions, resultHandler: {
                        
                        image, error in
                        
                        self.imageArray.append(image!)
                        
                        
                    })
        
                    
                }
            }
            else {
                self.collectionView?.reloadData()
                print("No Photos")
            }
        }
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        
        let width = collectionView.frame.width / 3 - 6
        
        
        
        return CGSize(width: width, height: width)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        
        return 6.0
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        
        return 6.0
    }

}

I apologize it's a lot but I really have no idea where I am going wrong. This is all my code for my CollectionViewController, and then in storyboard I have a collection VC with an image in the one cell with a tag as 1 and then the one cell with an identifier as "Cell". The problem I am having is when I run the code on my phone, selecting one image ends up selecting others. For example: I select the first image, then I scroll down and another image is already selected. I did a little test and the first 21 images are almost unique. But then it's as if the image almost have the same id. Like selecting the first image also selects the 22nd image. By the way I am building a custom image picker.


Solution

  • There's more than one issue here, but the main problem you're complaining of is because you're forgetting that cells are reused. You need to configure cells in cellForItem, not in didSelect and didDeselect.

    When your didSelect turns around and talks directly to a physical cell, it's doing totally the wrong thing. Instead, didSelect and didDeselect should talk to your data model, setting some property in the model's objects that says, the objects at these index paths should be considered selected, and the objects at these other index paths should not. Then reload the data.

    Then, when cellForItem comes along, it must configure its cell completely, either displaying its selected appearance or its unselected appearance, based on the data model and index path. That way, if a cell was in a previous selected row but is now reused in an unselected row, it has the desired unselected appearance.

    In other words, only cellForItem should think in terms of cells. Everyone else should think solely in terms of index path (or even better, in terms of some unique identifier associated with the data, but that's another story).