【问题标题】:UICollectionViewCell is not being correctly called from the UIViewController没有从 UIViewController 正确调用 UICollectionViewCell
【发布时间】:2021-01-29 07:57:23
【问题描述】:

我正在尝试为 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")
    }

【问题讨论】:

  • 在您的单元格类中,您有 var count: Int = 0 ... 但您没有显示任何代码设置
  • 你是对的。我能够通过更新集合视图委托来使计数工作,但我的主要问题是为什么覆盖不起作用。我认为这在我的 OP 中不是很清楚,所以我编辑了最后一句话来澄清这一点。

标签: ios swift uicollectionview autolayout uicollectionviewcell


【解决方案1】:

根据你的另一个问题,我猜你正在尝试做这样的事情......

显示设备照片中的图像,并允许多个选择按顺序

并且,当您取消选择一个单元格时 - 例如,取消选择我的第二个选择 - 您想要重新编号剩余的选择:

要做到这一点,您需要跟踪数组中的单元格选择 - 因为它们是被制作的 - 这样您就可以保持编号。

有几种方法可以解决这个问题……这里有一个。

首先,我建议将您的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
  • 重新加载单元格以便重新编号

这是一个完整的示例 - 代码中有很多 cmets。

圆形视图

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, constant: 4.0),
            // 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, green: 0.5, blue: 1.0, 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, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 1.0
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, 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()
            }
        }
    }
    
}

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

【讨论】:

  • DonMag 像往常一样,您的帮助,甚至更多您的解释绝对是无价的!我是 Swift 编码的新手,但正是这样的解释帮助我更好地掌握了这个主题……甚至比 Apple 文档还要多。所以非常感谢!
【解决方案2】:

您的 var count: Int = 0 不应该设置为您的 CollectionView 委托吗?


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)")
        }
 }

【讨论】:

  • 你说的基本正确。我需要将计数添加到集合视图委托,但使用 indexPath.item 会给我项目而不是实际计数。我刚刚添加了 cell.count = count 和 count += 1。这似乎给了我计数,但我的主要问题是覆盖不起作用。对此的任何想法将不胜感激。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-03
相关资源
最近更新 更多