Не использовать повторно используемую ячейку в 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()
}

Дайте мне знать, как это происходит!

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