How to scroll to a given indexPath.item?

Viewed 66

I have 2 collection views in parallel. One of them is a menu and the other is a big one containing vertical table views that are populated with API queried data. enter image description here

When the user scrolls right, then Order History is highlighted and the next cell in the big collection view at the bottom takes over the rest of the screen and it shows its own tableview data. It's just like the implementation here.

Everything works EXCEPT when I tap on Order History in the state indicated in the attached image, the collection view at the bottom does not scroll to the 2nd cell in the big collection view. If I tap on the Current Orders cell then it does scroll to the 1st cell in the big collection view. I have no idea what's causing this problem after spending the last 48 hours on this.

This is the menu bar:

class MenuBar: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
    lazy var menuCollection: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        let collectionview = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionview.translatesAutoresizingMaskIntoConstraints = false
        collectionview.backgroundColor = .white
        collectionview.delegate = self
        collectionview.dataSource = self
        collectionview.register(InvoiceCell.self, forCellWithReuseIdentifier: InvoiceCell.identifier)
        return collectionview
    }()
    
    var leftIndicatorConstraint: NSLayoutConstraint?
    private let menuOptions = ["Current Orders", "Order History"]
    var containerViewController: ContainerViewController? // I use an instance of the parent VC to pass on the value
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        addSubview(menuCollection)
        menuCollection.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
        menuCollection.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
        menuCollection.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
        menuCollection.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
        menuCollection.selectItem(at: IndexPath(row: 0, section: 0), animated: true, scrollPosition: .left)
        setupIndicator()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setupIndicator() {
        let indicatorBar = UIView()
        indicatorBar.backgroundColor = UIColor(hex: Constants.Colors.primary)
        addSubview(indicatorBar)
        indicatorBar.translatesAutoresizingMaskIntoConstraints = false
        leftIndicatorConstraint = indicatorBar.leadingAnchor.constraint(equalTo: self.leadingAnchor)
        leftIndicatorConstraint?.isActive = true
        indicatorBar.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
        indicatorBar.heightAnchor.constraint(equalToConstant: 5).isActive = true
        indicatorBar.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.5).isActive = true
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        containerViewController?.scrollToMenu(menuIndex: indexPath.item) // Here I pass on the value of the menu item that's tapped
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return menuOptions.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: InvoiceCell.identifier, for: indexPath) as? InvoiceCell else { return UICollectionViewCell() }
        cell.thisOption = menuOptions[indexPath.row]
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: menuCollection.frame.size.width / CGFloat(menuOptions.count), height: menuCollection.frame.size.height)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
}

And this is the parent UIView Controller that contains the menu collection view as well as the big base collection view

class ContainerViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
    lazy var menuBar: MenuBar = {
        let menuBar = MenuBar()
        menuBar.containerViewController = self
        menuBar.translatesAutoresizingMaskIntoConstraints = false
        return menuBar
    }()
    
    lazy var baseCollectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.register(PastCell.self, forCellWithReuseIdentifier: PastCell.pastCellId)
        collectionView.register(CurrentCell.self, forCellWithReuseIdentifier: CurrentCell.currentCellId)
        collectionView.isPagingEnabled = true
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.backgroundColor = .white
        return collectionView
    }()
    
    var orderViewModel = OrderViewModel(order: Order(addressOne: "", addressTwo: "", city: "", postalCode: "", mpName: "", planType: 0, restaurantAddress: "", restaurantId: "", restaurantName: "", timeOfCreation: nil, currentTotal: 0.00, orderMenu: [], status: 0, itemsHTML: ""))
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white
        setupView()
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        orderViewModel.removeListener()
    }
    
    private func setupView() {
        view.addSubview(menuBar)
        menuBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
        menuBar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
        menuBar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
        menuBar.heightAnchor.constraint(equalToConstant: 40).isActive = true
        
        view.addSubview(baseCollectionView)
        baseCollectionView.topAnchor.constraint(equalTo: menuBar.bottomAnchor, constant: 0).isActive = true
        baseCollectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
        baseCollectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
        baseCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
    }
    
    func scrollToMenu(menuIndex: Int) {
        // Here I am using the passed on menu index to scroll to the appropriate cell in the big collection view.
        let indexPath = IndexPath(item: menuIndex, section: 0)
        baseCollectionView.scrollToItem(at: indexPath, at: .left, animated: true) // For some reason it ALWAYS SCROLL TO THE FIRST CELL    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        menuBar.leftIndicatorConstraint?.constant = scrollView.contentOffset.x / 2
    }
    
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        let index = Int(targetContentOffset.pointee.x / view.frame.width)
        menuBar.menuCollection.selectItem(at: IndexPath(item: index, section: 0), animated: true, scrollPosition: .left)
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 2
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        switch indexPath.row {
        case 0:
            if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CurrentCell.currentCellId, for: indexPath) as? CurrentCell {
                if let tabBarHeight = self.tabBarController?.tabBar.frame.size.height {
                    cell.adjustSize(top: menuBar.frame.height * 1.2, bottom: tabBarHeight * 0.5)
                    return cell
                }
            }
        case 1:
            if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PastCell.pastCellId, for: indexPath) as? PastCell {
                cell.backgroundColor = .systemOrange
                return cell
            }
        default:
            return UICollectionViewCell()
        }
        return UICollectionViewCell()
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: view.frame.size.width, height: view.frame.size.height)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
}
1 Answers
Related