Данные из метки в CollectionViewCell иногда обновляются при перезагрузке, иногда это не так - PullRequest
0 голосов
/ 31 декабря 2018

Прежде всего позвольте мне сказать, что это, кажется, распространенный вопрос о SO, и я прочитал все посты, которые я мог найти от Swift до Obj-C.За последние 9 часов я пробовал кучу разных вещей, но моя проблема все еще существует.

У меня есть vc (vc1) с collectionView в нем.Внутри collectionView у меня есть пользовательская ячейка с меткой и imageView внутри нее.Внутри cellForItem у меня есть свойство, которое также находится внутри пользовательской ячейки, и когда свойство устанавливается из datasource[indePath.item], внутри ячейки появляется наблюдатель свойства, который устанавливает данные для метки и imageView.

кнопка в vc1, которая нажимает на vc2, если пользователь выбирает что-то из vc2, он возвращается к vc1 через делегата.vc2 получает popped.

Правильные данные всегда передаются обратно (я проверял несколько раз в отладчике).

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

IЯ перепробовал все, начиная с prepareToReuse и заканчивая удалением метки, но по какой-то причине только данные метки ячейки путаются.Странно то, что иногда метка обновляется правильно, а иногда нет ? imageView ВСЕГДА показывает правильное изображение, и у меня никогда не возникает проблем, даже если данные метки неверны .2 объекта модели, которые находятся внутри источника данных, всегда находятся в правильной позиции индекса с правильной информацией.

В чем может быть проблема?

vc1: UIViewController, CollectionViewDataSource & Delegate {

    var datasource = [MyModel]() // has 1 item in it from viewDidLoad

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

        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: customCell, for: indexPath) as! CustomCell

        cell.priceLabel.text = ""
        cell.cleanUpElements()
        cell.myModel = dataSource[indexPath.item]

        return cell
    }

    // delegate method from vc2
    func appendNewDataFromVC2(myModel: MyModel) {

        // show spinner

        datasource.append(myModel) // now has 2 items in it

        // now that new data is added I have to make a dip to fb for some additional information
        firebaseRef.observeSingleEvent(of: .value, with: { (snapshot) in

            if let dict = snapshot.value as? [String: Any] else { }

            for myModel in self.datasource {
                myModel.someValue = dict["someValue"] as? String
            }

            // I added the gcd timer just to give the loop time to finish just to see if it made a difference
            DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {

               self.datasource.sort { return $0.postDate > $1.postDate } // Even though this sorts correctly I also tried commenting this out but no difference
               self.collectionView.reloadData()

               // I also tried to update the layout
               self.collectionView.layoutIfNeeded()

               // remove spinner
            }
        })
    }    
}

CustomCell Ниже.Это гораздо более упрощенная версия того, что находится внутри наблюдателя свойства myModel .Данные, которые отображаются в метке, зависят от других данных, и есть несколько условий, которые определяют это.Добавление всего этого внутри cellForItem создаст кучу кода, поэтому я не обновляю данные там (и не добавляю их здесь) и вместо этого решил сделать это внутри ячейки.Но, как я уже говорил ранее, когда я проверяю данные, это всегда на 100% правильно.Наблюдатель свойств всегда работает правильно.

CustomCell: UICollectionViewCell {

    let imageView: UIImageView = {
        let iv = UIImageView()
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()

    let priceLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    var someBoolProperty = false

    var myModel: MyModel? {
        didSet {

           someBoolProperty = true

           // I read an answer that said try to update the label on the main thread but no difference. I tried with and without the DispatchQueue
           DispatchQueue.main.async { [weak self] in
                self?.priceLabel.text = myModel.price!
                self?.priceLabel.layoutIfNeeded()  // tried with and without this
           }

           let url = URL(string: myModel.urlStr!)
           imageView.sd_setImage(with: url!, placeholderImage: UIImage(named: "placeholder"))

           // set imageView and priceLabel anchors
           addSubview(imageView)
           addSubview(priceLabel)

           self.layoutIfNeeded() // tried with and without this
        }
    }

    override func prepareForReuse() {
        super.prepareForReuse()

        // even though Apple recommends not to clean up ui elements in here, I still tried it to no success
        priceLabel.text = ""
        priceLabel.layoutIfNeeded() // tried with and without this
        self.layoutIfNeeded() // tried with and without this

        // I also tried removing the label with and without the 3 lines above 
        for view in self.subviews {
            if view.isKind(of: UILabel.self) {
                view.removeFromSuperview()
            }
        }
    }

    func cleanUpElements() {
        priceLabel.text = ""
        imageView.image = nil
    }
}

Я добавил 1 точку останова для всех, куда добавил priceLabel.text = "" (всего 3), и после перезагрузки collectionView точки останова всегда получают 6 раз (3 раза для 2).объекты в источнике данных). Первый раз в prepareForReuse, второй раз в cellForItem и третий раз в cleanUpElements()

1 Ответ

0 голосов
/ 31 декабря 2018

Оказывается, мне пришлось сбросить свойство внутри ячейки.Несмотря на то, что ячейки использовались повторно, а priceLabel.text очищался, свойство все еще сохраняло свое прежнее значение bool.Как только я сбросил его через cellForItem, проблема исчезла.

10 часов, смх

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

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: customCell, for: indexPath) as! CustomCell

    cell.someBoolProperty = false
    cell.priceLabel.text = ""
    cell.cleanUpElements()
    cell.myModel = dataSource[indexPath.item]

    return cell
}
...