没有从UIViewController正确调用UICollectionViewCell

问题描述

我正在尝试为UICollectionViewCell创建一个自定义叠加层,当用户选择图像时,它会在灰色叠加层上放置用户选择该图像的数字(即顺序)。运行代码时,我不会得到任何错误,但它似乎也无能为力。我添加了一些打印语句来帮助调试,当我运行代码时,我得到“ Count:0”打印15次。那就是我在库中拥有的图像数量。当我选择第一行中的第一张图像时,仍然会像预期的那样获得“计数:0”,但是当我选择下一张图像时,我得到的打印件如下所示。似乎计数不起作用,但我不确定为什么。我究竟做错了什么?我不知道为什么计数错误,但是我要解决的主要问题/原因是重叠式广告无法正常显示

打印声明

Cell selected: [0,0]
Count :0
Count :0
Count :0
Cell selected: [0,4]
Count :0

视图控制器

func collectionView(_ collectionView: UICollectionView,didSelectItemAt indexPath: IndexPath) {
        if let cell = collectionView.cellForItem(at: indexPath) as? TestCVCell {
            cell.setupView()
            print("Cell selected: \(indexPath)")
        }
 }
    
func collectionView(_ collectionView: UICollectionView,diddeselectItemAt indexPath: IndexPath) {
        if let cell = collectionView.cellForItem(at: indexPath) as? TestCVCell {
            cell.backgroundColor = nil
            cell.imageView.alpha = 1
        }
 }

自定义叠加层

lazy var circleView: UIView = {
     let view = UIView()
     view.backgroundColor = .black
     view.layer.cornerRadius = self.countSize.width / 2
     view.alpha = 0.4
     view.translatesAutoresizingMaskIntoConstraints = false
     return view
}()   
lazy var countLabel: UILabel = {
    let label = UILabel()
    let font = UIFont.preferredFont(forTextStyle: .headline)
    label.font = UIFont.systemFont(ofSize: font.pointSize,weight: UIFont.Weight.bold)
    label.textAlignment = .center
    label.textColor = .white
    label.adjustsFontSizetoFitWidth = true
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}() 
    private func setup(){addSubview(circleView)
        addSubview(circleView)
        addSubview(countLabel)
        NSLayoutConstraint.activate([
            circleView.leadingAnchor.constraint(equalTo: leadingAnchor),circleView.trailingAnchor.constraint(equalTo: trailingAnchor),circleView.topAnchor.constraint(equalTo: topAnchor),circleView.bottomAnchor.constraint(equalTo: bottomAnchor),countLabel.leadingAnchor.constraint(equalTo: leadingAnchor),countLabel.trailingAnchor.constraint(equalTo: trailingAnchor),countLabel.topAnchor.constraint(equalTo: topAnchor),countLabel.bottomAnchor.constraint(equalTo: bottomAnchor),])
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

TestCVCell:UICollectionViewCell

    override var isSelected: Bool {
        didSet { overlay.isHidden = !isSelected }
    }
    var imageView: UIImageView = {
        let view = UIImageView()
        view.clipsToBounds = true
        view.contentMode = .scaleAspectFill
        view.backgroundColor = UIColor.gray
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    var count: Int = 0 {
        didSet { overlay.countLabel.text = "\(count)" }
    }
    let overlay: CustomAssetCelloverlay = {
        let view = CustomAssetCelloverlay()
        view.isHidden = true
        return view
    }()
   func setupView() {
        addSubview(imageView)
        addSubview(overlay)
        print("Count :\(count)")
        NSLayoutConstraint.activate([
            overlay.topAnchor.constraint(equalTo: imageView.topAnchor),overlay.bottomAnchor.constraint(equalTo: imageView.bottomAnchor),overlay.leftAnchor.constraint(equalTo: imageView.leftAnchor),overlay.rightAnchor.constraint(equalTo: imageView.rightAnchor),])
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        imageView.frame = self.bounds
        setupView()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupView()
        fatalError("init(coder:) has not been implemented")
    }

解决方法

根据您的另一个问题,我猜您正在尝试执行类似的操作...

显示设备“照片”中的图像,并允许依次选择

enter image description here

,然后,当您取消选择一个单元格时(例如,取消选择我的第二个选择项),您想对其余选择项重新编号:

enter image description here

要完成此操作,您需要跟踪数组中的单元格选择-进行选择-以便保持编号。

解决这个问题的几种方法...这是一种。

首先,建议您将count属性重命名为index,并在设置值时显示或隐藏overlay

var index: Int = 0 {
    didSet {
        overlay.countLabel.text = "\(index)"
        // hide if count is Zero,show if not
        overlay.isHidden = index == 0
    }
}

当您从cellForItemAt中退出一个单元格时,请查看indexPath是否在我们的“跟踪”数组中,并适当设置该单元格的.index属性(该属性还将显示/隐藏叠加层)。

下一步,当您选择一个单元格时:

  • indexPath添加到我们的跟踪数组
  • 我们可以使用跟踪数组的数量直接设置.index属性,以更新单元格的外观,因为它不会影响其他任何单元格

取消选择单元格时,我们必须做其他工作:

  • 从我们的跟踪数组中删除indexPath
  • 重新加载单元格,以便重新编号

这是一个完整的示例-代码中包含很多注释。

CircleView

class CircleView: UIView {
    // simple view subclass that keeps itself "round"
    //  (assuming it has a 1:1 ratio)
    override func layoutSubviews() {
        layer.cornerRadius = bounds.width * 0.5
    }
}

CustomAssetCellOverlay

class CustomAssetCellOverlay: UIView {
    lazy var circleView: CircleView = {
        let view = CircleView()
        view.backgroundColor = UIColor(red: 0.0,green: 0.5,blue: 1.0,alpha: 1.0)
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    lazy var countLabel: UILabel = {
        let label = UILabel()
        let font = UIFont.preferredFont(forTextStyle: .headline)
        label.font = UIFont.systemFont(ofSize: font.pointSize,weight: UIFont.Weight.bold)
        label.textAlignment = .center
        label.textColor = .white
        label.adjustsFontSizeToFitWidth = true
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    private func setup(){addSubview(circleView)
        addSubview(circleView)
        addSubview(countLabel)
        NSLayoutConstraint.activate([
            
            // circle view at top-left
            circleView.leadingAnchor.constraint(equalTo: leadingAnchor,constant: 4.0),circleView.topAnchor.constraint(equalTo: topAnchor,// circle view Width: 28 Height: 1:1 ratio
            circleView.widthAnchor.constraint(equalToConstant: 28.0),circleView.heightAnchor.constraint(equalTo: circleView.widthAnchor),// count label constrained ot circle view
            countLabel.leadingAnchor.constraint(equalTo: circleView.leadingAnchor),countLabel.trailingAnchor.constraint(equalTo: circleView.trailingAnchor),countLabel.topAnchor.constraint(equalTo: circleView.topAnchor),countLabel.bottomAnchor.constraint(equalTo: circleView.bottomAnchor),])
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }
}

TestCVCell

class TestCVCell: UICollectionViewCell {
    
    var imageView = UIImageView()
    
    var index: Int = 0 {
        didSet {
            overlay.countLabel.text = "\(index)"
            // hide if count is Zero,show if not
            overlay.isHidden = index == 0
        }
    }
    
    let overlay: CustomAssetCellOverlay = {
        let view = CustomAssetCellOverlay()
        view.backgroundColor = UIColor.black.withAlphaComponent(0.4)
        view.isHidden = true
        return view
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        contentView.addSubview(imageView)
        contentView.addSubview(overlay)
        imageView.translatesAutoresizingMaskIntoConstraints = false
        overlay.translatesAutoresizingMaskIntoConstraints = false
        
        // constrain both image view and overlay to full contentView
        NSLayoutConstraint.activate([
            
            imageView.topAnchor.constraint(equalTo: contentView.topAnchor),imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),overlay.topAnchor.constraint(equalTo: imageView.topAnchor),overlay.bottomAnchor.constraint(equalTo: imageView.bottomAnchor),overlay.leadingAnchor.constraint(equalTo: imageView.leadingAnchor),overlay.trailingAnchor.constraint(equalTo: imageView.trailingAnchor),])
        
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

TrackSelectionsViewController

class TrackSelectionsViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout,UINavigationControllerDelegate {

    var myCollectionView: UICollectionView!
    
    // array to track selected cells in the order they are selected
    var selectedCells: [IndexPath] = []
    
    // to load assests when needed
    let imgManager = PHImageManager.default()
    let requestOptions = PHImageRequestOptions()

    // will be used to get photos data
    var fetchResult: PHFetchResult<PHAsset>!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // set main view background color to a nice medium blue
        view.backgroundColor = UIColor(red: 0.25,alpha: 1.0)
        
        // request Options to be used in cellForItemAt
        requestOptions.isSynchronous = false
        requestOptions.deliveryMode = .opportunistic

        // vertical stack view for the full screen (safe area)
        let mainStack = UIStackView()
        mainStack.axis = .vertical
        mainStack.spacing = 0
        mainStack.translatesAutoresizingMaskIntoConstraints = false
        
        // add it to the view
        view.addSubview(mainStack)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            mainStack.topAnchor.constraint(equalTo: g.topAnchor,constant:0.0),mainStack.leadingAnchor.constraint(equalTo: g.leadingAnchor),mainStack.trailingAnchor.constraint(equalTo: g.trailingAnchor),mainStack.bottomAnchor.constraint(equalTo: g.bottomAnchor),])
        
        // create a label
        let label = UILabel()
        
        // add the label to the main stack view
        mainStack.addArrangedSubview(label)

        // label properties
        label.textColor = .white
        label.textAlignment = .center
        label.text = "Select Photos"
        label.heightAnchor.constraint(equalToConstant: 48.0).isActive = true
        
        // setup the collection view
        setupCollection()
        
        // add it to the main stack view
        mainStack.addArrangedSubview(myCollectionView)
        
        // start the async call to get the assets
        grabPhotos()
    }
    
    func setupCollection() {
        let layout = UICollectionViewFlowLayout()
        myCollectionView = UICollectionView(frame: self.view.frame,collectionViewLayout: layout)
        myCollectionView.delegate = self
        myCollectionView.dataSource = self
        myCollectionView.backgroundColor = UIColor.white
        myCollectionView.allowsMultipleSelection = true
        myCollectionView.register(TestCVCell.self,forCellWithReuseIdentifier: "cvCell")
    }
    
    //MARK: CollectionView
    
    func collectionView(_ collectionView: UICollectionView,didSelectItemAt indexPath: IndexPath) {
        if let cell = collectionView.cellForItem(at: indexPath) as? TestCVCell {
            // add newly selected cell (index path) to our tracking array
            selectedCells.append(indexPath)

            // when selecting a cell,//  we can update the appearance of the newly selected cell
            //  directly,because it won't affect any other cells
            cell.index = selectedCells.count
        }
    }

    func collectionView(_ collectionView: UICollectionView,didDeselectItemAt indexPath: IndexPath) {

        // when de-selecting a cell,//  we can't update the appearance of the cell directly
        //  because if it's not the last cell selected,the other
        //  selected cells need to be re-numbered

        // get the index of the deselected cell from our tracking array
        guard let idx = selectedCells.firstIndex(of: indexPath) else { return }

        // remove from our tracking array
        selectedCells.remove(at: idx)

        // reloadData() clears the collection view's selected cells,so
        
        // get a copy of currently selected cells
        let curSelected: [IndexPath] = collectionView.indexPathsForSelectedItems ?? []
        
        // reload collection view
        //  we do this to update all cells' appearance,//  including re-numbering the currently selected cells
        collectionView.reloadData()

        // save current Y scroll offset
        let saveY = collectionView.contentOffset.y
        
        collectionView.performBatchUpdates({
            // re-select previously selected cells
            curSelected.forEach { pth in
                collectionView.selectItem(at: pth,animated: false,scrollPosition: .centeredVertically)
            }
        },completion: { _ in
            // reset Y offset
            collectionView.contentOffset.y = saveY
        })

    }

    func collectionView(_ collectionView: UICollectionView,numberOfItemsInSection section: Int) -> Int {
        guard fetchResult != nil else { return 0 }
        return fetchResult.count
    }
    func collectionView(_ collectionView: UICollectionView,cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cvCell",for: indexPath) as! TestCVCell
        
        imgManager.requestImage(for: fetchResult.object(at: indexPath.item) as PHAsset,targetSize: CGSize(width:120,height: 120),contentMode: .aspectFill,options: requestOptions,resultHandler: { (image,error) in
            cell.imageView.image = image
        })

        // get the index of this indexPath from our tracking array
        //  if it's not there (nil),set it to -1
        let idx = selectedCells.firstIndex(of: indexPath) ?? -1
        
        // set .count property to index + 1 (arrays are zero-based)
        cell.index = idx + 1
        
        return cell
    }
    func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = collectionView.frame.width
        return CGSize(width: width/4 - 1,height: width/4 - 1)
    }
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        myCollectionView.collectionViewLayout.invalidateLayout()
    }
    func collectionView(_ collectionView: UICollectionView,minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 1.0
    }
    func collectionView(_ collectionView: UICollectionView,minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 1.0
    }
    
    //MARK: grab photos
    func grabPhotos(){
        DispatchQueue.global(qos: .background).async {
            let fetchOptions = PHFetchOptions()
            fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate",ascending: false)]
            self.fetchResult = PHAsset.fetchAssets(with: .image,options: fetchOptions)
            if self.fetchResult.count == 0 {
                print("No photos found.")
            }
            DispatchQueue.main.async {
                self.myCollectionView.reloadData()
            }
        }
    }
    
}

注意:这仅是 示例代码! 不应将其视为“生产就绪”。

,

不是要在您的CollectionView代表处设置您的var count: Int = 0吗?


func collectionView(_ collectionView: UICollectionView,didSelectItemAt indexPath: IndexPath) {
        if let cell = collectionView.cellForItem(at: indexPath) as? TestCVCell {
            cell.setupView()
            cell.count = indexPath.item
            print("Cell selected: \(indexPath)")
        }
 }