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 {....}
Related
I'm quite new to Swift 4, came from Android Studio into Xcode.
I'm having an issue with Collection Views, the thing is if I fill a struct decodable with pre-determined data the cell it's clickable and passes to the didSelectItemat function. But when I'm getting the said data from a JSON from the server it doesn't.
My question is if there anything that it's passing over my head, or if I'm missing something.
Here's the code that I'm using:
struct SelectJogos: Decodable{
let success: Bool
let jogos: [Jogos]
}
struct Jogos: Decodable{
let NumJogo: Int
let EquipaA: String
let EquipaB: String
let Fase: String
let Serie: String
let CodComp: String
let Data: String
let Hora: String
let Local: String
}
class SubmeteController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
var jogo = [Jogos]()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
//User em sessão
let user = UserDefaults.standard.string(forKey: "username")
print(user!)
//MandaPost para o server
let url = URL(string: "http://yourexemple.com/something")
var request = URLRequest(url: url!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let postString = "clube=\(user!)&opera=select"
request.httpBody = postString.data(using: .windowsCP1252)
//Receve a info do sv
let task = URLSession.shared.dataTask(with: request){data, request, error in
guard let data = data, error == nil else{
print("error=\(error!)")
return
}
do{
let dataServer = try JSONDecoder().decode(SelectJogos.self, from: data)
if dataServer.success != false{
self.jogo = dataServer.jogos
print(dataServer.success)
}else{
print("nogames")
}
DispatchQueue.main.async {
print("Number of Data:\(self.jogo.count)")
self.collectionView.reloadData()
}
}catch let jsonERR{
print("Error DO: ", jsonERR)
}
}
task.resume()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("clicked")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return jogo.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCell", for: indexPath) as! SubmeteListaViewCell
cell.NumJogo.text = String(jogo[indexPath.row].NumJogo)
print(jogo[indexPath.row].CodComp)
print(indexPath)
return cell
}
}
You have to set the delegate in viewDidLoad
`
collectionView.delegate = self
I want to pass the data from my JSON Url to my collection view cell, so after parsing my json I have got array of URL links, the question is how to send it to cell imageView?
here is my code
import UIKit
class ItemsListViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource{
var myItems = [Item]()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return myItems.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
cell.itemPriseLabel.text = myItems[indexPath.row].currentPrice
let imageUrl = myItems[indexPath.row].productImageUrl
print(imageUrl)
cell.itemImage.image? = imageUrl[indexPath.row]
return cell
}
var category1: Categories?
#IBOutlet weak var colectionViewVariable: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
downloadJSON3 {
self.colectionViewVariable.reloadData()
}
colectionViewVariable?.dataSource = self
colectionViewVariable?.delegate = self
// Do any additional setup after loading the view.
}
}
func getDataFromUrl(url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url) { data, response, error in
completion(data, response, error)
}.resume()
}
func downloadImage(url: URL) {
print("Download Started")
getDataFromUrl(url: url) { data, response, error in
guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
DispatchQueue.main.async() {
self.imageView.image = UIImage(data: data)
}
}
}
Duplicate: Answer found HERE I've stacked it into one concise answer but you should definitely read the answer as it is in the link as it is more complete and better altogether.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
cell.itemPriseLabel.text = myItems[indexPath.row].currentPrice
if let imageUrl = myItems[indexPath.row].productImageUrl.first {
URLSession.shared.dataTask(with: imageUrl, completionHandler: { (data, response, error) in
guard let data = data, response != nil, error == nil else {return}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data)
if let cell = collectionView.cellForItem(at: indexPath) as? CollectionViewCell {
cell.itemImage.image = image
}
})
}).resume()
}
return cell
}
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()
})
}
I am reading products data from API and print down the ids to make sure data has been fetched successfully. Then, I put products titles into collection view label.
The strange thing here is that the list of ids are printed very fast. Then the app wait for few seconds till the collectionView is populated.
I couldn't understand why the delay is occurring as I have only 10 products, which should not take any time for loading and I already made a loop over them and printed ids successfully in no time!
The following code shows exactly what I have done yet. I hope someone can help me figure out where is the bottle-nick:
import UIKit
class TestViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
var jsonData : [[String: Any]] = [[:]]
override func viewDidLoad() {
super.viewDidLoad()
var request = URLRequest(url: URL(string: shopUrl + "/admin/products.json")!)
request.httpMethod = "GET"
URLSession.shared.dataTask(with:request, completionHandler: {(data, response, error) in
if error != nil {
print(error)
} else {
do {
guard let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] else { return }
guard let root = json?["products"] as? [[String: Any]] else { return }
self.jsonData = root
self.collectionView.reloadData()
print("This will be printed very fast!")
for product in root {
guard let id = product["id"] as? Int else { return }
print(id)
}
}
}
}).resume()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.jsonData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let data = self.jsonData[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TestCollectionViewCell", for: indexPath) as! TestCollectionViewCell
if let title = data["title"] as? String {
cell.titleLabel.text = title
}
return cell
}
}
Try call your reloadData in main thread:
DispatchQueue.main.async {
self.collectionView.reloadData()
}
The problem is that URLSession callback handler is still in background thread so it wont update your UI fast, so you have to switch back to main thread before update any UI after call network request with URLSession
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.