skip to Main Content

I’m building my first app in Swift. I have RestaurantViewController. Half of the screen is filled by restaurant name, description, logo, atd.
And under this I have UICollectionView(), filled with restaurant Coupons. Everything works fine, but I wanna disable scrolling INSIDE the CollectionView, and allow to scroll in entire page – so when user scrolls in coupons, whole page is scrolling down and next coupons are showed.

How can I do it?

IMAGE – Screen

IMAGE – I wanna this after scroll

Coupons are loaded via API, so they are available few moments after setupUI() etc.

My code inside setupUI():

...
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
self.couponsCollectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
couponsCollectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: CouponCollectionCell.reuseIdentifier)
couponsCollectionView!.alwaysBounceVertical = true
couponsCollectionView!.isScrollEnabled = false
...

I am not using Storyboard, just all programmatically. Also with UIKit and SnapKit (external library)
My SnapKit code for create constraints in screen:

...
couponsCollectionView!.snp.makeConstraints { make in
   make.top.equalTo(couponsTitle.snp.bottom).offset(0)
   make.left.equalTo(0)
   make.width.equalToSuperview()
   make.leading.trailing.bottom.equalToSuperview()
}
...

Thank you!!!

2

Answers


  1. You can start with adding the content top of UICollectionView as a header and rest as cells of UICollectionView

    An example is as follow

    class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
        let headerHeight: CGFloat = 300.0
        let cellHeight: CGFloat = 50.0
        var data = []
    
        lazy var collectionView: UICollectionView = {
            let layout = UICollectionViewFlowLayout()
            let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
            collectionView.delegate = self
            collectionView.dataSource = self
            collectionView.showsVerticalScrollIndicator = false
            return collectionView
        }()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            view.addSubview(collectionView)
            collectionView.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                collectionView.topAnchor.constraint(equalTo: view.topAnchor),
                collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
            ])
    
            collectionView.register(DataCell.self, forCellWithReuseIdentifier: "DataCell")
    
            collectionView.register(HeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "HeaderView")
        }
    
        // MARK: - UICollectionViewDataSource
    
        func numberOfSections(in collectionView: UICollectionView) -> Int {
            return 2 
        }
    
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return section == 0 ? 1 : data.length
        }
    
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            if indexPath.section == 0 {
                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HeaderView", for: indexPath) as! HeaderView
    // do some stuff here
                return cell
            } else {
                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DataCell", for: indexPath) as! DataCell
    // Do some stuff here in your cell
                return cell
            }
        }
    
        // MARK: - UICollectionViewDelegateFlowLayout
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            return CGSize(width: collectionView.bounds.width, height: indexPath.section == 0 ? headerHeight : cellHeight)
        }
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
            return section == 0 ? CGSize(width: collectionView.bounds.width, height: headerHeight) : CGSize.zero
        }
    }
    
    Login or Signup to reply.
  2. I’d add the restaurant information (logo, name, …) inside a UICollectionViewHeader so everything scrolls smoothly, as suggested by @clawesome and @Muhammad

    Following your approach and if we try to achieve the desired effect without using a header to include the restaurant information, I created a very simple project to simulate the behaviour.

    Instead of constraining the collection view at the bottom of the restaurant information, you could pin it to the top, and update the contentInsets and add the necessary padding. Like this you achieve the same effect as it was starting just under the restaurant information.

    Then you could implement func scrollViewDidScroll(_ scrollView: UIScrollView) and update the constraints of the restaurant information according to the amount the user has scrolled.

     private func configure() {
        view.backgroundColor = .systemBackground
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: addLayout())
        collectionView.dataSource = self
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.delegate = self
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellReuseIdentifier)
        
        restaurantView = UIView()
        restaurantView.translatesAutoresizingMaskIntoConstraints = false
        restaurantView.backgroundColor = .systemGreen
        view.addSubview(collectionView)
        view.addSubview(restaurantView)
        
        restaurantViewAnchorConstraint = restaurantView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0)
        
        NSLayoutConstraint.activate([
            restaurantViewAnchorConstraint,
            restaurantView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            restaurantView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            restaurantView.heightAnchor.constraint(equalToConstant: 200),
            
            collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
            
        ])
        
        collectionView.contentInset.top = 200
        collectionView.verticalScrollIndicatorInsets.top = 200
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let offsetY = collectionView.contentOffset.y
        restaurantViewAnchorConstraint.constant = -(200 + offsetY)
    }
    

    200 is the height of the restaurant information view and the restaurantViewAnchorConstraint is a NSLayoutConstraint declared as a property as:

    var restaurantViewAnchorConstraint: NSLayoutConstraint!

    Remember to first add the collection view as a subview, not after the restaurant information, if not the collection view will hide all this information.

    Video proof

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search