Mohamed Elloumi Mohamed Elloumi - 1 month ago 8
Swift Question

Empty Collection View Swift

I followed 1 tutorial and i was able to fill a collectionView with some data(imageview and text):

let appleProducts = ["A", "B", "C", "D"]
let imageArray = [UIImage(named: "pug1"), UIImage(named: "pug2"), UIImage(named: "pug3"), UIImage(named: "pug4")]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{

return appleProducts.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath as IndexPath) as! CollectionViewCell

cell.imageView?.image = self.imageArray[indexPath.row]

cell.title?.text = self.appleProducts[indexPath.row]
return cell

}


Now passing from the demo project to mine, I want to fill this CollectionView with data(Picture and text) that I get from FirebaseDatabse so I created this method:

struct item {
let pictureId: String!
let picture: String!
}
var items = [item]()
func getLatestAddedItems(){
self.items.removeAll()
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("Items").observe(.value, with: {
snapshot in

//self.items.insert(item(picture: picture), at: 0)
for childSnap in snapshot.children.allObjects {

let snap = childSnap as! FIRDataSnapshot
//print(snap.key)

let picture = (snap.value as? NSDictionary)?["bookImage"] as? String ?? ""
//print(picture)
self.items.append(item(pictureId:snap.key, picture:picture))

}
print(self.items.count)


})
}


And I create this button to call GetLatestAddedItems Method:

@IBAction func selectAction(_ sender: AnyObject) {
getLatestAddedItems()
}


And this one to check results:

@IBAction func gettableAction(_ sender: AnyObject) {

print(self.items[0].picture)
print(self.items[1].picture)
print(self.items[2].picture)
print(self.items.count) }


OutPut results:

picture 1 link
picture 2 link
picture 3 link
3


Everythings look fine and correct, now after making required changes in ContentView methods:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{

return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath as IndexPath) as! CollectionViewCell

//cell.imageView?.image = self.imageArray[indexPath.row]
let url = NSURL(string: items[indexPath.row].picture)
let data = NSData(contentsOf: url! as URL)
cell.imageView?.image = UIImage(data: data! as Data)

cell.title?.text = self.items[indexPath.row].pictureId
return cell

}


Now I'm getting an empty ContentView, the first time with button it works because I call the getLatestAddedItems() that will get and add data to the Items table, I try to call it in both ViewWillAppear or Viewdidload but nothings changes.

This is what I think the return items.count is returning 0 so nothings will appear any suggestions ?

Answer

Move your collectionView's protocol delegation initialisation to one of the ViewController lifecycle scope such as viewDidLoad() or viewWillAppear(_animated : Bool) if you are using a custom viewController(i.e embed a collectionView inside a viewController)

And reload your collectionView every time your user receives a value from its database.

override func viewDidLoad(){
  super.viewDidLoad()

  self.collectionView.dataSource = self 
  self.collectionView.delegate = self 

 }

func getLatestAddedItems(){
 self.items.removeAll()
 let databaseRef = FIRDatabase.database().reference()
 databaseRef.child("Items").observe(.childAdded, with: {
    snapshot in

    for childSnap in snapshot.children.allObjects {

        let snap = childSnap as! FIRDataSnapshot
        let picture = (snap.value as? NSDictionary)?["bookImage"] as? String ?? ""
         self.items.append(item(pictureId:snap.key, picture:picture))
         print(self.items.count)
         self.collectionView.reloadData() 
     }
   })
}

PS:- All the calls to your firebase server are asynchronous, which takes some time to retrieve the data from your FBDB, so put print(self.items.count) should be inside the completionBlock of the firebase observing call otherwise if it is put outside it will be called even before your data has been retrieved from FBDB.

Comments