UICollectionView doesn't scroll inside UITableViewCell

北城余情 提交于 2020-06-29 06:19:51

问题


I'm having an issue with UICollectionView scrolling inside UITableViewCell.

Unfortunately, the CollectionView doesn't scroll at all. When I'm trying to disable other UITableViewCells it works without any problem and vice versa.

CollectionViewCell:

import UIKit

class CategoriesCollectionViewCell: UICollectionViewCell {
    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .white
        layoutUI()
    }

    required init(coder adecoder: NSCoder) {
        fatalError("init(codeer:) has not been implemented")
    }

    lazy var categoriesImage: UIImageView = {
        let categoriesImage = UIImageView()
        categoriesImage.contentMode = .scaleToFill
        categoriesImage.layer.cornerRadius = 8.0
        categoriesImage.image = UIImage(named: "pizza")
        categoriesImage.layer.cornerRadius = 8.0
        categoriesImage.layer.masksToBounds = true
        categoriesImage.translatesAutoresizingMaskIntoConstraints = false
        return categoriesImage
    }()

    lazy var containerView: UIView = {
        let containerView = UIView()
        containerView.backgroundColor = .black
        containerView.alpha = 0.7
        containerView.translatesAutoresizingMaskIntoConstraints = false
        return containerView
    }()

    lazy var categoryName: UILabel = {
        let categoryName = UILabel()
        categoryName.textColor = .white
        categoryName.font = UIFont(name: "AvenirNext-DemiBold", size: 16)
        categoryName.text = "Soup"
        categoryName.textAlignment = .left
        categoryName.translatesAutoresizingMaskIntoConstraints = false
        return categoryName
    }()

    lazy var recipesNumber: UILabel = {
        let recipesNumber = UILabel()
        recipesNumber.textColor = .white
        recipesNumber.font = UIFont(name: "AvenirNext-Regular", size: 16)
        recipesNumber.text = "33"
        recipesNumber.textAlignment = .left
        recipesNumber.translatesAutoresizingMaskIntoConstraints = false
        return recipesNumber
    }()

    func setupcategoriesImageConstraints() {
        NSLayoutConstraint.activate([
            categoriesImage.topAnchor.constraint(equalTo: topAnchor),
            categoriesImage.bottomAnchor.constraint(equalTo: bottomAnchor),
            categoriesImage.leadingAnchor.constraint(equalTo: leadingAnchor),
            categoriesImage.trailingAnchor.constraint(equalTo: trailingAnchor),
        ])
    }

    func setupContainerViewConstraints() {
        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: categoriesImage.topAnchor),
            containerView.bottomAnchor.constraint(equalTo: categoriesImage.bottomAnchor),
            containerView.leadingAnchor.constraint(equalTo: categoriesImage.leadingAnchor),
            containerView.trailingAnchor.constraint(equalTo: categoriesImage.trailingAnchor)
        ])
    }

    func setupCategoryNameConstraints() {
        NSLayoutConstraint.activate([
            categoryName.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 16),
            categoryName.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
            categoryName.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 16)
        ])
    }

    func setuprecipesNumberConstraints() {
        NSLayoutConstraint.activate([
            recipesNumber.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -16),
            recipesNumber.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
            recipesNumber.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 16)
        ])
    }

    func addSubviews() {
        addSubview(categoriesImage)
        categoriesImage.addSubview(containerView)
        containerView.addSubview(categoryName)
        containerView.addSubview(recipesNumber)
    }

    func layoutUI() {
        addSubviews()
        setupcategoriesImageConstraints()
        setupContainerViewConstraints()
        setupCategoryNameConstraints()
        setuprecipesNumberConstraints()
    }
}

CollectionViewInTableViewCell:

import UIKit

class CategoriesTableViewCellCollectionViewCell: UITableViewCell, UICollectionViewDelegateFlowLayout {

    let categories = ["italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food"]

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        layoutUI()
        selectionStyle = .none
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    lazy var containerView: UIView = {
        let containerView = UIView()
        containerView.backgroundColor = .clear
        containerView.translatesAutoresizingMaskIntoConstraints = false
        return containerView
    }()

    lazy var categoriesNameLabel: UILabel = {
        let categoriesNameLabel = UILabel()
        categoriesNameLabel.text = "Categories"
        categoriesNameLabel.textColor = .customDarkGray()
        categoriesNameLabel.textAlignment = .left
        categoriesNameLabel.font = UIFont(name: "AvenirNext-Regular", size: 14)
        categoriesNameLabel.translatesAutoresizingMaskIntoConstraints = false
        return categoriesNameLabel
    }()

    lazy var seeAllCategoriesButton: UIButton = {
        let seeAllCategoriesButton = UIButton()
        seeAllCategoriesButton.setTitle("See all", for: .normal)
        seeAllCategoriesButton.setTitleColor(.CustomGreen(), for: .normal)
        seeAllCategoriesButton.titleLabel?.font = UIFont(name: "AvenirNext-Regular", size: 14)
        seeAllCategoriesButton.translatesAutoresizingMaskIntoConstraints = false
        seeAllCategoriesButton.addTarget(self, action: #selector(test), for: .touchUpInside)
        return seeAllCategoriesButton
    }()

    @objc func test() {
        print("Test worked")
    }

    lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.backgroundColor = .clear
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.register(CategoriesCollectionViewCell.self, forCellWithReuseIdentifier: "CategoriesCollectionViewCell")
        return collectionView
    }()

    func setupContainerViewConstraints() {
        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: topAnchor, constant: 16),
            containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            containerView.heightAnchor.constraint(equalTo: categoriesNameLabel.heightAnchor)
        ])
    }

    func setupCategoriesNameLabelConstraints() {
        NSLayoutConstraint.activate([
            categoriesNameLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
            categoriesNameLabel.centerYAnchor.constraint(equalTo: containerView.centerYAnchor)
        ])
    }

    func setupSeeAllCategoriesButtonConstraints() {
        NSLayoutConstraint.activate([
            seeAllCategoriesButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
            seeAllCategoriesButton.centerYAnchor.constraint(equalTo: containerView.centerYAnchor)
        ])
    }

    func setupCollectionViewConstraints() {
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: categoriesNameLabel.topAnchor, constant: 16),
            collectionView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 16),
            collectionView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            collectionView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
        ])
    }

    func addSubviews() {
        addSubview(categoriesNameLabel)
        addSubview(containerView)
        containerView.addSubview(seeAllCategoriesButton)
        containerView.addSubview(collectionView)
    }

    func layoutUI() {
        addSubviews()
        setupCollectionViewConstraints()
        setupContainerViewConstraints()
        setupCategoriesNameLabelConstraints()
        setupSeeAllCategoriesButtonConstraints()
    }

}

extension CategoriesTableViewCellCollectionViewCell: UICollectionViewDelegate, UICollectionViewDataSource {

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

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoriesCollectionViewCell", for: indexPath) as! CategoriesCollectionViewCell

        return cell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: self.frame.width / 2, height: self.frame.width / 4)
    }

}

TableViewCell:

import UIKit

class HomeTableViewCell: UITableViewCell {

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        layoutUI()
        selectionStyle = .none
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    lazy var containerView: UIView = {
        let containerView = UIView()
        containerView.backgroundColor = .white
        containerView.translatesAutoresizingMaskIntoConstraints = false
        containerView.layer.shadowColor = UIColor.black.cgColor
        containerView.layer.shadowOpacity = 1
        containerView.layer.shadowOffset = .init(width: 2, height: 2)
        containerView.layer.shadowRadius = 7.0
        containerView.layer.cornerRadius = 8.0
//        containerView.clipsToBounds = true
        return containerView
    }()

    lazy var foodImage: UIImageView = {
        let foodImage = UIImageView()
        foodImage.translatesAutoresizingMaskIntoConstraints = false
        foodImage.contentMode = .scaleAspectFill
        foodImage.clipsToBounds = true
        foodImage.layer.cornerRadius = 8.0
        return foodImage
    }()

    lazy var foodTitle: UILabel = {
        let foodTitle = UILabel()
        foodTitle.textColor = .CustomGreen()
        foodTitle.numberOfLines = 0
        foodTitle.translatesAutoresizingMaskIntoConstraints = false
        return foodTitle
    }()

    func setupContainerView() {
        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: topAnchor, constant: 16),
            containerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16),
            containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
        ])
    }

    func setupFoodImage() {
        NSLayoutConstraint.activate([
            foodImage.topAnchor.constraint(equalTo: containerView.topAnchor),
            foodImage.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
            foodImage.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
            foodImage.heightAnchor.constraint(equalToConstant: 250)
        ])
    }

    func setupFoodTitle() {
        NSLayoutConstraint.activate([
            foodTitle.topAnchor.constraint(equalTo: foodImage.bottomAnchor, constant: 16),
            foodTitle.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -16),
            foodTitle.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
            foodTitle.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16)
        ])
    }

    func addSubview() {
        addSubview(containerView)
        containerView.addSubview(foodImage)
        containerView.addSubview(foodTitle)
    }

    func layoutUI() {
        addSubview()
        setupContainerView()
        setupFoodImage()
        setupFoodTitle()
    }

}

HomeView:

class HomeView: UIView {

    var recipes: Recipes?
    var recipesDetails = [Recipe]()
    let indicator = ActivityIndicator()

    override init( frame: CGRect) {
        super.init(frame: frame)
        layoutUI()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    lazy var foodTableView: UITableView = {
        let foodTableView = UITableView()
        foodTableView.translatesAutoresizingMaskIntoConstraints = false
        foodTableView.backgroundColor = .white
        foodTableView.delegate = self
        foodTableView.dataSource = self
        foodTableView.register(CategoriesTableViewCellCollectionViewCell.self, forCellReuseIdentifier: "CategoriesTableViewCellCollectionViewCell")
        foodTableView.register(HomeTableViewCell.self, forCellReuseIdentifier: "HomeTableViewCell")
        foodTableView.rowHeight = UITableView.automaticDimension
//        foodTableView.estimatedRowHeight = 100
        foodTableView.showsVerticalScrollIndicator = false
        foodTableView.separatorStyle = .none
        return foodTableView
    }()

    func setupFoodTableView() {
        NSLayoutConstraint.activate([
            foodTableView.topAnchor.constraint(equalTo: topAnchor),
            foodTableView.bottomAnchor.constraint(equalTo: bottomAnchor),
            foodTableView.leadingAnchor.constraint(equalTo: leadingAnchor),
            foodTableView.trailingAnchor.constraint(equalTo: trailingAnchor)
        ])
    }

    func addSubview() {
        addSubview(foodTableView)
    }

    func layoutUI() {
        indicator.setupIndicatorView(self, containerColor: .customDarkGray(), indicatorColor: .white)
        addSubview()
        setupFoodTableView()
        fetchData()

    }

    func fetchData() {
        AF.request("https://api.url").responseJSON { (response) in
            if let error = response.error {
                print(error)
            }
            do {
                self.recipes = try JSONDecoder().decode(Recipes.self, from: response.data!)
                self.recipesDetails = self.recipes?.recipes ?? []
                DispatchQueue.main.async {
                    self.foodTableView.reloadData()
                }
            } catch {
                print(error)
            }
            self.indicator.hideIndicatorView()
        }
    }

}

extension HomeView: UITableViewDelegate, UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return recipesDetails.count

    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        if indexPath.row == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: "CategoriesTableViewCellCollectionViewCell", for: indexPath) as! CategoriesTableViewCellCollectionViewCell
            cell.layoutIfNeeded()
            cell.collectionView.reloadData()
            return cell
        }

        let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
        let url = URL(string: recipesDetails[indexPath.row].image ?? "Error")
        cell.foodImage.kf.setImage(with: url)
        cell.foodTitle.text = recipesDetails[indexPath.row].title
        cell.layoutIfNeeded()
        return cell

    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if indexPath.row == 0 {
            return 160

        } else {
            return 250
        }

    }

}

Can anyone tell me what is the problem or what I did wrong?

Thanks.


回答1:


You cannot scroll your collection view because it is outside the bounds of its superview. In your code, you have this line:

containerView.heightAnchor.constraint(equalTo: categoriesNameLabel.heightAnchor)

That makes your containerView height only around 20-pts, but you also have your collection view as a subview of containerView.

Here is how your code looks for me (pretty much as-is):

And, I cannot scroll the collection view.

You can confirm it is outside the containerView bounds by giving containerView a background color (I used cyan):

You can also confirm it by setting containerView.clipsToBounds = true:

Now, we don't even see the collection view.

You have a couple other constraint issues, but they don't really relate to being unable to scroll the collection view.

Your layout is also a little confusing, in that you have your Categories label outside your containerView... it seems it would make much more sense to have that label + the "See All" button + the collection view all inside the containerView.

I made a few edits to your CategoriesTableViewCellCollectionViewCell which resolves that issue -- and I think is close to your ultimate goal. You may need to do a little tweaking, but hopefully it will give you a good direction.

class CategoriesTableViewCellCollectionViewCell: UITableViewCell, UICollectionViewDelegateFlowLayout {

    let categories = ["italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food"]

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        layoutUI()
        selectionStyle = .none
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    lazy var containerView: UIView = {
        let containerView = UIView()
        containerView.backgroundColor = .clear
        containerView.translatesAutoresizingMaskIntoConstraints = false
        return containerView
    }()

    lazy var categoriesNameLabel: UILabel = {
        let categoriesNameLabel = UILabel()
        categoriesNameLabel.text = "Categories"
        categoriesNameLabel.textColor = .gray // .customDarkGray()
        categoriesNameLabel.textAlignment = .left
        categoriesNameLabel.font = UIFont(name: "AvenirNext-Regular", size: 14)
        categoriesNameLabel.translatesAutoresizingMaskIntoConstraints = false
        return categoriesNameLabel
    }()

    lazy var seeAllCategoriesButton: UIButton = {
        let seeAllCategoriesButton = UIButton()
        seeAllCategoriesButton.setTitle("See all", for: .normal)
//      seeAllCategoriesButton.setTitleColor(.CustomGreen(), for: .normal)
        seeAllCategoriesButton.setTitleColor(UIColor(red: 0.0, green: 0.5, blue: 0.0, alpha: 1.0), for: .normal)
        seeAllCategoriesButton.titleLabel?.font = UIFont(name: "AvenirNext-Regular", size: 14)
        seeAllCategoriesButton.translatesAutoresizingMaskIntoConstraints = false
        seeAllCategoriesButton.addTarget(self, action: #selector(test), for: .touchUpInside)
        return seeAllCategoriesButton
    }()

    @objc func test() {
        print("Test worked")
    }

    lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.backgroundColor = .clear
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.register(CategoriesCollectionViewCell.self, forCellWithReuseIdentifier: "CategoriesCollectionViewCell")
        return collectionView
    }()

    func setupContainerViewConstraints() {
        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: topAnchor, constant: 16),
            containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            // should not be constrained to categoriesNameLabel height
            //containerView.heightAnchor.constraint(equalTo: categoriesNameLabel.heightAnchor)
            // constrain 16-pts from bottom of cell
            containerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16)
        ])
    }

    func setupCategoriesNameLabelConstraints() {
        NSLayoutConstraint.activate([
            categoriesNameLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
            // centerY to seeAllCategoriesButton
            categoriesNameLabel.centerYAnchor.constraint(equalTo: seeAllCategoriesButton.centerYAnchor)
        ])
    }

    func setupSeeAllCategoriesButtonConstraints() {
        NSLayoutConstraint.activate([
            seeAllCategoriesButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
            // constrain top to containerView top
            seeAllCategoriesButton.topAnchor.constraint(equalTo: containerView.topAnchor)
        ])
    }

    func setupCollectionViewConstraints() {
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: seeAllCategoriesButton.bottomAnchor, constant: 0),
            collectionView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16),
            collectionView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            collectionView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
        ])
    }

    func addSubviews() {
        // but categoriesNameLabel inside containerView
        //addSubview(categoriesNameLabel)
        addSubview(containerView)
        containerView.addSubview(categoriesNameLabel)
        containerView.addSubview(seeAllCategoriesButton)
        containerView.addSubview(collectionView)
    }

    func layoutUI() {
        addSubviews()
        setupCollectionViewConstraints()
        setupContainerViewConstraints()
        setupCategoriesNameLabelConstraints()
        setupSeeAllCategoriesButtonConstraints()
    }

}

The result looks like this - and you can see that I have scrolled the collection view a bit:



来源:https://stackoverflow.com/questions/60505427/uicollectionview-doesnt-scroll-inside-uitableviewcell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!