dynamic collectionView in TableView

Viewed 213

I have a dynamic collectionView in TableView and there are three errors:

  1. the cells are not displayed correctly when the application starts

  2. when I do inserItem the cells flicker as if they are rebooting

  3. and most importantly when collectionView.insertItems the first four cells are hidden, the video

    thanks

MainViewController

class ViewController: UIViewController, UIScrollViewDelegate {


  override func viewDidLoad() {
        super.viewDidLoad()
        setting()
    }

  func setting(){

   getData()

   tableView.dataSource = self
   tableView.delegate = self

   tableView.estimatedRowHeight = UITableView.automaticDimension 
   tableView.rowHeight = UITableView.automaticDimension    

  }

         //load data
   func pagination(_ completion: (()->())?){

      SmartNetworkSevrice.getGoods(with: url) { [unowned self] (data) in
         guard data.modals.count > 0 else {
             self.tableView.tableFooterView = nil
             return
         }

         self.goods.append(contentsOf: data.modals)
         self.offSet += data.modals.count
         DispatchQueue.main.async {
             let indexPath = IndexPath(row: 0, section: 0)
             self.tableView.tableFooterView = nil
             if self.goods.count == data.modals.count || self.isRefresh {
                self.tableView.reloadRows(at: [indexPath], with: .none)
             } else {
                if let cell = self.tableView.cellForRow(at: indexPath) as? TVCellGoods {
                    UIView.performWithoutAnimation {
                        self.tableView.beginUpdates()
                        cell.insertGoods(data.modals)
                        cell.layoutIfNeeded()
                        cell.collectionViewHeight.constant = cell.collectionView.collectionViewLayout.collectionViewContentSize.height
                        self.tableView.endUpdates()
                     }

                }
             }
            completion?()
         }
     }

     // define bottom of tableView
    func scrollViewDidScroll(_ scrollView: UIScrollView) {

        guard scrollView == self.tableView else {  return }
        if (!isMoreDataLoading) {

            // Вычислить позицию длины экрана до нижней части результатов
            let scrollViewContentHeight = scrollView.contentSize.height
            let scrollOffsetThreshold = scrollViewContentHeight - scrollView.bounds.size.height
            if(scrollView.contentOffset.y > scrollOffsetThreshold && scrollView.isDragging) {
                isMoreDataLoading = true
                self.tableView.isScrollEnabled = false;
                self.tableView.isScrollEnabled = true;
                pagination(nil)
            }
        }

       }

    }

extension ViewController: UITableViewDataSource {


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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            guard let cell = tableView.dequeueReusableCell(withIdentifier: "goods", for: indexPath) as? TVCellGoods else { return UITableViewCell() }
              guard bestGoods.count != 0, goods.count != 0 else { return UITableViewCell() }
            cell.delegate = self
            cell.configure(bestGoods, goods, categories)
            // автообновление высоты
            self.tableView.beginUpdates()
            cell.collectionViewHeight.constant = cell.collectionView.collectionViewLayout.collectionViewContentSize.height
            self.tableView.endUpdates()
            return cell 
    }

}

extension ViewController: UITableViewDelegate, CallDelegate {

    func callMethod() {}

    func callMethod(push vc:UIViewController) {
        self.navigationController?.pushViewController(vc, animated: true)
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            return UITableView.automaticDimension
    }

    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
            return UITableView.automaticDimension
    }

}

TableViewCell with collectionView

class TVCellGoods: UITableViewCell {

    @IBOutlet weak var collectionView:UICollectionView!
    @IBOutlet weak var collectionViewHeight:NSLayoutConstraint!

    weak var delegate:CallDelegate?
    var bestGoods = [Goods]() // лучшие товары
    var goods = [Goods]() // все товары
    var categories = [Menu]()

    override func awakeFromNib() {
        super.awakeFromNib()

        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.tag = 2
        collectionView.isScrollEnabled = false

    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

    func configure(_ best:[Goods],_ goods:[Goods], _ category:[Menu]) {

        self.bestGoods = best
        self.goods = goods
        self.categories = category
        self.collectionView.reloadData()

    }

    func insertGoods(_ data:[Goods]) {

        self.goods.append(contentsOf: data)
        let count = self.bestGoods.count + self.categories.count  + self.goods.count
        let indexPaths = ((count - data.count) ..< count)
            .map { IndexPath(row: $0, section: 0) }
        self.collectionView.performBatchUpdates({
            self.collectionView.insertItems(at: indexPaths)
        }, completion: nil)

    }

}

and CollectionViewCell

class CVCellGoods: UICollectionViewCell {

    @IBOutlet weak var bgView: UIView!
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var title: UILabel!
    @IBOutlet weak var price: UILabel!
    @IBOutlet weak var delivery: UIImageView!
    @IBOutlet weak var premium: UIImageView!

    override func prepareForReuse() {
        super.prepareForReuse()
        delivery.image = nil
        premium.image = nil
        title.text = nil
        price.text = nil
        imageView.image = nil
        imageView.sd_cancelCurrentImageLoad()
    }

    override func awakeFromNib() {
        super.awakeFromNib()

        imageView.backgroundColor = UIColor(red: 215/255, green: 215/255, blue: 215/255, alpha: 1)

        self.contentView.layer.cornerRadius = 5
        self.contentView.layer.masksToBounds = true

        self.layer.shadowColor = UIColor.black.cgColor
        self.layer.shadowOffset = CGSize(width: 0, height: 0.5)
        self.layer.shadowRadius = 1
        self.layer.shadowOpacity = 0.3
        self.layer.masksToBounds = false
        self.layer.shouldRasterize = true
        self.layer.rasterizationScale = UIScreen.main.scale

    }

}

EDIT: this gives a small bug at startup

TVCellGoods.swift

override func layoutSubviews() {
    super.layoutSubviews()
    self.collectionView.collectionViewLayout.invalidateLayout()
}

enter image description here

3 Answers

I just figured out how to solve your problem. You gonna have to invalidate your collection view layout, as you are using an collectionView inside a tableView just add the following method on TVCellGoods.swift

override func layoutSubviews() {
    super.layoutSubviews()
    self.collectionView.collectionViewLayout.invalidateLayout()
}

I tested it, and worked!! see video

It's not a good idea to add a scroll view inside another scroll view! Do you have an example project in order to help you?

the cells are not displayed correctly when the application starts

You calculate collectionView itemSize in ViewDidLoad,

but in ViewDidLoad TableView & TableviewCell width not equal ViewController.

override func viewDidLoad() {
    super.viewDidLoad()
    print(view.frame.size.width) //320
    print(tableView.frame.size.width) //414
}

You can try reloadData in ViewDidAppear.

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    print(view.frame.size.width) //320
    print(tableView.frame.size.width) //320
    tableView.reloadData()
}

when collectionView.insertItems the first four cells are hidden

Subclass UICollectionViewFlowLayout

class MyCollectionViewFlowLayout: UICollectionViewFlowLayout  {
      override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
         return true
    }
}    

TVCellGoods.swift

override func awakeFromNib() {
    super.awakeFromNib()
    collectionView.delegate = self
    collectionView.dataSource = self
    collectionView.tag = 2
    collectionView.isScrollEnabled = false
    collectionView.collectionViewLayout = MyCollectionViewFlowLayout()
}
Related