I have searched the web and all the similar questions that have claimed to be solved have been either for Objective-C or they are not working right now. Does someone have a real-life example with real data, for example from a server?
I am creating a Parse app and I'm stuck at self.collectionView.reloadData()
. I can see my data source is being populated with data from the server.
after running findObjectsInBackground(...)
, I can see that the number of objects is correct in numberOfSections(collectionView)
but collectionView(_:cellForItemAt:)
always shows that the indices for the objects returned from the Parse server are never available.
Objects I create on my own (like samples) are working fine. The objects are fetched nicely from the server and put in my local datastore and I can see them after the database fetch operation is complete.
I have already tried DispatchQueue
, OperationQueue.main.addOperation
, viewWillAppear
, viewDidLayoutSubviews
, self.collectionViewLayout.invalidateLayout()
, viewDidAppear
, self.collectionView?.reloadItems(at:(self.collectionView?.indexPathsForVisibleItems)!)
and others.
I have also used the Kyle Andrews' tutorial on Custom CollectionView Layouts: Building a Multi-Directional UICollectionView in Swift.
This is the entire code for the collectionVieController class.
import UIKit
import Parse
private let reuseIdentifier = "Cell"
/// This class will act as both the data source and delegate since UICollectionViewController conforms to both of our desired protocols: UICollectionViewDataSource and UICollectionViewDelegate. The data source will act as a vendor providing views and information, while the delegate handles managing selections and performing actions within the collection.
class PaymentStatusCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
//var users = Set<String>()
var users:[String] = ["Name", "Andrew", "Peter", "Paul","Mark"]
var payments:[String] = ["Payment", "100", "500", "200", "50"]
fileprivate let concurrentPhotoQueue = DispatchQueue( label: "com.raywenderlich.GooglyPuff.photoQueue", attributes: .concurrent)
let queue = DispatchQueue(label: "com.appcoda.myqueue")
let queue2 = DispatchQueue.global()
let button = UIButton()
var usersPayments:[String:[String]] = ["Name":["Amount"]]
//this object expects one entry per user.
var usersTotalPayments:[String:String] = ["Name":"Total"]
//var usersTotalPayments = [String:[String]]()
//var usersTotalPayments = Dictionary<String, Array<String>>()
@IBOutlet var paymentStatusCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
// Do any additional setup after loading the view.
usersTotalPayments["BB"] = "10000"
usersTotalPayments["AA"] = "1000"
//self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
//self.collectionView?.register(PaymentStatusCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
_ = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(PaymentStatusCollectionViewController.reloadData), userInfo: nil, repeats: false)
//button.setTitle("Shoudl This Force..", for: [])
//self.view.addSubview(button)
}
func reloadData() {
DispatchQueue.main.async { [unowned self] in
print(#function, "========================================")
self.collectionView?.reloadData()
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
//print(#function, " :", usersTotalPayments.keys.count)
//self.collectionView?.reloadData()
//self.collectionView!.reloadItems(at: <#[IndexPath]#>)
self.collectionView?.reloadData()
//self.collectionView?.reloadInputViews()
//self.collectionViewLayout.invalidateLayout()
print(#function, " :", self.usersTotalPayments.keys.count)
self.collectionView?.reloadItems(at:(self.collectionView?.indexPathsForVisibleItems)!)
}
// override func viewDidLayoutSubviews() {
// super.viewDidLayoutSubviews()
// //print(#function, " :", usersTotalPayments.keys.count)
// self.collectionViewLayout.invalidateLayout()
// }
// override func viewWillAppear(_ animated: Bool) {
// super.viewWillAppear(true)
// getPaymentDetails()
// }
func getPaymentDetails() {
//self.paymentStatusCollectionView.reloadData()
// Do any additional setup after loading the view.
let paymentsQuery = Payments.query()
paymentsQuery?.findObjectsInBackground(block: { (objects, error) in
if let objects = objects {
// print(objects.count)
if objects.count == 0 {
// Utils.stopIndicator(activityIndicator: activityIndicator)
Utils.createAlert(self, title: "No Payment has been made yet", message: "Please Encourage USers to make some Payments", buttonTitle: "Ok")
// TODO:- i think here, we should go back to our homeScreen or to a list of possible friends...
}else {
//print(#function, " no. of objects :", objects.count)
for paymentsObject in objects {
let user = paymentsObject[Utils.name] as! String
let amount = paymentsObject[Utils.amount] as! String
self.usersTotalPayments[user] = amount
self.users.append(user)
self.payments.append(amount)
//print(self.usersTotalPayments[user]!)
//print(#function, " A ---- USER :", user)
//if self.usersTotalPayments[user] == nil {
// //print(#function, " B ---- 0")
// self.usersTotalPayments[user] = [amount]
// print(self.usersTotalPayments[user]!)
//}else {
// //print(#function, " C ---- Other")
// self.usersTotalPayments[user]?.append(amount)
// print(self.usersTotalPayments[user]!)
//}
}
self.collectionView?.reloadData()
print("Done Reloading with COUNT: ", self.usersTotalPayments.keys.count)
}else if error != nil {
print(error!)
}else {
print("Unknown Error")
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
print(#function, " :", usersTotalPayments.keys.count)
// guard let coumt = usersTotalPayments.keys!.count else {
// return 0
// }
//if usersTotalPayments.keys.count == 1 {
// return 1
//} else {
return users.count
//}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 300, height:30)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return 2
}
// the function belo mde things worse
// override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// print(#function, " :", usersTotalPayments.keys.count)
// collectionView.reloadData()
// }
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PaymentStatusCollectionViewCell
print(#function, " :", indexPath)
// Configure the cell
cell.label.text = "Sec " + indexPath.section.description + "/Item " + indexPath.item.description
cell.backgroundColor = (indexPath.item % 2 == 0) ? UIColor.yellow : UIColor.lightGray
//let key = usersTotalPayments.
if indexPath.item == 0 { //item0 represents the object in column 0. the section is the index for row
cell.label.text = users[indexPath.section]
}else if indexPath.item == 1{
cell.label.text = payments[indexPath.section]
}
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//print("selected \(indexPath)")
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
// if isLandscape {
// return CGSize(width: 200, height:30)
// }
// else {
// return CGSize(width: 200, height:30)
// }
return CGSize(width: 200, height:30)
}
//
// func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// print(#function)
// return self.view.frame.size
// }
// func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
// //return CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height)
// return CGSize(width: 200, height:30)
// }
//
// func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat{
// return 2
// }
//
// func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets{
// return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
// }
//
//
// // MARK: UICollectionViewDelegateFlowLayout
// func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
// return 4.0
// }
//
}