UICollectionView не обновляет все ячейки должным образом через cellForItemAt - PullRequest
1 голос
/ 02 марта 2020

У меня есть следующие настройки: UICollectionView с пользовательской ячейкой, где пользователь может выбрать несколько ячеек, а затем выполнить некоторую тяжелую операцию с изображением. Теперь я пытаюсь обновить выбранные ячейки, выбранные пользователем, в результате этой операции, которая будет создана функцией подклассовой пользовательской ячейки. Для всех видимых ячеек функция вызывается при нажатии кнопки пользователем, а для всех других ячеек это происходит через cellForItemAt, чтобы быть наиболее эффективным.

Однако я сталкиваюсь с проблемой сейчас, что все видимые ячейки обновляются, но после прокрутки ячеек сразу за или до видимых ячеек не обновляются через cellForItemAt, а только при прокрутке вперед и назад. Пожалуйста, смотрите прикрепленное видео.

Для демонстрации я просто показываю зеленый UIView для работы с изображением. Поскольку эта операция требует значительных ресурсов, Я не могу использовать какие-либо из UICollectionView reloadData или аналогичные, поскольку они отменят выбор выделенных ячеек, и повторный выбор вручную вызовет мерцание.

ViewController

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!

    // data source
    var items: [String] = ["Lynna", "Vickie", "Emmerich", "Chere", "Patrizius", "Micky", "Sarena", "Jewell", "Audrye", "Rockwell", "Lari", "Travis", "Ber", "Oneida", "Bethanne", "Geri", "Gray", "Wolfie", "Hallsy", "Vanna", "Aline", "Winslow", "Amelita", "Janek", "Samuel", "Jaclin", "Dick", "Jeanine", "Brandy", "Munroe", "Felice", "Melonie", "Esta", "Bria", "Kenon", "Roarke", "Gerry", "Carmel", "Conrado", "Jaquelin", "Avrit", "Noam", "Kleon", "Edith", "Carine", "Jackie", "Franni", "Riccardo", "Theda", "Virgina", "Carleen", "Gregorio", "Taylor", "Ellsworth", "Adolphus", "Annabella", "Natalya", "Celina", "Cathrine", "Evie", "Gasper", "Aguste", "Jory", "Farrell", "Natasha", "Deborah", "Pietro", "Lucila", "Yuri", "Micheil", "Claus", "Nelson", "Elisa", "Alfons", "Nicolea", "Sofia", "Geordie", "Anette", "Myrtice", "Tami", "Lucien", "Brigida", "Claire", "Brennan", "Kendal", "Jillana", "Adelaida", "Charles", "Mart", "Hastie", "Dewain", "Heida", "Karisa", "Arleyne", "Margi", "Brent", "Natalie", "Ashton", "Teodorico", "Prentice", "Rolando", "Tootsie", "Fonz", "Tremayne", "Bernardo", "Guthrie", "Hendrick", "Constantina", "Lin", "Reece", "Horace", "Celka", "Amata", "Dunc", "Franchot", "Niko", "Janna", "Jacklyn", "Eddy", "Ashely", "Angelica", "Pinchas", "Krishnah", "Edmon", "Darnell", "Alyson", "Pearle", "Ashla", "Chickie", "Ada", "Elane", "Bethanne-Two", "Lon", "Marlin", "Karissa", "Ellswerth", "Lisbeth", "Cortie", "Lucille", "Lissa", "Markos", "Alys", "Orrin", "Nancy", "Pepe", "Lauren", "Wyndham", "Belle", "Gordie", "Marcille", "Idaline", "Hillary", "Enrique", "Murielle", "Juliann", "Beatrisa", "Lavinia", "Noni", "Justino", "Hasty", "Sile", "Kiley", "Shepard", "Nickie", "Ly", "Dannel", "Tressa", "Merci", "Rog", "Frans", "Bernita", "Ginnie", "Niko-Two", "Sheilakathryn", "Whitman", "Kendell", "Florenza", "Marybeth", "Drusi", "Paulina", "Fina", "Moria", "Jacobo", "Rowan", "Mariejeanne", "Luelle", "Vicki", "Auria", "Trisha", "Ken", "Carline", "Dorry", "Forrester", "Wylie", "Elset", "Francyne", "Bondy", "Demott", "Erik", "Elenore", "Correy", "Isadora", "Mason", "Barris", "Marlee", "Haslett", "Lorette", "Arlyn", "Genna", "Trude", "Parnell", "Albina", "Spencer", "Lefty", "Luigi", "Winnie", "Torie", "Zita", "Bert", "Danyelle", "Aldis", "Rivy", "Niels", "Monte", "Sandy", "Georgeta", "Durante", "Cobbie", "Kathryn", "Dinnie", "Fanchon", "Say", "Ethelred", "Vick", "Betteanne", "Orazio", "Mannie", "Jinny", "Kerrin", "Abagail", "Reyna", "Tomlin", "Shelton", "Noble", "Massimiliano", "Madel", "Cayla", "Mathias", "Tod", "Liana", "Celia", "Antoni", "Ruthann", "Irvine", "Leisha", "Geri-Two", "Marlie", "Pascale", "Latia", "Minne", "Arlette", "Rhys", "Flint", "Sollie", "Hinda", "Igor", "Marcella", "Wilma", "Marijo", "Marika", "Pooh", "Roland", "Sergent", "Fawnia", "Valaree", "Evonne", "Angeline", "Welbie", "Mace", "Niles", "Grannie", "Berenice", "L;urette", "Nert", "Rheba", "Cristie", "Lazar", "Amil", "Yanaton", "Luella", "Herold", "Enrique-Two", "Joanna", "Dom", "Crista", "Ellyn", "Ema", "Romona", "Dona", "Madelina", "Nevsa", "Conway", "Laverna", "Orion", "Leighton", "Earle"]

    // stores the selected items
    var selectedItems = [String]()

    var applyImageOperation = false

    override func viewDidLoad() {
        super.viewDidLoad()

        // Set up collection view
        let layout = UICollectionViewFlowLayout()
        layout.minimumLineSpacing = 0
        layout.minimumInteritemSpacing = 0
        collectionView.collectionViewLayout = layout

        collectionView.allowsMultipleSelection = true
        collectionView.delegate = self
        collectionView.dataSource = self
        self.collectionView!.register(MyCustomCell.self, forCellWithReuseIdentifier: "CELL")
        self.collectionView!.contentInset = UIEdgeInsets(top: 6, left: 6, bottom: 6, right: 6)
    }

    @IBAction func btnSelectAllPressed(_ sender: Any) {
        // Add all items to the seleted array
        selectedItems = [String]() // empty the current selection first
        for item in items {
            selectedItems.append(item)
        }

        // Update the collection view visually by selecting all cells
        for i in 0..<collectionView.numberOfItems(inSection: 0) {
            collectionView.selectItem(at: IndexPath(row: i, section: 0), animated: false, scrollPosition: [])
        }
    }

    @IBAction func btnApplyImageOperationPressed(_ sender: Any) {
        // set image operation mode true if we have items selected
        if selectedItems.count > 0 {
            applyImageOperation = true
        }

        // All visible cells should call activateImageOperationMode() - all others will do in cellForItem
        for visibleCell in collectionView.visibleCells {
            if let cell = visibleCell as? MyCustomCell {
                if let name = cell.titleLabel.text {
                    if selectedItems.contains(name) {
                        cell.activateImageOperationMode()
                    }
                }
            }
        }
    }
}




extension ViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: ((collectionView.frame.width-12)/3), height: ((collectionView.frame.width-12)/3))
    }

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

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CELL", for: indexPath) as! MyCustomCell
        cell.titleLabel.text = items[indexPath.row]

        // if image operation is not active, we don't show / remove the view
        if !applyImageOperation {
            cell.deactivateImageOperationMode()
        }
        // if image operation is active, we show the view for all selected items
        else {
            // Check if cell is selected
            if selectedItems.contains(items[indexPath.row]) {
                cell.activateImageOperationMode()
            }
            // If cell is not selected
            else {
                cell.deactivateImageOperationMode()
            }
        }

        return cell
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        // Add item to selectedItems
        if !selectedItems.contains(items[indexPath.row]) {
            selectedItems.append(items[indexPath.row])
        }
    }

    func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
        // Remove item from selectedItems
        if selectedItems.contains(items[indexPath.row]) {
            selectedItems.removeAll{ $0 == items[indexPath.row] }
        }
    }
}

MyCustomCell

import UIKit

class MyCustomCell: UICollectionViewCell {

    let titleLabel: UILabel = {
        let l = UILabel()
        l.translatesAutoresizingMaskIntoConstraints = false
        l.text = "n/a"
        l.textColor = .red
        l.textAlignment = .center
        return l
    }()

    let bgColor: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .lightGray
        return v
    }()

    var myImageOperationView: UIView?



    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        self.commonInit()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.commonInit()
    }

    func commonInit(){
        // Configure Selection Background View
        selectedBackgroundView = {
            let view = UIView()
            view.backgroundColor = .red
            return view
        }()

        // Set std BG color
        contentView.addSubview(bgColor)
        bgColor.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 6).isActive = true
        bgColor.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -6).isActive = true
        bgColor.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 6).isActive = true
        bgColor.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -6).isActive = true

        // Setup label
        bgColor.addSubview(titleLabel)
        titleLabel.topAnchor.constraint(equalTo: bgColor.topAnchor, constant: 0).isActive = true
        titleLabel.bottomAnchor.constraint(equalTo: bgColor.bottomAnchor, constant: 0).isActive = true
        titleLabel.leftAnchor.constraint(equalTo: bgColor.leftAnchor, constant: 0).isActive = true
        titleLabel.rightAnchor.constraint(equalTo: bgColor.rightAnchor, constant: 0).isActive = true
    }

    func activateImageOperationMode() {
        if myImageOperationView == nil {
            // HERE WOULD BE SOME HEAVY IMAGE OPERATION
            // Adding this view is just a placeholder for demonstration
            myImageOperationView = UIView()
            myImageOperationView!.translatesAutoresizingMaskIntoConstraints = false
            myImageOperationView!.backgroundColor = .green
            bgColor.addSubview(myImageOperationView!)
            myImageOperationView!.topAnchor.constraint(equalTo: bgColor.topAnchor, constant: 0).isActive = true
            myImageOperationView!.bottomAnchor.constraint(equalTo: bgColor.bottomAnchor, constant: 0).isActive = true
            myImageOperationView!.leftAnchor.constraint(equalTo: bgColor.leftAnchor, constant: 0).isActive = true
            myImageOperationView!.rightAnchor.constraint(equalTo: bgColor.rightAnchor, constant: 0).isActive = true

            // bring label to top
            bgColor.bringSubviewToFront(titleLabel)
        }
    }

    func deactivateImageOperationMode() {
        if myImageOperationView != nil {
            myImageOperationView!.removeFromSuperview()
            myImageOperationView = nil
        }
    }
}

Видео, демонстрирующее поведение

enter image description here

1 Ответ

1 голос
/ 02 марта 2020

UICollectionView по умолчанию имеет значение prefetchingEnabled == YES, что означает, что представление коллекции будет запрашивать ячейки, прежде чем ему нужно будет их отобразить. Если состояние приложения изменяется так, что уже выбранные ячейки должны отображаться по-разному, вы можете реализовать метод collectionView:willDisplayCell:forItemAtIndexPath: для обновления ячейки.

Похоже, что это именно ваша проблема ... вы включаете функцию, которая должна изменить внешний вид ячеек, но ячейки, которые были извлечены, но не видны, не обновляются. Реализация collectionView:willDisplayCell:forItemAtIndexPath: должна решить проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...