Firebase images to UICollectionView - ios

I need some guidance in populating my collection view with images from Firebase. I've searched stack, google and youtube but couldn't find a fix for my problem. The captions (or "description/ requestDescription as I've named them) are displaying correctly, but the images related to each is not. There are 3 unique photos in the Firebase database but only two of them appear in the collection view (all 3 appear when I print the snapshotValue to the console though). Any guidance is greatly appreciated.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return requestDescriptionsArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cellID = "CollectionViewCell"
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! CollectionViewCell
//grab all images from database
ref = FIRDatabase.database().reference()
ref.child("Requests").queryOrdered(byChild: "replies").observe(.childAdded, with: { snapshot in
let snapshotValue = snapshot.value as? NSDictionary
print(snapshotValue!)
//get images
let photoURL = snapshotValue?["photo"] as! String
let url = URL(string: photoURL)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
cell.photo.image = UIImage(data: data!)
}
}).resume()
})
cell.requestDescription.text = requestDescriptionsArray[indexPath.item]
return cell
}
func initForImages() {
ref = FIRDatabase.database().reference()
ref.child("Requests").queryOrdered(byChild: "replies").observe(.childAdded, with: { snapshot in
let snapshotValue = snapshot.value as? NSDictionary
print(snapshotValue!)
//get descriptons and add to array
let description = snapshotValue?["Description"] as! String
self.requestDescriptionsArray.insert(description, at: 0)
self.collection.reloadData()
})
}

Related

Swift get json data and index out of range error

My question is simple but I couldn't find a solution. When I get JSON data from server I want to display the data to collectionviewcell but I got index out of range error.
This is my code:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SiparislerTumAnasayfa", for: indexPath) as! SiparislerTumAnasayfa
let url = URL(string: "https://abc/api/SiparislerTumListeler/abc")
let session = URLSession.shared
let task = session.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error)
}
else
{
if data != nil{
do{
let responseJSON = try? JSONSerialization.jsonObject(with: data!, options: [])
if let responseJSON = responseJSON as? [String: Any] {
self.jsonArray = responseJSON["results"] as? [[String: Any]]
DispatchQueue.main.async {
let row = self.jsonArray![indexPath.row]
if let urunAdi = row["siparis_urun_adi"] as? String {
cell.siparisUrunAdi.text = urunAdi
}
}
}
}
catch {
print(error)
}
}
}
}
task.resume()
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.jsonArray!.count
}
[indexPath.row] is where I got the error.
You need to add code that handles the case where indexPath.row is greater or equal to self.jsonArray.count.
Maybe the number of array from response api are less than the numberOfItemsInSection
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {....}

Cannot get array to display in the collection view (Swift 4)

I tested the collection view can displayed the content. However, I can't retrieve and add array result in the collection view.
Here is my code:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MainPageCollectionViewCell
cell.FoodTitle.text = arr[indexPath.row].title
cell.Food.image = arr[indexPath.row].image_url
return cell
}
About the array, it is a function inside the fetchFoodList.
func fetchFoodList(){
let url = URL(string: "MYAPI.json")
guard let unwrappedUrl = url else { return }
let task = URLSession.shared.dataTask(with: unwrappedUrl, completionHandler: {(data, response, error)in
if error != nil{
print(error!)
} else {
if let urlContent = data{
do {
let json = try JSON(data:data!)
let recipes = json["recipes"]
for arr in recipes.arrayValue{
print(arr["title"])
print(arr["image_url"])
}
}
catch{
print("JSON Processing Failed")
}
}
}
})
task.resume()
}
}
However, the array result title and image_url can displayed in the console.
After appending the array list need to reload collection like this
collectionView.reloadData()

How to correctly store the data that retrieved from sever?

I defined var postImgData = [UIImage]() at beginning of class to store all the images get from sever. The following is the code to get the image from sever
func loadPosts(){
let query = PFQuery(className: "posts")
query.whereKey("username", equalTo:PFUser.current()!.username!)
query.skip = self.picArray.count // skip the already loaded images
query.findObjectsInBackground { (objects, error) in
if error == nil {
if let objects = objects{
for object in objects{
self.collectionView?.performBatchUpdates({
let indexPath = IndexPath(row: self.uuidArray.count, section: 0)
self.uuidArray.append(object.value(forKey: "uuid") as! String)
self.picArray.append(object.value(forKey: "pic") as! PFFile)
self.collectionView?.insertItems(at: [indexPath])
}, completion: nil)
}
}
} else{
print(error!.localizedDescription)
}
}
}
Then i want to use self.postImgData[indexPath.row] = UIImage(data: data!)! to save each image data into postImgData array that defined earlier. But when i run the app it gives error said this line self.postImgData[indexPath.row] = UIImage(data: data!)! Thread 1: Fatal error: Index out of range. I am not sure why its happening? I tried use append function and its working but its didn't show the correct photo when i selected each cell. I am note sure what is correct way to do that? thanks
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return picArray.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//define cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! pictureCell
picArray[indexPath.row].getDataInBackground { (data, error) in
if error == nil {
cell.picImg.image = UIImage(data: data!)
self.postImgData[indexPath.row] = UIImage(data: data!)!
} else {
print(error!.localizedDescription)
}
}
return cell
}

How to load images as the user scrolls down in a collectionsView?

How to set it such that images are downloaded one by one and those that are downloaded are loaded first? Also, how to handle more image downloads as the user scrolls down while purging or clearing those on top?
Here's my code to download those images asynchronously from Firebase:
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("palettes").queryOrdered(byChild: "top").queryEqual(toValue: "#000000").observeSingleEvent(of: .value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict as [String:AnyObject]{
let URL = each.value["URL"] as! String
if let url = NSURL(string: URL) {
if let data = NSData(contentsOf: url as URL){
let image = UIImage(data: data as Data)
self.imageArray.append(image!)
self.collectionView?.reloadData()
}
}
}
}
})
And here's my code for populating collectionView:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let textLabel = cell.viewWithTag(2)
let ootdImage = cell.viewWithTag(4) as! UIImageView
ootdImage.image = imageArray[indexPath.row]
textLabel?.backgroundColor = averageColor
return cell
}
Edit: Right now, as my JSON tree only contains three entries, only three images are downloaded. But they are downloaded altogether and thus I figure it must be the reason why it takes a few seconds before all three images are downloaded and appear in the same instant.
func scrollViewDidScroll(scrollView: UIScrollView)
{
if scrollView.contentSize.height == scrollView.bounds.size.height + scrollView.bounds.origin.y {
//call web service here
}
}
Also add UIScrollViewDelegate in your class.
I found a solution to this:
Using alamofireImage,
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("palettes").queryOrdered(byChild: "top").queryEqual(toValue: "#000000").observeSingleEvent(of: .value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict as [String:AnyObject]{
let URL = each.value["URL"] as! String
self.URLArrayString.append(URL)
print(self.URLArrayString.count)
self.collectionView?.reloadData() //Reloads data after the number and all the URLs are fetched
}
}
})
after fetching all the URLs and total number of URLs, it will reload the cell immediately (images are not yet downloaded at this point, we let alamofireImage handle the image downloads one by one) which brings us to the next code:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let textLabel = cell.viewWithTag(2)
let ootdImage = cell.viewWithTag(4) as! UIImageView
// find out how to make it such that the data is gathered before being displayed.
//ootdImage.image = imageArray[indexPath.row]
let url = NSURL(string: URLArrayString[indexPath.row])
let placeholderImage = UIImage(named: "Rectangle")!
let filter = AspectScaledToFillSizeWithRoundedCornersFilter(
size: ootdImage.frame.size,
radius: 0
)
ootdImage.af_setImage(withURL: url as! URL, placeholderImage: placeholderImage, filter: filter, imageTransition: .crossDissolve(0.2)
)
textLabel?.backgroundColor = averageColor
return cell
}

Collection View Displaying Only One Item From An Array

I have a collection view in a view controller.. There is one problem which i can't figure out. The custom cell in a collection view is displaying one item from an array.
Cant figure out what is missing in the code.. I have used both the delegate and data source method..
Here is the code i am using..
viewDidLoad()
pathRef.observeSingleEventOfType(.ChildAdded, withBlock: { (snapshot) in
let post = CollectionStruct(key: snapshot.key, snapshot: snapshot.value as! Dictionary<String, AnyObject>)
self.userCollection.append(post)
let indexPath = NSIndexPath(forItem: self.userCollection.count-1, inSection: 0)
self.collectionView!.insertItemsAtIndexPaths([indexPath])
})
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return userCollection.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionCell", forIndexPath: indexPath) as! CollectionViewCell
let post = userCollection[indexPath.row]
if let imageUrl = post.category{
if imageUrl.hasPrefix("gs://"){
FIRStorage.storage().referenceForURL(imageUrl).dataWithMaxSize(INT64_MAX, completion: { (data, error) in
if let error = error {
print("Error Loading")
}
cell.userImg.image = UIImage.init(data: data!)
})
}else if let url = NSURL(string: imageUrl), data = NSData(contentsOfURL: url){
cell.userImg.image = UIImage.init(data: data)
}
}
return cell
}
I am trying to retrieve images stored in firebase database..
Use observeEventType instead of observeSingleEventOfType. More info in the docs.

Resources