Here is function where I try to fetch images from parse for users that are in the namesArray.
func fetchData(){
let imagePredicate = NSPredicate(format: "username IN %#", namesArray)
let imageQuery = PFQuery(className: "_User", predicate: imagePredicate)
imageQuery.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
for object in objects! {
self.imagesArray.append(object["image"] as! PFFile)
if self.imagesArray.count == self.namesArray.count {
self.tableView.reloadData()
}
} else {
print("error: \(error?.localizedDescription)")
}
}
}
Here is my tableView:cellForRowAtIndexPath method:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! ChatsCell
cell.nameLabel.text = namesArray[indexPath.row]
if imagesArray.count == namesArray.count && self.imagesLoaded == false{
imagesArray[indexPath.row].getDataInBackgroundWithBlock { (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let image = UIImage(data: imageData!)
cell.imageView?.image = image
self.tableView.reloadData()
self.imagesLoaded = true
}
}
}
return cell
}
But when I do so I see that images are not synchronised with names of the users. Even if I put my users in other order images will stay in the same order as they was before.
How can I change it?
Not sure what you're asking here. Is it that you were expecting the images to be returned in an array sorted by the user?
If so, then you will need to add a sort order to your PFQuery. I suggest you sort your namesArray by username, and then also sort the imageQuery by username:
imageQuery.orderByDescending("username")
Hope I understood the question ;]
--T
So I found that if you use one query you will receive ordered data so I've changed my code and now it works pretty well. So what I've done is that I do query for every separate member of the namesArray:
func fetchData() {
for index in 0..<self.namesArray.count {
let imagePredicate = NSPredicate(format: "username == %#", namesArray[index])
let imageQuery = PFQuery(className: "_User", predicate: imagePredicate)
imageQuery.findObjectsInBackgroundWithBlock({ (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
for object in objects! {
self.imageFilesArray![index] = object["image"] as? PFFile
}
for imageFile in self.imageFilesArray! {
let index = self.imageFilesArray?.indexOf{$0 == imageFile}
imageFile?.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
let userImage = UIImage(data: imageData!)
self.imagesArray?[index!] = userImage
self.tableView.reloadData()
})
}
}
})
}
}
and here is tableView:cellForRowAtIndexPath:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! ChatsCell
cell.nameLabel.text = namesArray[indexPath.row]
cell.messageTextLabel.text = messagesArray[indexPath.row]
cell.chatImageView.image = self.imagesArray![indexPath.row] != nil ? self.imagesArray![indexPath.row] : UIImage(named: "add")
return cell
}
Related
I am trying to import a PFFile which is an image and put that in a cell, but I get the error fatal error: Index out of range. Why am I getting this? How do I fix it? Am I importing the image correctly?
if let imagepic = object["Animal"] as? PFObject{
if let imageofpic = imagepic["Pic"] as? PFFile{
imageofpic.getDataInBackgroundWithBlock({ (data: NSData?, error: NSError?) -> Void in
if(error == nil){
let image = UIImage(data: data!)
self.imagepro.append(image!)
dispatch_async(dispatch_get_main_queue()){
self.table.reloadData()
}
}else{
print(error)
}
})
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: extrasescell = table.dequeueReusableCellWithIdentifier("extra") as! extrasescell
cell.pic.image = imagepro[indexPath.row]
return cell
}
I have 4 functions containing Parse query.findObjectsInBackgroundWithBlock. These are being called to grab data and then populate the table view. Using dispatch groups.
Here is two examples of my parse querys
func getEventImages() {
print("getEventImages enter")
dispatch_group_enter(self.group)
let query = PFQuery(className: "events")
query.orderByAscending("eventDate")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error: NSError!) -> Void in
// Initialize your array to contain all nil objects as
// placeholders for your images
if error == nil {
self.eventMainImageArray = [UIImage?](count: objects.count, repeatedValue: nil)
for i in 0...objects.count - 1 {
let object: AnyObject = objects[i]
let mainImage = object["mainImage"] as! PFFile
//dispatch_group_enter(self.group)
mainImage.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
let mainImage = UIImage(data:imageData)
self.eventMainImageArray[i] = mainImage
print("getEventImages appended")
}
else {
print("error!!")
}
})
}
}
print("getEventImages leave")
dispatch_group_leave(self.group)
}
}
func getEventInfo() {
print("eventInfo enter")
dispatch_group_enter(group)
let query = PFQuery(className: "events")
query.orderByAscending("eventDate")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!,error: NSError!) -> Void in
self.eventNameArray = [String?](count: objects.count, repeatedValue: nil)
self.eventInfoArray = [String?](count: objects.count, repeatedValue: nil)
self.eventDateArray = [NSDate?](count: objects.count, repeatedValue: nil)
self.eventTicketsArray = [String?](count: objects.count, repeatedValue: nil)
if error == nil {
for i in 0...objects.count - 1 {
let object: AnyObject = objects[i]
let eventName = object["eventName"] as! String
let eventInfo = object["eventInfo"] as! String
let eventDate = object["eventDate"] as! NSDate
let eventTicket = object["Tickets"] as! String
self.eventNameArray[i] = eventName
self.eventInfoArray[i] = eventInfo
self.eventDateArray[i] = eventDate
self.eventTicketsArray[i] = eventTicket
print("event info appended")
}
}
print("event info leave")
dispatch_group_leave(self.group)
}
}
And my dispatch_group_nofity
dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in
print("Finished reloadDataFromServer()")
self.tableView.reloadData()
self.refreshControl?.finishingLoading()
}
}
The problem is that its hit and miss if the data gets retrieved quick enough before dispatch_group_leave(self.group) is called leading to reloading the tableview data too soon. I need to get this so the dispatch_group_leave gets called when the appending is completed.
There is no need for two methods to retrieve the data, no need to unpack the data into multiple arrays and no need to use dispatch groups.
All you need is a simple method to retrieve your event data
var events:[PFObject]=[PFObject]()
func getEventInfo() {
let query = PFQuery(className: "events")
query.orderByAscending("eventDate")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!,error: NSError!) -> Void in
if error==nil {
self.events=objects as! [PFObject]
self.tableView.reloadData()
} else {
print("Something went wrong! - \(error)"
}
self.refreshControl?.finishingLoading()
}
}
Then, you haven't shown your cellForRowAtIndexPath but you would have something like
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! MyTableViewCell
let event=self.events[indexPath.row]
cell.eventName.text=event["eventName"] as? String
cell.eventInfo.text=event["eventInfo"] as? String
if let mainImageFile=event["mainImage"] as? PFFile {
mainImageFile.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
let mainImage = UIImage(data:imageData)
cell.mainImage= mainImage
}
else {
print("error!!")
}
}
return cell;
}
You can use a PFImageView or a framework like SDWebImage to handle image caching and putting a placeholder image in place while the image is loaded.
If you want to update an event is as easy as
var event=self.events[someindex];
event["eventName"]=newValue
event.saveInBackground()
I am appending the username column and the image column to two different arrays from parse. I am then putting them into the collection view. I am anticipating that the username in the nameArray corresponds to the imageArray, but majority of the time they are in the wrong order. How do I get them to append into the array in the right order? i.e. User 1 has picture 1, user 2 has picture 2. username array = [User 1, User 2]. image array = [picture 1, picture 2].
func getFriendPicandName(){
let imagequery = PFQuery(className: "_User")
imagequery.findObjectsInBackgroundWithBlock {( objects: [AnyObject]?, error: NSError?) -> Void in
// for object in objects!{
var user = PFUser.currentUser()
let relation = user!.relationForKey("Friendship")
relation.query()!.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
for object in objects!{
let userPic = object["ProPic"] as! PFFile
userPic.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if(error == nil){
let image = UIImage(data: imageData!)
self.arrayOfFriends.append(image!)
print(self.arrayOfFriends)
}
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
})
}
}
}
var query = PFQuery(className: "_User")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var user = PFUser.currentUser()
let relations = user!.relationForKey("Friendship")
relations.query()!.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...(objectIDs.count){
self.arrayOfFriendsNames.append(objectIDs[i].valueForKey("username") as! String)
print(self.arrayOfFriendsNames)
}
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
}
})
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayOfFriendsNames.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: friendcellView = collectionView.dequeueReusableCellWithReuseIdentifier("friendcell", forIndexPath: indexPath) as! friendcellView
cell.friendname.text = arrayOfFriendsNames[indexPath.item]
cell.friendpic.image = arrayOfFriends[indexPath.item]
cell.friendpic.layer.cornerRadius = cell.friendpic.frame.size.width/2;
cell.friendpic.clipsToBounds = true
return cell
}
You should not call your query twice, I would imagine your for loop to look something like this:
for object in objects!{
let userPic = object["ProPic"] as! PFFile
userPic.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if(error == nil){
let image = UIImage(data: imageData!)
self.arrayOfFriends.append(image!) // Add image here
print(self.arrayOfFriends)
}
self.arrayOfFriendsNames.append(object.valueForKey("username") as! String) // Add Name here
}
I don't know much about swift but I combined you both function into one...i hope it will help you...
func getFriendPic(){
let imagequery = PFQuery(className: "_User")
imagequery.findObjectsInBackgroundWithBlock {( objects: [AnyObject]?, error: NSError?) -> Void in
// for object in objects!{
var user = PFUser.currentUser()
let relation = user!.relationForKey("Friendship")
relation.query()!.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...(objectIDs.count){
self.arrayOfFriendsNames.append(objectIDs[i].valueForKey("username") as! String)
print(self.arrayOfFriendsNames)
}
for object in objects!{
let userPic = object["ProPic"] as! PFFile
userPic.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if(error == nil){
let image = UIImage(data: imageData!)
self.arrayOfFriends.append(image!)
print(self.arrayOfFriends)
}
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
})
}
}
}
}
I have a UIImage array populated with PFFiles from Parse.
how do I put the UIImages into the cell?
Below is my code.
var arrayOfFriends = [UIImage]()
var imagequery = PFQuery(className: "_User")
query.findObjectsInBackgroundWithBlock {( objects: [AnyObject]?, error: NSError?) -> Void in
for object in objects!{
let userPic = object["ProPic"] as! PFFile
userPic.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if(error == nil){
let image = UIImage(data: imageData!)
self.arrayOfFriends.append(image!)
print(self.arrayOfFriends)
}
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
})
}
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: friendcellView = collectionView.dequeueReusableCellWithReuseIdentifier("friendcell", forIndexPath: indexPath) as! friendcellView
//this is where the error occurs(error message above)
cell.friendpic.image = UIImage(named: arrayOfFriends[indexPath.row])
cell.friendpic.layer.cornerRadius = cell.friendpic.frame.size.width/2;
cell.friendpic.clipsToBounds = true
return cell
}
You already have it:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: friendcellView = collectionView.dequeueReusableCellWithReuseIdentifier("friendcell", forIndexPath: indexPath) as! friendcellView
//this is where the error occurs(error message above)
// change this line
// cell.friendpic.image = UIImage(named: arrayOfFriends[indexPath.row])
cell.friendpic.image = arrayOfFriends[indexPath.row] // here, arrayOfFriends[indexPath.row] is a UIImage, you don't have to do anything more
cell.friendpic.layer.cornerRadius = cell.friendpic.frame.size.width/2;
cell.friendpic.clipsToBounds = true
return cell
}
Just change this line
cell.friendpic.image = UIImage(named: arrayOfFriends[indexPath.row])
to this:
cell.friendpic.image = arrayOfFriends[indexPath.row]
arrayOfFriends[indexPath.row].getDataInBackgroundWithBlock { (data: NSData?, erreur: NSError?) -> Void in
if erreur == nil {
let image = UIImage(data: data)
cell.friendpic.image = image
}
}
this goes in cellForRowAtIndexPath
I am using Parse to load some data for title , http req for ImageView and ratingView. I also have a segment control view with 2 options.
I put an indicator where the image is presented , but when I scroll down
or when I press another segment(different movies) I get for 1-2 seconds the previous image , ratingView , titles and they change after this instantly. And obviously not all together. Can I somehow sync the changes or give an indicator 'waiting to load' until everything loads ?
func segmentView(segmentView: SMSegmentView, didSelectSegmentAtIndex index: Int) {
self.currentSegmentSelected = index
loadData().findObjectsInBackgroundWithBlock { [weak self] objects, error in
if error == nil {
dispatch_async(dispatch_get_main_queue(), {
self?.loadObjects()
})}
}
}
queryForTable(Parse)
override func queryForTable() -> PFQuery {
let query:PFQuery = PFQuery(className:"Movies")
if(objects?.count == 0)
{
query.cachePolicy = PFCachePolicy.CacheThenNetwork
}
if self.currentSegmentSelected == 0 {
query.whereKey("genres", containsString: "Adventure")
}
else if currentSegmentSelected == 1 {
query.whereKey("genres", containsString: "Comedy")
}
return query
}
cellForRowAtIndexPath
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cellIdentifier:String = "cell"
var cell:TableCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? TableCell
if(cell == nil) {
cell = TableCell(style: UITableViewCellStyle.Default, reuseIdentifier: cellIdentifier)
}
//??? indicator.startAnimating()
//??? indicator.backgroundColor = UIColor.whiteColor()
if let pfObject = object {
PFCloud.callFunctionInBackground("averageStars", withParameters: ["movieId":pfObject["movieId"]]) {
(response: AnyObject?, error: NSError?) -> Void in
if let rating = response as! Float? {
cell?.rating.rating = rating
}}
if let movieId = pfObject["movieId"] as? Int {
if RecommendedSet.ratings[movieId] == nil {
cell?.userRating.rating = 0
} else {
cell?.userRating.rating = RecommendedSet.ratings[movieId]!
}
}
cell?.title?.text = pfObject["title"] as? String
if let Id = pfObject["tmdbId"] as? Int {
Alamofire.request(.GET, timdb , parameters: ["api_key": APIkey])
.responseJSON { response in
if let JSON...
let imgData: NSData = NSData(contentsOfURL: imgURL)!
dispatch_async(dispatch_get_main_queue(), {
cell?.posterView?.image = UIImage(data: imgData)
//??? self.indicator.stopAnimating()
//??? self.indicator.hidesWhenStopped = true
})
}}}}}}}
return cell
}