swiping through Uiimageview pffile array - ios

Working code to download array and print image:
var query = PFQuery(className: "Cats")
query.orderByDescending("objectId")
query.findObjectsInBackgroundWithBlock ({( objects:[AnyObject]?, error: NSError?) in
if(error == nil){
//let imageObjects = objects as! [PFFile]
//if (rightSwipe.direction == .Right) {
let randomNumber = Int(arc4random_uniform(UInt32(objects!.count)))
println(randomNumber)
for object : PFObject in objects as! [PFObject] {
let thumbNail = object["image"] as! PFFile
println(thumbNail)
thumbNail.getDataInBackgroundWithBlock({
(imageData: NSData?, error: NSError?) -> Void in
if (error == nil) {
let image = UIImage(data:imageData!)
self.shoesImageView.image = image
}
})//getDataInBackgroundWithBlock - end
//}
}//for - end
}
else{
println("Error in retrieving \(error)")
}
})
Parse class photo:
photo of running app:
photo of storyboard:
What my goal is, is to swipe through images on each uiimage view. If you look at the output it is printing the array, I just don't know how to get it to swipe through the photos. Last, if there is a way to implement a form of randomization for the swipe that would be great. I've been tinkering with this for quite some time now and no luck.
Thanks guys

If you are looking to build an app similar to a photo gallery, then I recommend that you use a scroll view and let the user scroll with paging enabled.

Related

PFQuery in TableCell Cause Images to Jump

So I have researched and haven't found anything similar to my situation. All relevant code will be posted below. I am using parse for my backend and am trying to create a feed with people posted images. The images and username load fine, but since the display name and profile picture are in a separate file i had to query them separately. The query for those is located in the table cell itself. This causes images to jump while scrolling. I am aware that is because of the way cells dequeue. Is there a better way to either query or relate the data from both parse queries?
var usernameArray = [String]()
var detailArray = [String]()
var uuidArray = [String]()
var postArray = [PFFile]()
var followArray = [String]()
var page: Int = 10
var image: UIImage!
var follow = PFObject(className: "Follow")
func loadFollowers(){
let followerQuery = PFQuery(className: "Follow")
followerQuery.whereKey("follower", equalTo: (PFUser.current()?.username!)! as String)
followerQuery.whereKey("blocker", notEqualTo: (PFUser.current()?.username!)! as String)
followerQuery.findObjectsInBackground { (objects: [PFObject]?, error: Error?) in
if error == nil{
self.followArray.removeAll(keepingCapacity: false)
for object in objects!{
self.followArray.append(object.object(forKey: "following") as! String)
self.followArray.insert((PFUser.current()?.username!)! as String, at: 0)
}
let dataQuery = PFQuery(className: "Posts")
dataQuery.whereKey("user", containedIn: self.followArray)
dataQuery.limit = self.page
dataQuery.addDescendingOrder("_created_at")
dataQuery.findObjectsInBackground(block: { (objects: [PFObject]?, error: Error?) in
if error == nil{
self.usernameArray.removeAll(keepingCapacity: false)
self.detailArray.removeAll(keepingCapacity: false)
self.postArray.removeAll(keepingCapacity: false)
self.uuidArray.removeAll(keepingCapacity: false)
for object in objects!{
self.usernameArray.append(object.object(forKey: "user") as! String)
self.detailArray.append(object.object(forKey: "title") as! String)
self.postArray.append(object.object(forKey: "picture") as! PFFile)
self.uuidArray.append(object.object(forKey: "uuid") as! String)
self.tableView.reloadData()
}
}
})
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "postView") as! FeedTableViewCell
cell.userName.text = usernameArray[indexPath.row]
cell.details.text = detailArray[indexPath.row]
cell.uuid.text = uuidArray[indexPath.row]
let query = PFUser.query()
query?.whereKey("username", equalTo:cell.userName.text!)
query?.findObjectsInBackground(){(objects: [PFObject]?, error: Error?) -> Void in
if !(error != nil){
for object in (objects as [PFObject]?)!{
cell.displayName.text = object.object(forKey: "display") as? String
if let userPicture = object.object(forKey: "avi") as? PFFile {
userPicture.getDataInBackground(block: { (imageData: Data?, error: Error?) in
if (error == nil) {
let image = UIImage(data:imageData!)
cell.avi.image = image
}
})
}
}
}
}
aviArray[indexPath.row].getDataInBackground { (data: Data?, error: Error?) in
if error == nil{
cell.avi.image = UIImage(data: data!)
}
}
postArray[indexPath.row].getDataInBackground { (data: Data?, error: Error?) in
if error == nil{
cell.post.image = UIImage(data: data!)
}
}
return cell
}
Thank you in advance for the help. I am open to all suggestions because I am at a loss for this. Im not an expert so there is probably a much better way to do this.
Get the actual URL of the image, and set the images with SDWebImage.
No need to reinvent the wheel anymore. It will solve this problem and reduce the complexity of your code.
Listen up, my dude, you should create a pointer in the Follow table that stores the _User objects. Running a PFQuery like that in "cellForRowAt indexPath" is bad news. You should do everything in your power to structure the backend data on Parse to inter-relate in such a way where you can pull all data like that in the original query. However, it is safe to run small queries in the "cellForRowAt indexPath" using things like "strings" pulled from the server, images not so much, but, it's okay to load images in the background inside "cellForRowAt indexPath", but running full queries on images like that is not a good idea. My suggestion is use PFImageView and connect your Follow table to the _User table on the backend, this is possible, and it's the right way to do things. Also, you should consider inserting all images into an array when they are pulled in, what you're doing there is running a query on every cell every damn time the table view is updated, this is horrible.

Why am I only getting 2 objects for PFFile, but 10 for String?

I am importing a profile picture as well as other information such as name. There are 10 objects, and I get 10 names when importing from Parse, but I only get two photos. I can ENSURE 100% that the column names are correct AND there are images in the columns. I don't know if this error has any relevance, but I am guessing it does
let imagequery = PFQuery(className: "Animal")
imagequery.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if(error == nil){
for object in objects!{
if let thumbNail = object["Pic"] as? PFFile{
thumbNail.getDataInBackgroundWithBlock({
(imageData: NSData?, error: NSError?) -> Void in
if (error == nil) {
let image = UIImage(data:imageData!)
//image object implementation
self.tableImage.append(image!)
print(image)
}
})
}
if let name = object["Name"] as? String{
self.Name.append(name)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.collectionview.reloadData()
})
}
Something similar once happened to me when I imported rows from another parse app, images just didn't work because the parse files was not located in this database. I cleaned the column and uploaded images again and it worked.

How do I load an image from Parse into Swift

I've got an image in Parse that I want to load as the image for a button.
Here's my code:
let myData = PFObject(className: "Headliner")
if let theImageFileINeed = myData["ImageFile"] as! PFFile? {
theImageFileINeed.getDataInBackgroundWithBlock { (imageData: NSData?, error: NSError?) -> Void in
if (error == nil) {
print("loadingimage?")
if let imageData = imageData {
let image = UIImage(data: imageData)
self.headlinerImage.setImage(image, forState: UIControlState.Normal)
}
} else {
print("error")
}
}
}
Here's the code I'm referencing from the Parse documentation:
let userImageFile = anotherPhoto["imageFile"] as PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
}
}
}
When I use that exact code (I'm putting this in viewDidLoad, but am not sure if that's correct), swapping out the name of my table for "anotherPhoto" in the example (imageFile is the name of my field, too, so I didn't have to change that), I get the following error message: "Use of unresolved identifier "Headliner". So, then I assumed that maybe this goes inside a query? Or I need to specify the table somehow I want to pull data from, so I added the myData variable to pull that in.
When I run this, I don't get an error message, but my button doesn't update the image from parse.
I suspect it is related to types, probably in that "let my data = PFObject(className: "headliner") line... But I don't know how to fix it...
Any help would be appreciated! I bake cookies, so I'll send you some if you help me fix this!!!
Mali
Load image in tableView from Parse using PFFile
First step, make sure you import parse library:
import Parse
second step, declare a PFFile array, something like that:
var imageFiles = [PFFile]()
third, store all the images in the array:
let query = PFQuery(className:"your_class")
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil{
for writerData in objects! {
self.imageFiles.append(writerData["avatar"] as! PFFile)
}
/*** reload the table ***/
self.yourTableView.reloadData()
} else {
print(error)
}
}
Fourth, in order to show it (in my case I am displaying the images in UITableView), so in cellForRowAtIndexPath:
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! YourClassTableViewCell
imageFiles[indexPath.row].getDataInBackgroundWithBlock{
(imageData: NSData?, error: NSError?) -> Void in
if imageData != nil{
let image = UIImage(data: imageData!)
cell.cellAvatarImage.image = image
} else {
print(error)
}
}
Pretty sure this code should work. You may have to change the button settings in the interface builder to 'Custom'
Or maybe just create the button programmatically... See here:
How to create a button programmatically?
You need to try update your image in a different thread. ( This got me many times too)
Also I generally change the name unwrapped version of my variables so I can distinguish them easily.
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//Image update code goes here
if let unWrappedimageData = imageData {
let image = UIImage(data: unWrappedimageData)
self.headlinerImage.setImage(unWrappedimageData, forState: UIControlState.Normal)
}
})

Better way to retrieve multiple images from Parse

Noob question here and I know my code below is very wrong but it works in that it retrieves the 3 images I need. However, I'd like to know a better way to retrieve multiple images from Parse.
Any help would be greatly appreciated!
func retrieveImage() {
var query = PFQuery(className: "Items")
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
let imageObjects = objects as! [PFObject]
for (index, object) in enumerate(imageObjects) {
let thumbnail1 = object["image1"] as! PFFile
let thumbnail2 = object["image2"] as! PFFile
let thumbnail3 = object["image3"] as! PFFile
thumbnail1.getDataInBackgroundWithBlock{(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let image = UIImage(data: imageData!) {
self.itemImages[index] = image
}
}
thumbnail2.getDataInBackgroundWithBlock{(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let image = UIImage(data: imageData!) {
self.itemImages2[index] = image
}
}
}
thumbnail3.getDataInBackgroundWithBlock{(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let image = UIImage(data: imageData!) {
self.itemImages3[index] = image
}
}
}
}
}
}
}
}
First the idea... we want to do an arbitrarily long list of asynch tasks, collect their results, and be notified on completion or error. We do this by parameterizing the task (in this case, the PFFiles whose contents are to be fetched are the parameters), and we use those parameters as a "to-do list".
A recursive function does the work, picking off the first item in the list, doing the asynch task, and then calling itself with the remainder of the list. An empty to-do list means we're done.
I've tried to translate the answer I referred to here into swift (literally learning the language line by line)....
func load(pfFiles: Array<PFFile>, var filling: Dictionary<PFFile, UIImage>, completion: (success: Bool) -> Void) {
completion(success: true)
var count = pfFiles.count
if (count == 0) {
return completion(success: true)
}
var file = pfFiles[0]
var remainder = Array(pfFiles[1..<count])
file.getDataInBackgroundWithBlock{(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let image = UIImage(data: imageData!) {
filling[file.name] = image
self.load(remainder, filling: filling, completion: completion)
}
} else {
completion(success: false)
}
}
}
Given this is my first attempt, I'll be a little shocked and delighted if it works, but the algorithm is sound, and the swift compiles and appears to match the idea I outlined. Here's how to call it...
var pfFiles: Array<PFFile>
for (index, object) in enumerate(imageObjects) {
pfFiles.append(object["image1"])
pfFiles.append(object["image2"])
pfFiles.append(object["image3"])
}
var filling: Dictionary<String, UIImage>
// call the function here
// in the completion assign filling to property
// anytime after, when you have a PFFile like someObject["image2"]
// you use its name to look it up the UIImage in the results dictionary
Let me know if that last bit is clear enough. As you can see, I ran out of steam on my swift translation and resorted to pseudo code.
I believe you can just do self.itemImages[index] = thumbnail1.getData()!
If it crashs, do : query.includeKey("image1")
NOTE:
If you afraid to block the main queue, open a new thread to do such thing

Profile picture in cell not updating correctly

I have a PFQueryTableViewController populated by comments from different users. Each user has a profile picture stored on the Parse database. After loading each comment into a cell, I query the PFUser class to retrieve the profile picture of the user who posted the comment and add it to the cell. I also use PFCachePolicy to cache the profile picture to the device's memory so that displaying new cells with new profile pictures is a smoother transition.
However this is not the case. When a user posts a new comment and a new cell is added, the profile pictures shuffle around and takes about two seconds or so to update with the right image (probably because the table is re-queried and updated). I am trying to achieve something similar to iMessage or WhatsApp where the profile picture remained 'fixed' in the cell.
I am not sure what the problem is or if there is a better way to do this?
// get objectId of the user who posted a comment
let senderId = object?["Users"]!.objectId as String!
// query PFUser class using senderId to retrieve profile picture
var senderImage:PFQuery = PFUser.query()!
senderImage.cachePolicy = PFCachePolicy.CacheThenNetwork
senderImage.getObjectInBackgroundWithId(senderId){
(sender: PFObject?, error: NSError?) -> Void in
if error == nil && sender?.objectForKey("profilePicture") != nil {
let thumbnail = sender?.objectForKey("profilePicture") as? PFFile
thumbnail?.getDataInBackgroundWithBlock({
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
imageView.image = UIImage(data:imageData!)
} else {
println(error)
}
})
}
}
That's because you're not waiting until the images are finished loading when you update the UIImageView. Try Using this code:
var query = PFQuery(className:"Users")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
self.scored = objects!.count
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
let userImageFile = object["Image"] as! PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
self.imageArray.append(image!)
}
}
dispatch_async(dispatch_get_main_queue()) {
//don't reload image view here!
}
}
}
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
dispatch_async(dispatch_get_main_queue()) {
//wait until here to reload the image view
if self.imageArray.isEmpty == false {
//image array is not empty
self.ImageView.image = imageArray.first
}
else {
//no images found in parse
}
}

Resources