1

I'm programming an iOS application using multiple horizontal UICollectionViews stacked in a StackLayout inside a ScrollView. I want to display imaged in theses collection views but my custom UICollectionViewCell is not showing anything.

Here is what I'm doing

I've followed this tutorial for the multiple collection views, and this one for the custom cell.

Here is my ViewController :

import UIKit

struct CustomImage {
    var title: String
    var image: UIImage
}

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {

@IBOutlet weak var perspectiveCV: UICollectionView!
@IBOutlet weak var monkeyCV: UICollectionView!
@IBOutlet weak var beachCV: UICollectionView!

let perspectiveImages = [
    CustomImage(title: "Perspective 1", image: #imageLiteral(resourceName: "perspective-1")),
    CustomImage(title: "Perspective 2", image: #imageLiteral(resourceName: "perspective-2")),
    CustomImage(title: "Perspective 3", image: #imageLiteral(resourceName: "perspective-3")),
    CustomImage(title: "Perspective 4", image: #imageLiteral(resourceName: "perspective-4")),
    CustomImage(title: "Perspective 5", image: #imageLiteral(resourceName: "perspective-5")),
    CustomImage(title: "Perspective 6", image: #imageLiteral(resourceName: "perspective-6"))
]

let monkeyImages = [
    CustomImage(title: "Monkey 1", image: #imageLiteral(resourceName: "monkey-1")),
    CustomImage(title: "Monkey 1", image: #imageLiteral(resourceName: "monkey-2")),
    CustomImage(title: "Monkey 1", image: #imageLiteral(resourceName: "monkey-3"))
]

let beachImages = [
    CustomImage(title: "Beach 1", image: #imageLiteral(resourceName: "beach-1")),
    CustomImage(title: "Beach 2", image: #imageLiteral(resourceName: "beach-2")),
    CustomImage(title: "Beach 3", image: #imageLiteral(resourceName: "beach-3")),
    CustomImage(title: "Beach 4", image: #imageLiteral(resourceName: "beach-4"))
]

override func viewDidLoad() {
    super.viewDidLoad()
    title = "Images"

    setupCollectionViews()
}

fileprivate func setupCollectionViews() {
    // Heights
    perspectiveCV.heightAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8).isActive = true
    monkeyCV.heightAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8).isActive = true
    beachCV.heightAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8).isActive = true

    // Left and right padding
    perspectiveCV.contentInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
    monkeyCV.contentInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
    beachCV.contentInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)

    // Hide scrolling indicator
    perspectiveCV.showsHorizontalScrollIndicator = false
    monkeyCV.showsHorizontalScrollIndicator = false
    beachCV.showsHorizontalScrollIndicator = false
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    switch collectionView {
    case monkeyCV:
        return monkeyImages.count
    case beachCV:
        return beachImages.count
    default:
        return perspectiveImages.count
    }
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    switch collectionView {
    case monkeyCV:
        let monkeyCell = monkeyCV.dequeueReusableCell(withReuseIdentifier: "monkeyCell", for: indexPath) as! ImageCollectionViewCell
        monkeyCell.backgroundColor = UIColor.systemOrange
        monkeyCell.data = monkeyImages[indexPath.row]
        return monkeyCell
    case beachCV:
        let beachCell = beachCV.dequeueReusableCell(withReuseIdentifier: "beachCell", for: indexPath) as! ImageCollectionViewCell
        beachCell.backgroundColor = UIColor.systemBlue
        beachCell.data = beachImages[indexPath.row]
        return beachCell
    default:
        let perspectiveCell = perspectiveCV.dequeueReusableCell(withReuseIdentifier: "perspectiveCell", for: indexPath) as! ImageCollectionViewCell
        perspectiveCell.backgroundColor = UIColor.systemRed
        perspectiveCell.data = perspectiveImages[indexPath.row]
        return perspectiveCell
    }
}

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

}

Here is my custom UICollectionViewCell :

import UIKit

class ImageCollectionViewCell: UICollectionViewCell {

var data: CustomImage? {
    didSet {
        guard let data = data else { return }
        bg.image = data.image
    }
}

fileprivate let bg: UIImageView = {
    let iv = UIImageView()
    iv.translatesAutoresizingMaskIntoConstraints = false
    iv.contentMode = .scaleAspectFit
    iv.clipsToBounds = true
    return iv
}()

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

    bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
    bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
    bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
    bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true

    contentView.addSubview(bg)
}

required init?(coder: NSCoder) {
    super.init(coder: coder)
}
}

And here is my Storyboard

Can you explain to me what's wrong with my code ?

My setup :

  • Swift version : 5.1.3
  • Xcode version : 11.3.1
  • Target iOS version : 13.2
  • MacBook Pro (13-inch, 2016, Four Thunderbolt 3 Ports) MacOS Catalina 10.15.2 (19C57)
rmluux
  • 43
  • 5
  • did you apply your custom cell to your UICollectionViewCell ? In your storyboard? – Julian Silvestri Jan 28 '20 at 14:03
  • @JulianSilvestri yes in the storyboard, by clicking on the cell, in the right inspector I've set the custom class to ImageCollectionViewCell. – rmluux Jan 28 '20 at 14:10
  • In your storyboard you have three collectionViews. Each one with their own respective cell. Each one of those cells needs to be applied to your custom cell class. Have you done so? – Julian Silvestri Jan 28 '20 at 14:16
  • @JulianSilvestri I have set the custom cell class to my ImageCollectionViewCell, from the storyboard (in the right inspector by clicking on a cell), for each of the three cells. Is that what you was asking for ? – rmluux Jan 28 '20 at 14:21
  • @JulianSilvestri could it be linked with the "UICollectionViewDelegateFlowLayout" : first time I use that, first time I got problems. I can't understand how it can be possible to be stuck on something that looks that easy.. – rmluux Jan 28 '20 at 20:43
  • Is there any update on this ? Has my 2nd updated answer helped at all ? – Julian Silvestri Feb 03 '20 at 19:27

1 Answers1

1

It looks like you have not established the delegates/datasources assigned to each of your collection views.

In your view did load connect the delegate and datasource to each of your collection views.

self.mycollectionView1.delegate = self
self.mycollectionView1.datasource = self
self.mycollectionView2.delegate = self
self.mycollectionView2.datasource = self
self.mycollectionView3.delegate = self
self.mycollectionView3.datasource = self

You can also set the delegates and datasources through the storyboard. You can select each collectionView and ctrl + drag to your ViewController NOT THE VIEW! enter image description here

Note the highlighted portion in the image above. That indicates your viewController. Ctrl + drag each collection view to that point and click on delegate and datasource

EDIT-----

Instead of using a switch statement like you have done so here

switch collectionView {
    case monkeyCV:
        let monkeyCell = monkeyCV.dequeueReusableCell(withReuseIdentifier: "monkeyCell", for: indexPath) as! ImageCollectionViewCell
        monkeyCell.backgroundColor = UIColor.systemOrange
        monkeyCell.data = monkeyImages[indexPath.row]
        return monkeyCell
    case beachCV:
        let beachCell = beachCV.dequeueReusableCell(withReuseIdentifier: "beachCell", for: indexPath) as! ImageCollectionViewCell
        beachCell.backgroundColor = UIColor.systemBlue
        beachCell.data = beachImages[indexPath.row]
        return beachCell
    default:
        let perspectiveCell = perspectiveCV.dequeueReusableCell(withReuseIdentifier: "perspectiveCell", for: indexPath) as! ImageCollectionViewCell
        perspectiveCell.backgroundColor = UIColor.systemRed
        perspectiveCell.data = perspectiveImages[indexPath.row]
        return perspectiveCell
    }

Try establishing your cells like so

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)-> UICollectionViewCell {
      let cell = myCollectionView1.dequeueResuableCell(withReuseIdentifier: "myIdentifier", for: indexPath) as! mycustomCellClass
      cell.backgroundColor = UIColor.systemOrange
      return cell


      if collectionView == mycollectionView2 {
          let cell2 = mycollectionView2.dequeueResuableCell(withReuseIdentifier: "myIdentifier2", for: indexPath) as! mycustomCellClass
          cell2.backgroundColor = UIColor.systemBlue
      }

}

And so on. You may also need to modify your numberOfItemsInSection Function to replace that switch statement as well.

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    switch collectionView {
    case monkeyCV:
        return monkeyImages.count
    case beachCV:
        return beachImages.count
    default:
        return perspectiveImages.count
    }
}

From above change it to below

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
     if (collectionView == mycollectionView1 { 
        return monkeyImages.count
     } else if (collectionView == myCollectionView2 {
        return beachImages.count
     } else {
       //this is your final collectionview 
       return persepctiveImages.count
     }
}

EDIT 2-----------

Try removing the sizeForItemAt

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

Go to your storyboard and physically drag the size you want the cell to be. On the right panel inside the size inspector, make sure the 'Estimate Size' option is set to none.

Also you can try this as well.

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

Dividing by 3 gives three cells.

Julian Silvestri
  • 1,970
  • 1
  • 15
  • 33
  • I have already done that from the StoryBoard. I've checked again by dragging the CollectionViews to the viewController and delegate and datasource are checked. In the doubt, I also did what you told to me in the ViewDidLoad but nothing changed. I can maybe send here the project in an archive if it can be more helpfull. – rmluux Jan 28 '20 at 14:48
  • I'm reading your updated answer but I can't see any difference as the "if/else" and "switch/case" are working pretty much the same. I tried to use the "if/else" statements but the result is the same – rmluux Jan 28 '20 at 16:05