Не использовать повторно используемую ячейку в UITableView с CollectionView в каждой ячейке
У меня есть UITableView
и в клетке-прототипе есть UICollectionView
,
MainViewController
является делегатом UITableView
а также MyTableViewCell
класс делегат для UICollectionView
,
При обновлении каждого TableViewCell
содержимое я называю cell.reloadData()
чтобы collectionView внутри ячейки перезагрузил ее содержимое.
Когда я использую многократно используемые ячейки, каждая ячейка появляется, так как содержимое последней ячейки исчезло! Затем он загружает правильное содержимое из URL.
У меня будет 5 до 10 UITableViewCells
в большинстве. Поэтому я решил не использовать многоразовые клетки для UITableView
, Я изменил строку создания ячейки в методе tableView следующим образом:
let cell = MyTableViewCell(style: .default, reuseIdentifier:nil)
Затем я получил ошибку в классе MyTableViewCell (который является делегатом для UICollectionView) в этой функции:
override func layoutSubviews() {
myCollectionView.dataSource = self
}
EXC_BAD_INSTRUCTION CODE(code=EXC_I386_INVOP, subcode=0x0)
fatal error: unexpectedly found nil while unwrapping an Optional value
MyTableViewCell.swift
import UIKit
import Kingfisher
import Alamofire
class MyTableViewCell: UITableViewCell, UICollectionViewDataSource {
struct const {
struct api_url {
static let category_index = "http://example.com/api/get_category_index/";
static let category_posts = "http://example.com/api/get_category_posts/?category_id=";
}
}
@IBOutlet weak var categoryCollectionView: UICollectionView!
var category : IKCategory?
var posts : [IKPost] = []
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
if category != nil {
self.updateData()
}
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override func layoutSubviews() {
categoryCollectionView.dataSource = self
}
func updateData() {
if let id = category?.id! {
let url = const.api_url.category_posts + "\(id)"
Alamofire.request(url).responseObject { (response: DataResponse<IKPostResponse>) in
if let postResponse = response.result.value {
if let posts = postResponse.posts {
self.posts = posts
self.categoryCollectionView.reloadData()
}
}
}
}
}
internal func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "postCell", for: indexPath as IndexPath) as! MyCollectionViewCell
let post = self.posts[indexPath.item]
cell.postThumb.kf.setImage(with: URL(string: post.thumbnail!))
cell.postTitle.text = post.title
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//You would get something like "model.count" here. It would depend on your data source
return self.posts.count
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
}
MainViewController.swift
import UIKit
import Alamofire
class MainViewController: UITableViewController {
struct const {
struct api_url {
static let category_index = "http://example.com/api/get_category_index/";
static let category_posts = "http://example.com/api/get_category_posts/?category_id=";
}
}
var categories : [IKCategory] = []
override func viewDidLoad() {
super.viewDidLoad()
self.updateData()
}
func updateData() {
Alamofire.request(const.api_url.category_index).responseObject { (response: DataResponse<IKCategoryResponse>) in
if let categoryResponse = response.result.value {
if let categories = categoryResponse.categories {
self.categories = categories
self.tableView.reloadData()
}
}
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return self.categories.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.categories[section].title
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let cell = tableView.dequeueReusableCell(withIdentifier: "CollectionHolderTableViewCell") as! MyTableViewCell
let cell = MyTableViewCell(style: .default, reuseIdentifier:nil)
cell.category = self.categories[indexPath.section]
cell.updateData()
return cell
}
}
MyCollectionViewCell.swift
import UIKit
class MyCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var postThumb: UIImageView!
@IBOutlet weak var postTitle: UILabel!
var category : IKCategory?
}
Почему повторное использование клеток не вызвало это? Почему я делаю не так?
1 ответ
Есть несколько вещей, которые должны сделать вас быстрее.
Во-первых, раскомментируйте строку, в которой используются повторно используемые ячейки, и удалите строку кода, которая создает одноразовые ячейки. Здесь безопасно использовать многоразовые клетки.
Во-вторых, в MyTableViewCell
, установить dataSource
для представления коллекции сразу после super.awakeFromNib()
вызов. Вам нужно только установить dataSource
один раз, но layoutSubviews()
потенциально будет вызываться несколько раз. Это не подходящее место для установки источника данных для ваших нужд.
override func awakeFromNib() {
super.awakeFromNib()
categoryCollectionView.dataSource = self
}
Я удалил звонок updateData()
от awakeFromNib()
, как вы уже называете это при создании ячейки. Вы также можете удалить layoutSubviews()
переопределить, но, как правило, вы должны быть осторожны, чтобы позвонить super.layoutSubviews()
при переопределении.
Наконец, причина, по которой записи, похоже, снова появляются в неправильных ячейках, заключается в том, что массив записей не очищался, так как ячейки использовались повторно. Чтобы устранить эту проблему, добавьте следующий метод в MyTableViewCell
:
func resetCollectionView {
guard !posts.isEmpty else { return }
posts = []
categoryCollectionView.reloadData()
}
Этот метод очищает массив и перезагружает представление вашей коллекции. Поскольку в массиве нет записей, представление коллекции будет пустым, пока вы не вызовете updateData снова. Последний шаг - вызвать эту функцию в ячейке prepareForReuse
метод. Добавьте следующее в MyTableViewCell:
override func prepareForReuse() {
super.prepareForReuse()
resetCollectionView()
}
Дайте мне знать, как это происходит!