Странное поведение с UICollectionView при повторном использовании ячеек

=== РЕДАКТИРОВАТЬ ===

Хорошо! Наконец-то я думаю, что заставил это работать. Когда я читал эту статью ( http://matteomanferdini.com/the-correct-way-to-display-lists-in-ios-and-what-many-developers-do-wrong/), я хотел применять лучшие практики, поэтому я произвел рефакторинг и создал новый класс для своего источника данных. Делая это, я думаю, что я нашел, почему я получил эту странную проблему. Теперь для моих ячеек TableView (AgendaTableViewCell) я удалил events собственности, и сделал day собственность вместо Здесь мой новый класс

//
//  AgendaTableViewCell.swift
//  fnfa
//
//  Created by JBARA Omar on 15/02/2018.
//  Copyright © 2018 JBARA Omar. All rights reserved.
//

import UIKit

class AgendaTableViewCell: UITableViewCell {

    @IBOutlet weak var eventsCollectionView: UICollectionView!

    var eventsDataSource: EventsDataSource?
    var day: Date? {
        didSet{
            setup()
        }
    }
    let dateFormatter = DateFormatter()

    private func setup() {
        self.eventsDataSource = EventsDataSource(events: DataMapper.instance.events.findBy(date: day!)!)
        print("setup row")
        print(day)
        print(self.eventsDataSource?.events)
        dateFormatter.setLocalizedDateFormatFromTemplate("d")
        eventsCollectionView.dataSource = eventsDataSource
        eventsCollectionView.reloadData()
        eventsCollectionView.register(UINib(nibName: "EventCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: String(describing: EventCollectionViewCell.self))
    }

}

Я думаю, что я не получаю устаревшие значения в первый раз, потому что теперь, каждый раз, когда ячейка используется повторно, она устанавливает day собственности, следовательно, называя setup метод. Теперь я уверен, что он устанавливает данные с правильными событиями для этого конкретного дня. Я думаю, что то, что происходило раньше, это то, что оно использовало старое events собственность из другой повторно используемой ячейки. Я не знаю, является ли это хорошим способом для достижения этой цели (тот факт, что я устанавливаю источник данных каждый раз, когда ячейка используется повторно, кажется не жизнеспособным, но, поскольку я зависим от этого day собственность, я не мог бы сделать иначе), но на самом деле это работает!

=== НАЧАЛЬНЫЙ ВОПРОС ===

Я не знаю, это ошибка или я слишком тупой, но в последнее время это сводило меня с ума. Позвольте мне объяснить, что происходит.

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

В представлении я отображаю события в течение нескольких дней. Это сделано из TableView. В этом TableView я сделал xib-файлы с пользовательской ячейкой класса, и в своем коде я правильно их регистрирую (используя tableView.register(UINib(nibName: "AgendaTableViewCell", bundle: nil), forCellReuseIdentifier: "dayRow"))

Тогда вот сложная часть. В моем табличном представлении у меня есть заголовок для каждого дня, который я отображаю, таким образом определяя раздел для каждого, и в каждом разделе у меня есть ровно одна строка.

В этой строке я отображаю CollectionView, а также создал шаблон CollectionViewCell в xib-файле с пользовательским классом. Он правильно выбирает данные из моего JSON, но у меня есть одна очень неприятная проблема.

Вот первый рисунок, который поможет вам понять, что происходит, и код моего AgendaViewController (основного представления, имеющего TableView), AgendaTableViewCell, который является ячейкой в ​​моем TableView, и который также отвечает за управление моим CollectionView в ячейках. и EventCollectionViewCell, представляющие одну ячейку для представления коллекции (именно эта отображает информацию о событии).

показать GIF

Странная вещь здесь в том, что в первые два дня (Mercredi 4 и Jeudi 5) она работает, как и ожидалось, но для Vendredi 6 она становится действительно странной. Проблема не в данных, которые я получаю, потому что я проверил, мой events: [Event]? в AgendaTableViewCell, и у него есть правильные события.

Я думаю, что проблема на самом деле в снятии очереди / повторном использовании ячеек. Потому что, когда я иду ко второй камере, а затем возвращаюсь к первой, она работает как положено, и у меня больше нет проблем. Я действительно не знаю, что делать, я пробовал с prepareForReuse, но если я правильно понял, это уместно, когда речь идет о визуальном контенте, например, для сброса изображения, чтобы предотвратить повторное использование изображения из другой ячейки. Но здесь, это действительно текстовое содержание, которое является неправильным, и только это. Я действительно не понимаю, что здесь происходит. Кроме того, что действительно странно, так это то, что он работает нормально, если я тестирую всего 3 дня на моем days arrray. Но когда массив длиннее 3, тогда ошибка здесь. Это странная часть. И я заметил, что это выглядит странным образом. Я протестировал 15 дней, и в моих представлениях коллекций, похоже, что первая ячейка перед повторным использованием - с 4-го, 5-го и 8-го дня. И это продолжается и продолжается.

И вот мои файлы:

AgendaViewController

//
//  AgendaViewController.swift
//  fnfa
//
//  Created by JBARA Omar on 15/02/2018.
//  Copyright © 2018 JBARA Omar. All rights reserved.
//

import UIKit

class AgendaViewController: UITableViewController {


    private let reuseIdentifier = "dayRow"

    private var days = [
        Date(timeIntervalSince1970: 1522836000), // 4 Avril 2018, 12:00
        Date(timeIntervalSince1970: 1522922400), // 5 Avril 2018, 12:00
        Date(timeIntervalSince1970: 1523008800), // 6 Avril 2018, 12:00
        Date(timeIntervalSince1970: 1523095200), // 7 Avril 2018, 12:00
        Date(timeIntervalSince1970: 1523181600)  // 8 Avril 2018, 12:00
    ]
    private let dateFormatter = DateFormatter()
    private let locale = Locale(identifier: "fr_FR")

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.allowsSelection = false
        dateFormatter.locale = locale
        tableView.register(UINib(nibName: "AgendaTableViewCell", bundle: nil), forCellReuseIdentifier: reuseIdentifier)
        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let row = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! AgendaTableViewCell
        row.events = DataMapper.instance.events.findBy(date: days[indexPath.section])
        return row
    }


    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return days.count
    }

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
    }
    */

    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let view = super.tableView(tableView, viewForHeaderInSection: section)
        view?.backgroundColor = #colorLiteral(red:0.1175380871, green:0.1734368503, blue:0.310670346, alpha:1)

        return view
    }

    override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 50
    }

    override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
        if (view is UITableViewHeaderFooterView) {
            let header = view as! UITableViewHeaderFooterView
            dateFormatter.setLocalizedDateFormatFromTemplate("EEEE d")
            header.textLabel?.text = dateFormatter.string(from: days[section]).capitalized(with: locale)
            header.textLabel?.textAlignment = .center
            header.textLabel?.textColor = #colorLiteral(red:1.0, green:1.0, blue:1.0, alpha:1.0)
        }
    }

}

EventCollectionViewCell

//
//  XIBEventCollectionViewCell.swift
//  fnfa
//
//  Created by Jbara Omar on 18/02/2018.
//  Copyright © 2018 JBARA Omar. All rights reserved.
//

import UIKit

class EventCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var eventImage: UIImageView!
    @IBOutlet weak var labelTitle: UILabel!

    var event: Event? {
        didSet{
            eventImage.image = event?.getUIImage()
            labelTitle.text = event?.name
        }
    }


    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }



}

AgendaTableViewCell

//
//  AgendaTableViewCell.swift
//  fnfa
//
//  Created by JBARA Omar on 15/02/2018.
//  Copyright © 2018 JBARA Omar. All rights reserved.
//

import UIKit

class AgendaTableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {

    @IBOutlet weak var eventsCollectionView: UICollectionView!

    let dateFormatter = DateFormatter()
    var identifier = "events"
    var events: [Event]?

    override func awakeFromNib() {
        super.awakeFromNib()
        dateFormatter.setLocalizedDateFormatFromTemplate("d")
        eventsCollectionView.delegate = self as UICollectionViewDelegate
        eventsCollectionView.dataSource = self as UICollectionViewDataSource
        eventsCollectionView.register(UINib(nibName: "EventCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: identifier)
    }


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

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = eventsCollectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! EventCollectionViewCell
        cell.event = events?[indexPath.item]
        return cell
    }


    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

DataMapper - это просто класс, который я создал для получения моих данных от json, здесь неуместно размещать код, потому что он использует JSONDecoder только для внутреннего использования.

Я новичок в разработке для iOS, поэтому некоторая помощь будет очень полезной! Спасибо!

0 ответов

Другие вопросы по тегам