不使用 UITableView 中的可重用单元格和每个单元格中的 CollectionView

Had*_*adu 2 uitableview reuseidentifier ios uicollectionview swift

我有一个UITableView并且在它的原型单元中有一个UICollectionView.

MainViewController是代表,UITableViewMyTableViewCell班级是代表UICollectionView

在更新每个TableViewCell内容时,我调用cell.reloadData()使单元格内的 collectionView 重新加载其内容。

当我使用可重用单元格时,当每个单元格出现时,最后一个单元格的内容消失了!。然后它从 URL 加载正确的内容。

UITableViewCells最多有 5 到 10 个。所以我决定不将可重复使用的单元用于UITableView. 我将 tableView 方法中的单元格创建行更改为:

let cell = MyTableViewCell(style: .default, reuseIdentifier:nil)
Run Code Online (Sandbox Code Playgroud)

然后我在 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
Run Code Online (Sandbox Code Playgroud)

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
    }

}
Run Code Online (Sandbox Code Playgroud)

主视图控制器.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
    }

}
Run Code Online (Sandbox Code Playgroud)

MyCollectionViewCell.swift

import UIKit

class MyCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var postThumb: UIImageView!
    @IBOutlet weak var postTitle: UILabel!

    var category : IKCategory?

}
Run Code Online (Sandbox Code Playgroud)

为什么不重用细胞会导致这种情况?为什么我做错了?

BJH*_*ios 6

有几件事情要做,可以让您快速上手。

首先,取消注释使用可重用单元格的行并删除创建不可重用单元格的代码行。在这里使用可重复使用的细胞是安全的。

其次,在 中MyTableViewCelldataSourcesuper.awakeFromNib()调用后立即为集合视图设置。您只需要设置dataSource一次,但layoutSubviews()可能会被多次调用。这不是根据您的需要设置数据源的正确位置。

override func awakeFromNib() {
    super.awakeFromNib()
    categoryCollectionView.dataSource = self
}
Run Code Online (Sandbox Code Playgroud)

我已经删除了对updateData()from的调用awakeFromNib(),因为您已经在创建单元格时调用了它。您也可以删除layoutSubviews()覆盖,但作为一般规则,您应该super.layoutSubviews()在覆盖时小心调用。

最后,帖子似乎重新出现在错误的单元格中的原因是帖子数组没有在单元格被重用时被清空。要解决此问题,请将以下方法添加到MyTableViewCell

func resetCollectionView {
    guard !posts.isEmpty else { return }
    posts = []
    categoryCollectionView.reloadData()
}
Run Code Online (Sandbox Code Playgroud)

此方法清空数组并重新加载您的集合视图。由于现在数组中没有帖子,因此在您再次调用 updateData 之前,集合视图将为空。最后一步是在单元格的prepareForReuse方法中调用该函数。将以下内容添加到 MyTableViewCell:

override func prepareForReuse() {
    super.prepareForReuse()
    resetCollectionView()
}
Run Code Online (Sandbox Code Playgroud)

让我知道事情的后续!