I am trying to retrieve user profile image from parse. I have a collection view and I am retrieving all images people posted. I want to show each users profile image in the cell as well. I was using the below code
override func viewDidLoad() {
super.viewDidLoad()
let query = PFQuery(className: "Posts")
query.includeKey("pointName")
query.findObjectsInBackgroundWithBlock{(question:[AnyObject]?,error:NSError?) -> Void in
if error == nil
{
if let allQuestion = question as? [PFObject]
{
self.votes = allQuestion
self.collectionView.reloadData()
}
}
}
// Wire up search bar delegate so that we can react to button selections
// Resize size of collection view items in grid so that we achieve 3 boxes across
loadCollectionViewData()
}
/*
==========================================================================================
Ensure data within the collection view is updated when ever it is displayed
==========================================================================================
*/
// Load data into the collectionView when the view appears
override func viewDidAppear(animated: Bool) {
loadCollectionViewData()
}
/*
==========================================================================================
Fetch data from the Parse platform
==========================================================================================
*/
func loadCollectionViewData() {
// Build a parse query object
}
/*
==========================================================================================
UICollectionView protocol required methods
==========================================================================================
*/
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.votes.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("newview", forIndexPath: indexPath) as! NewCollectionViewCell
let item = self.votes[indexPath.row]
// Display "initial" flag image
var initialThumbnail = UIImage(named: "question")
cell.postsImageView.image = initialThumbnail
if let pointer = item["uploader"] as? PFObject {
cell.userName!.text = item["username"] as? String
print("username")
}
if let profile = item["uploader"] as? PFObject,
profileImageFile = profile["profilePicture"] as? PFFile {
cell.profileImageView.file = profileImageFile
cell.profileImageView.loadInBackground { image, error in
if error == nil {
cell.profileImageView.image = image
}
}
}
if let votesValue = item["votes"] as? Int
{
cell.votesLabel?.text = "\(votesValue)"
}
// Fetch final flag image - if it exists
if let value = item["imageFile"] as? PFFile {
println("Value \(value)")
cell.postsImageView.file = value
cell.postsImageView.loadInBackground({ (image: UIImage?, error: NSError?) -> Void in
if error != nil {
cell.postsImageView.image = image
}
})
}
return cell
}
However I found out that it sets profile image to the current user and not the user who posted the image. How can I do this? Thank you
UPDATE
so In parse my post class is
so I know who uploaded it but I don't know how to retrieve the profile image for this specific user.
You need to use a pointer that will point to the user who created the object. The profile photo should be in the user class. You then include the pointer in your query and that will return the user data.
okay, this might help you in objective-C
PFUser *user = PFUser *user = [PFUser currentUser];
[user fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {
_profileImage.file = [object objectForKey:#"profilePicture"];
}];
Related
I am trying to create a tableView of users from my Parse database that are in the same class (at school). All users have to have a username, but not all will have given the app their full name or set a profile picture. I use this code:
let studentsQuery = PFQuery(className:"_User")
studentsQuery.whereKey("objectId", containedIn: studentsArray! as! [AnyObject])
let query2 = PFQuery.orQueryWithSubqueries([studentsQuery])
query2.findObjectsInBackgroundWithBlock {
(results: [PFObject]?, error: NSError?) -> Void in
if error != nil {
// Display error in tableview
} else if results! == [] {
spinningActivity.hideAnimated(true)
print("error")
} else if results! != [] {
if let objects = results {
for object in objects {
if object.objectForKey("full_name") != nil {
let studentName = object.objectForKey("full_name")! as! String
self.studentNameResults.append(studentName)
}
if object.objectForKey("username") != nil {
let studentUsername = object.objectForKey("username")! as! String
self.studentUsernameResults.append(studentUsername)
}
if object.objectForKey("profile_picture") != nil {
let studentProfilePictureFile = object.objectForKey("profile_picture") as! PFFile
studentProfilePictureFile.getDataInBackgroundWithBlock({ (image: NSData?, error: NSError?) in
if error == nil {
let studentProfilePicture : UIImage = UIImage(data: image!)!
self.studentProfilePictureResults.append(studentProfilePicture)
} else {
print("Can't get profile picture")
// Can't get profile picture
}
self.studentsTableView.reloadData()
})
spinningActivity.hideAnimated(true)
} else {
// no image
}
}
}
} else {
spinningActivity.hideAnimated(true)
print("error")
}
}
This code works fine if all of the users have a username, full_name, and a profile_picture. I can't figure out, however, how to get a tableView of the usernames of a user and add a user's name or picture to the user's corresponding tableViewCell only if the user has a picture. Here is how my tableView is configured:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return studentUsernameResults.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("studentsCell", forIndexPath: indexPath) as! StudentsInClassInformationTableViewCell
cell.studentProfilePictureImageView.layer.cornerRadius = cell.studentProfilePictureImageView.frame.size.width / 2
cell.studentProfilePictureImageView.clipsToBounds = true
cell.studentProfilePictureImageView.image = studentProfilePictureResults[indexPath.row]
cell.studentUsernameLabel.text = studentUsernameResults[indexPath.row]
cell.studentNameLabel.text = studentNameResults[indexPath.row]
return cell
}
The studentProfilePictureResults, studentUsernameResults, and studentNameResults come from arrays of the user's picture, username, and name results pulled from Parse. If a user does not have a profile picture, I get the error, Index is out of range. Obviously, this means that there are, say, three names, three usernames, and only two pictures and Xcode doesn't know how to configure the cell. My question: How can I set a tableView up of a user's username and place their name and profile picture in the same cell, only if they have one?
Trying to store the different attributes in different arrays will be a problem, since as you have found, you end up with problems where a particular user doesn't have an attribute. You could use an array of optionals, so that you could store nil for an absent attribute, but it is much simpler to store the PFObject itself in a single array and accessing the attributes in cellForRowAtIndexPath rather than splitting out the attributes.
Since fetching the photo requires a separate, asynchronous, operation, you can store it separately. Rather than using an array to store the retrieved photos, which would have the same problem of ordering, you can use a dictionary, indexed by the user id; although for a large number of students it would probably be more efficient to use something like SDWebImage to download the photos as required in cellForRowAtIndexPath.
// these are instance properties defined at the top of your class
var students: [PFObject]?
var studentPhotos=[String:UIImage]()
// This is in your fetch function
let studentsQuery = PFUser.Query()
studentsQuery.whereKey("objectId", containedIn: studentsArray! as! [AnyObject])
let query2 = PFQuery.orQueryWithSubqueries([studentsQuery])
query2.findObjectsInBackgroundWithBlock {
(results: [PFObject]?, error: NSError?) -> Void in
guard (error == nil) else {
print(error)
spinningActivity.hideAnimated(true)
return
}
if let results = results {
self.students = results
for object in results {
if let studentProfilePictureFile = object.objectForKey("profile_picture") as? PFFile {
studentProfilePictureFile.getDataInBackgroundWithBlock({ (image: NSData?, error: NSError?) in
guard (error != nil) else {
print("Can't get profile picture: \(error)")
return
}
if let studentProfilePicture = UIImage(data: image!) {
self.studentPhotos[object["username"]!]=studentProfilePicture
}
}
}
spinningActivity.hideAnimated(true)
self.tableview.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.students != nil {
return self.students!.count
}
return 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("studentsCell", forIndexPath: indexPath) as! StudentsInClassInformationTableViewCell
cell.studentProfilePictureImageView.layer.cornerRadius = cell.studentProfilePictureImageView.frame.size.width / 2
cell.studentProfilePictureImageView.clipsToBounds = true
let student = self.students[indexPath.row]
if let studentPhoto = self.studentPhotos[student["username"]!] {
cell.studentProfilePictureImageView.image = studentProfilePictureResults[indexPath.row]
} else {
cell.studentProfilePictureImageView.image = nil
}
cell.studentUsernameLabel.text = student["username"]!
if let fullName = student["full_name"] {
cell.studentNameLabel.text = fullName
} else {
cell.studentNameLabel.text = ""
return cell
}
A few other pointers;
The use of _ to separate words in field names isn't really used in the iOS world; camelCase is preferred, so fullName rather than full_name
It looks like your Parse query could be more efficient if you had a class field or reference object so that you didn't need to supply an array of other class members.
I have a pointer in my parse. The pointer tells me who uploaded the images. I am trying to retrieve the username and the profile picture of the uploader. To do that I have put query.includeKey("uploader") . Users are managed through the user class. and posts are managed in the posts class. To retrieve the images and names I have the below code.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("newview", forIndexPath: indexPath) as! NewCollectionViewCell
let item = self.votes[indexPath.row]
// Display "initial" flag image
var initialThumbnail = UIImage(named: "question")
cell.postsImageView.image = initialThumbnail
if let pointer = item["uploader"] as? PFObject {
cell.userName!.text = item["username"] as? String
}
if let profile = item["uploader"] as? PFObject {
cell.profileImageView.loadInBackground({ (image:UIImage, error:NSError) -> Void in
if error != nil{
cell.profileImageView.image = image
}
})}
if let votesValue = item["votes"] as? Int
{
cell.votesLabel?.text = "\(votesValue)"
}
// Fetch final flag image - if it exists
if let value = item["imageFile"] as? PFFile {
cell.postsImageView.file = value
cell.postsImageView.loadInBackground({ (image: UIImage?, error: NSError?) -> Void in
if error != nil {
cell.postsImageView.image = image
}
})
}
return cell
}
However errors are happening saying that loadinbackround can't be invoked with an argument list of type (UIImage, NSError)->void. The strange part is that the error is only for the first part where I try to retrieve the images for the user. I am really stuck in this and want help. Is my pointer retrieving wrong? Thank you.
UPDATE 2
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
let query = PFQuery(className: "Posts")
query.includeKey("pointName")
query.findObjectsInBackgroundWithBlock{(question:[AnyObject]?,error:NSError?) -> Void in
if error == nil
{
if let allQuestion = question as? [PFObject]
{
self.votes = allQuestion
self.collectionView.reloadData()
}
}
}
// Wire up search bar delegate so that we can react to button selections
// Resize size of collection view items in grid so that we achieve 3 boxes across
loadCollectionViewData()
}
/*
==========================================================================================
Ensure data within the collection view is updated when ever it is displayed
==========================================================================================
*/
// Load data into the collectionView when the view appears
override func viewDidAppear(animated: Bool) {
loadCollectionViewData()
}
/*
==========================================================================================
Fetch data from the Parse platform
==========================================================================================
*/
func loadCollectionViewData() {
// Build a parse query object
}
/*
==========================================================================================
UICollectionView protocol required methods
==========================================================================================
*/
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.votes.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("newview", forIndexPath: indexPath) as! NewCollectionViewCell
let item = self.votes[indexPath.row]
// Display "initial" flag image
var initialThumbnail = UIImage(named: "question")
cell.postsImageView.image = initialThumbnail
if let pointer = item["uploader"] as? PFObject {
cell.userName!.text = item["username"] as? String
print("username")
}
if let profile = item["uploader"] as? PFObject,
profileImageFile = profile["profilePicture"] as? PFFile {
cell.profileImageView.file = profileImageFile
cell.profileImageView.loadInBackground { image, error in
if error == nil {
cell.profileImageView.image = image
}
}
}
if let votesValue = item["votes"] as? Int
{
cell.votesLabel?.text = "\(votesValue)"
}
// Fetch final flag image - if it exists
if let value = item["imageFile"] as? PFFile {
println("Value \(value)")
cell.postsImageView.file = value
cell.postsImageView.loadInBackground({ (image: UIImage?, error: NSError?) -> Void in
if error != nil {
cell.postsImageView.image = image
}
})
}
return cell
}
In my post class it is like this
The users are managed in the user class. I want to get the profile image and username of the person who posted the image.
In the user class I have all the user information.
cell.postsImageView.loadInBackground({
(image: UIImage!, error: NSError!) -> Void in
if error == nil {
if image != nil {
dispatch_async(dispatch_get_main_queue(),{
cell.postsImageView.image = image
})
}else{
println("Image not available")
}
}else{
println(Image Downloading error: \(error))
}
})
Try this , i think this will help you :)
Try to replace your loadInBackground method with the below:
cell.postsImageView.loadInBackground({ (image: UIImage!, error: NSError!) -> Void in
if error == nil {
cell.postsImageView.file = value
cell.postsImageView.image = image
}
})
}
You have two issues here, the first being the way you pass the closure to the loadInBackground method
The compiler error is because you're trying to call the method with non-optional closure parameters.
In the cell.postsImageView.loadInBackground call you use optionals, while in the cell.profileImageView.loadInBackground you don't.
Closure parameter types are important, that's why the compiler is complaining.
I'd suggest skipping the types and defining the closure like this:
cell.profileImageView.loadInBackground { image, error in
if error != nil{
cell.profileImageView.image = image
}
})}
The other and probably your main issue is that you don't set a file for the profile. So, if we say that the profile image is stored under imageFile in the uploader object, you would use:
if let profile = item["uploader"] as? PFObject,
profileImageFile = profile["imageFile"] as? PFFile {
cell.profileImageView.file = profileImageFile
cell.profileImageView.loadInBackground { image, error in
if error == nil {
cell.profileImageView.image = image
}
}
}
Though, as the Parse documentation says, the method downloads and displays the image:
Once the download completes, the remote image will be displayed.
https://parse.com/docs/ios/api/Classes/PFImageView.html#//api/name/loadInBackground:
If you don't need any special error handling and since you already have a placeholder image, try loading the image like this:
if let profile = item["uploader"] as? PFObject,
profileImageFile = profile["imageFile"] as? PFFile {
cell.profileImageView.file = profileImageFile
cell.profileImageView.loadInBackground()
}
I am trying to implement a like feature in my app using parse. If a user taps the vote up button. The label increases changing the like number in parse side as well. However with my code a user can tap many times to increase the like. I would like to make it detect that user has tapped and make the like button disabled. To do that I have made a class in parse called "Liked". I made a username, imageId both a string column and a likeStatus as a Boolean . However I can't make is so that if a user likes any image it will add new item to it with userId, ImageId and likeStatus.
This is the Collection View code
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("newview", forIndexPath: indexPath) as! NewCollectionViewCell
let item = self.votes[indexPath.row]
// Display the country name
if let value = item["imageText"] as? String {
cell.postsLabel.text = value
}
// Display "initial" flag image
var initialThumbnail = UIImage(named: "question")
cell.postsImageView.image = initialThumbnail
cell.complition = {
self.likeButton(indexPath)
}
if let votesValue = item["votes"] as? Int
{
cell.votesLabel?.text = "\(votesValue)"
}
// Fetch final flag image - if it exists
if let value = item["imageFile"] as? PFFile {
cell.postsImageView.file = value
cell.postsImageView.loadInBackground({ (image: UIImage?, error: NSError?) -> Void in
if error != nil {
cell.postsImageView.image = image
}
})
}
return cell
}
/*
==========================================================================================
Segue methods
==========================================================================================
*/
func likeButton(indexPath:NSIndexPath)
{
let cell = self.collectionView.cellForItemAtIndexPath(indexPath) as! NewCollectionViewCell
let object = self.votes[indexPath.row]
if let likes = object["votes"] as? Int
{
object["votes"] = likes + 1
object.saveInBackgroundWithBlock{ (success:Bool,error:NSError?) -> Void in
println("Data saved")
}
cell.votesLabel?.text = "\(likes + 1)"
}
else
{
object["votes"] = 1
object.saveInBackgroundWithBlock{ (success:Bool,error:NSError?) -> Void in
println("Data saved")
}
cell.votesLabel?.text = "1"
}
}
and this is the cell code
#IBAction func vote(sender: AnyObject) {
if self.complition != nil
{
self.complition!()
}
}
}
Any tips or How am I able to do this in code?Thank you.
The way I did this was by using a class in Parse that I called "UserLikeActivity" or something to that effect, and in it, it had a column pointer to the user that did the liking, a pointer to the actitivy that was liked (in my case it was a post), a type (indicating whether it was an upvote, downvote, follow, etc), and a pointer to the user who created the activity that was liked.
Now, when I was querying Parse to set my tables up, not only did I query the class that contained all the posts, but I also queried this class, which I then saved and used to determine the button state. So for every cell, if the activity had already been liked, I disabled the button. Hopefully this will help you get going in the right direction since you've asked this question about 7 times.
I am building a Vote/Instagram-type app, where a user selects a non-user-made photo which is pushed into a Timeline.
I have built out the selection screen and button, and the Timeline. For some reason, I can only guess that it has something to do with the chronology of saving the PFObject (the chose photo), the Timeline is displaying the photo chosen BEFORE the currently chosen photo.
The following block is the 'Select' button on the 'SelectScreenVC':
#IBAction func selectNext(sender: UIButton) {
let imageData = UIImagePNGRepresentation(lgImgURL.image)
let imageFile = PFFile(name: "\(cName.text!)", data: imageData)
var user = PFUser.currentUser()
user["nextChosen"] = "\(cName.text!)"
user["imageFile"] = imageFile
var userNextPhoto = PFObject(className: "UserNextPhoto")
userNextPhoto["username"] = PFUser.currentUser().username
userNextPhoto["cName"] = "\(cName.text!)" as String
userNextPhoto["imageFile"] = imageFile
userNextPhoto.saveInBackgroundWithBlock { (success: Bool!, error: NSError!) -> Void in
if error == nil {
println("User \(PFUser.currentUser().username) chose: \(self.cName.text!)")
} else {
println(error)
}
}
self.navigationController?.popToViewController(timeLineVC, animated: true)
}
This the TimeLineVC. I have the function which creates the timeline array and I skipped some of the TableView methods, except for the cellForRowAtIndexPath.
class TimeLineViewController: UITableViewController {
var timelineData:NSMutableArray = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
func loadData() {
timelineData.removeAllObjects()
var findTimelineData:PFQuery = PFQuery(className: "UserNextPhoto")
findTimelineData.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for object in objects {
self.timelineData.addObject(object)
}
} else {
NSLog("Error: %# %#", error, error.userInfo!)
}
let array: NSArray = self.timelineData.reverseObjectEnumerator().allObjects
self.timelineData = NSMutableArray(array: array)
self.tableView.reloadData()
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("timelineCell", forIndexPath: indexPath) as TimelineTableViewCell
let userNextPhoto:PFObject = self.timelineData.objectAtIndex(indexPath.row) as PFObject
let nextPhoto:PFObject = self.timelineData.objectAtIndex(indexPath.row) as PFObject
cell.usernameLabel.text = PFUser.currentUser().username
cell.cName.text = (userNextPhoto["cName"] as String)
return cell
}
I appreciate any help or insight!
That is what is going on: the new photo has not finished uploading when you pop the view controller. You can either wait for the photo to finish uploading (which is not a great UX), or you can pass back a reference to the newly-saved object back up to the popped view controller which can then display the new photo right away without waiting for the upload to finish.
I am fetching a string, NSDate and a PFFile from my Parse class to populate the collection view cells
All the cells load with image, date, info correctly. The info and date are ordered correctly (by ascending date). But every now and then when I build some of the images will be in a different cell. Im really scratching my head with this. Im guessing it has something to do with how I'm calling mixPhoto.getDataInBackgroundWithBlock({
I did try and use dispatch_async(dispatch_get_main_queue())
Still no luck... Heres my code, anyone got any ideas?
#IBOutlet weak var collectionView1: UICollectionView!
var mixPhotoArray : Array<UIImage> = []
var mixInfoArray: Array <String> = []
var mixDateArray: Array <NSDate> = []
override func viewDidLoad() {
super.viewDidLoad()
collectionView1.delegate = self;
collectionView1.dataSource = self;
self.queryParseMethod()
self.getImageData()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Do any additional setup after loading the view.
}
func getImageData() {
var query = PFQuery(className: "musicMixes")
query.orderByAscending("date")
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error: NSError!) -> Void in
for object in objects {
let mixPhoto = object["mixPhoto"] as PFFile
mixPhoto.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
dispatch_async(dispatch_get_main_queue()) {
let image = UIImage(data:imageData)
//image object implementation
self.mixPhotoArray.append(image!)
println(self.mixPhotoArray[0])
self.collectionView1.reloadData()
}
}
else {
println("error!!")
}
})//getDataInBackgroundWithBlock - end
}
}//for - end
}
func queryParseMethod() {
var query = PFQuery(className: "musicMixes")
query.orderByAscending("date")
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for object in objects {
let mixPhoto = object["mixPhoto"] as PFFile
let mixInfo = object["info"] as String
let dateForText = object["date"] as NSDate
//self.collectionView1.reloadData()
self.mixDateArray.append(dateForText)
self.mixInfoArray.append(mixInfo)
self.collectionView1.reloadData()
}//for - end
}
}
} // end of queryParseMethod
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
// MARK: UICollectionViewDataSource
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
//#warning Incomplete method implementation -- Return the number of sections
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//#warning Incomplete method implementation -- Return the number of items in the section
println("I have \(mixPhotoArray.count) Images")
return mixInfoArray.count
}
//func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:StreamCollectionViewCell = collectionView1.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as StreamCollectionViewCell
cell.mixImage.image = mixPhotoArray[indexPath.item]
cell.infoLabel.text = mixInfoArray[indexPath.item]
// NSDate array into cell
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
cell.mixDateLabel.text = dateFormatter.stringFromDate(mixDateArray[indexPath.item])
return cell
}
Like Wain said, I believe the main issue is that since your images are downloading at different speeds, they're not necessarily being appended to your array in order. Instead of recommending that you use a dictionary though, here's what I would recommend to circumvent that problem while still using an array:
// Declare your mixPhotoArray such that it can store optionals
var mixPhotoArray : Array<UIImage?> = []
func getImageData() {
var query = PFQuery(className: "musicMixes")
query.orderByAscending("date")
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error: NSError!) -> Void in
// Initialize your array to contain all nil objects as
// placeholders for your images
self.mixPhotoArray = [UIImage?](count: objects.count, repeatedValue: nil)
for i in 0...objects.count - 1 {
let object: AnyObject = objects[i]
let mixPhoto = object["mixPhoto"] as PFFile
mixPhoto.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
dispatch_async(dispatch_get_main_queue()) {
let image = UIImage(data:imageData)
// Replace the image with its nil placeholder
// and do so using the loop's current index
self.mixPhotoArray[i] = image
println(self.mixPhotoArray[i])
self.collectionView1.reloadData()
}
}
else {
println("error!!")
}
})
}
}
}
Then within collectionView:cellForItemAtIndexPath, you can set the image conditionally so that it only appears once its ready:
if mixPhotoArray[indexPath.item] != nil {
cell.mixImage.image = mixPhotoArray[indexPath.item]
}
You are storing your data in 2 arrays, mixPhotoArray and mixInfoArray, but you can't guarantee that they will both be in the same order. Images are different sizes so they will download at different speeds. You also shouldn't really be trying to download more than 4 at once so your current scheme isn't great.
Instead, you should have an array of dictionaries or custom classes which hold all of the details and which, when each image is downloaded, is updated with that image.
Obviously this means that you need to know which one is associated with the image you've just downloaded so you need to capture this dictionary / instance in the block so you can update it.
You could do it in 2 arrays as you are, so long as you capture an index where the image should be an insert the image to the array in the correct place.