Странное поведение с 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, представляющие одну ячейку для представления коллекции (именно эта отображает информацию о событии).
Странная вещь здесь в том, что в первые два дня (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, поэтому некоторая помощь будет очень полезной! Спасибо!