Search code examples
iosswiftuicollectionviewuicollectionviewcelluicollectionviewlayout

Keep Collection View Cells Overlapped When They Are Redrawn


I've gotten my cells to overlap by setting the minimumLineSpacing property of the collection views layout to negative. But, when I scroll and the cells are redrawn, they now overlap in the opposite direction. I've put pictures below.

enter image description here enter image description here

How do I keep the cells overlapping as seen in the first picture when the collection view is scrolled and cells are redraw?

import UIKit

class PopularView: UIView {

let cellID = "cellID"

// MARK: - Views
let collectionView: UICollectionView = {
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .vertical
    layout.minimumLineSpacing = -55     // -------Allows Overlap-----
    layout.itemSize = CGSize(width: SCREEN_WIDTH, height: 185)
    layout.minimumInteritemSpacing = 17
    let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
    view.backgroundColor = .white
    return view
}()



// MARK: - Initializers
override init(frame: CGRect) {
    super.init(frame: frame)
    collectionView.dataSource = self
    collectionView.register(PopularCell.self, forCellWithReuseIdentifier: cellID)

    backgroundColor = .white
    setupCollectionView()
}

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



// MARK: - Setup
fileprivate func setupCollectionView() {
    self.addSubview(collectionView)
    collectionView.anchors(top: self.topAnchor, topPad: 0, bottom: self.bottomAnchor, bottomPad: 0, left: self.leftAnchor, leftPad: 0, right: self.rightAnchor, rightPad: 0, height: nil, width: nil)
    collectionView.contentSize = CGSize(width: 700, height: 700)
    }
}

extension PopularView: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 500
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! PopularCell
    cell.background.backgroundColor = .random
    return cell
    }
}

Solution

  • Try this, that might help you:

    1- Inside your Cells, you could define innerView inside your cell and set the frame to

    let innerView:UIView = CGRect(x: 0,y: -overlapHeight,width: screenWidth, height:cell.height + overlapHeight)
    cell?.contentView.addSubview(innerView)
    

    2- Configure your cell during initialisation with this:

    cell?.contentView.clipsToBounds = false
    

    3- When loading cell, set the z-order:

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
            cell.layer.zPosition = CGFloat(indexPath.row)
    // configure your cell after here
    }
    

    You should be able to see the nested views inside your content view to have overlapping.

    I have drafted a sample code, not looking perfectly, but will help you get started:

    private let reuseIdentifier = "Cell"
    private let overlapHeight:CGFloat = 100
    class CustomCollectionCell:UICollectionViewCell {
        var innerView:UIView?
        override init(frame: CGRect) {
            super.init(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 300))
            self.backgroundColor = .darkGray
            let innerView = UIView(frame: CGRect(x: 0,y: -overlapHeight,width: UIScreen.main.bounds.width,height: overlapHeight + self.contentView.frame.height))
            self.innerView = innerView
            innerView.layer.cornerRadius = 20
            self.contentView.addSubview(innerView)
            self.contentView.clipsToBounds = false
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func configure(color:UIColor?) {
            innerView?.backgroundColor = color
        }
    }
    
    import UIKit
    
    private let reuseIdentifier = "Cell"
    private let overlapHeight:CGFloat = 100
    class CustomCollectionCell:UICollectionViewCell {
        var innerView:UIView?
        override init(frame: CGRect) {
            super.init(frame: frame)
            self.backgroundColor = .darkGray
            let innerView = UIView(frame: CGRect(x: 0,y: -overlapHeight,width: UIScreen.main.bounds.width,height: overlapHeight + self.contentView.frame.height))
            self.innerView = innerView
            innerView.layer.cornerRadius = 20
            self.contentView.addSubview(innerView)
            self.contentView.clipsToBounds = false
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func configure(color:UIColor?) {
            innerView?.backgroundColor = color
        }
    }
    
    class CollectionViewController: UICollectionViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let flowLayout = UICollectionViewFlowLayout()
            flowLayout.itemSize = CGSize(width: UIScreen.main.bounds.width, height: 190)
            flowLayout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
            flowLayout.scrollDirection = .vertical
            flowLayout.minimumInteritemSpacing = 0.0
            collectionView.collectionViewLayout = flowLayout
    
    
            // Register cell classes
            self.collectionView!.register(CustomCollectionCell.self, forCellWithReuseIdentifier: reuseIdentifier)
    
    
        }
    
        // MARK: UICollectionViewDataSource
    
        override func numberOfSections(in collectionView: UICollectionView) -> Int {
            // #warning Incomplete implementation, return the number of sections
            return 1
        }
    
    
        override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            // #warning Incomplete implementation, return the number of items
            return 30
        }
    
        override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
            cell.layer.zPosition = CGFloat(indexPath.row)
    
            var color:UIColor?
            switch indexPath.row % 4 {
            case 0:
                color = .purple
            case 1:
                color = .yellow
            case 2:
                color = .green
            default:
                color = .red
            }
            if let cell = cell as? CustomCollectionCell {
                cell.configure(color: color)
            }
    
    
            return cell
        }
    
    
    
    }
    

    Result:

    enter image description here