How do I load an image from Parse into Swift - ios

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)
}
})

Related

Retrieve multiple Parse Images(Swift)

Below is an example of how i would typically retrieve images from my Parse.com. I have now run into the situation where i would like to retrieve 20+ images from Parse but i am looking for a more efficient way to do so. Please can someone explain how to implement this in code and how i should store the 20+ PFFiles in Parse?
func loadData(){
let findDataParse = PFQuery(className: "JobListing")
findDataParse.findObjectsInBackgroundWithBlock{
(objects: [PFObject]?, error: NSError?) -> Void in
if (error == nil) {
for object in objects! {
let userImageFile = object["ImageOne"] as! PFFile
let userImageFile1 = object["ImageTwo"] as! PFFile
let userImageFile2 = object["ImageThree"] as! PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
let listingImage1 = UIImage(data:imageData!)
userImageFile1.getDataInBackgroundWithBlock {
(imageData1: NSData?, error1: NSError?) -> Void in
let listingImage2 = UIImage(data:imageData1!)
userImageFile2.getDataInBackgroundWithBlock {
(imageData2: NSData?, error1: NSError?) -> Void in
let listingImage3 = UIImage(data:imageData2!)
self.flyerImageLarge1.image = listingImage1
self.flyerImageLarge2.image = listingImage2
self.flyerImageLarge3.image = listingImage3
}}}}}}}
You can use ParseUI for cleaner and more efficient code.
To do so, add the ParseUI framework.
Then, click the image and change the "class" to PFImageView.
You can see this here.
Once you do that, you can easily set the PFImageView's image:
if let myServerImage = object.valueForKey("imageFromUser") as? PFFile {
self.myImage.file = myServerImage
self.myImage.loadInBackground()
}
Where myServerImage is the image you are retrieving from the Parse server and myImage is the image in your storyboard.
In your case it will be something like this:
func loadData(){
let findDataParse = PFQuery(className: "JobListing")
findDataParse.findObjectsInBackgroundWithBlock{
(objects: [PFObject]?, error: NSError?) -> Void in
if (error == nil) {
if let myServerImage = object.valueForKey("ImageOne") as? PFFile {
self.flyerImageLarge1.file = myServerImage
self.flyerImageLarge1.loadInBackground()
}
if let myServerImage = object.valueForKey("ImageTwo") as? PFFile {
self.flyerImageLarge2.file = myServerImage
self.flyerImageLarge2.loadInBackground()
}
if let myServerImage = object.valueForKey("ImageThree") as? PFFile {
self.flyerImageLarge3.file = myServerImage
self.flyerImageLarge3.loadInBackground()
}
}
}
I recommend adding the if let statement so that you don't get an error when an image doesn't exist.
Please forgive me if what I am saying is obvious or already considered. I'm not completely familiar with what you are doing, but it looks applicable.
Assuming you are not using it already, you will need to use a recursive function. Basically, a function that calls itself until the end condition.
I'm not familiar with your code so I'll demonstrate with a simple example in JavaScript:
/* A "public" function that your program will call */
function getStuff(total)
{
//quick positive check
if (total > 0)
{
//start and pass in an empty array
return _getStuffRecursion(total, []);
}
else
{
//total is not positive, return empty array
return [];
}
}
/* A "private" function that will do the recursion */
function _getStuffRecursion(total, resultsArray)
{
//do work this is where you would call your function that does the work.
var someResource = Math.random();
//add work to the array collected so far
resultsArray.push(someResource);
//change count
var newTotal = total - 1;
//check condition
if (newTotal > 0)
{
//recursive condition, go to the next level down and pass in what is collected so far
return _getStuffRecursion(newTotal, resultsArray)
}
else
{
//end condition met, just return the array with everything collected from the upper levels
return resultsArray;
}
}
/* Start */
//get started by calling the "public" function
var results = getStuff(20);
//print it to console
console.log(results);
If this solution works, I'm sure you can adapt it to Parse.

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

swiping through Uiimageview pffile array

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.

Getting error when loading image file from Parse

The line in question is "let productImageFile = productData!["productImage"] as! PFFile" which gives me the error "fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)". The only answers I've found have involved making sure I am not trying to unwrap explicitly defined optionals (I think that's the term), but I messed around with the optionals, and which I unbind and when, but I'm having no luck. No other source has been able to solve this specific issue for me and I'm stuck. Please help.
override func viewDidLoad() {
super.viewDidLoad()
//Create new PFQuery to retrieve info from Parse
var query: PFQuery = PFQuery(className: "MyProduct")
//function to get the data
func getProductData (){
//call function to get the data from parse by specifyng an objectId
query.getObjectInBackgroundWithId("XXXXXXXXXX") {
(productData:PFObject?, error:NSError?) -> Void in
if error == nil && productData != nil {
//Extract values from the productData PFObject and store them in constants
let dayOfTheWeek = productData!.objectForKey("day") as! String
let productTitle = productData!.objectForKey("productTitle") as! String
//-----start image loading
let productImageFile = productData!["productImage"] as! PFFile
productImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
self.productImageView.image = image!
} else {println("Could not load image.")}
}
}
//-----end image loading
let productPrice = productData!.objectForKey("productPrice") as! String
let productDescription = productData!.objectForKey("productDescription") as! String
//take the saved constants and assign their values to the labels and UIImage on screen
self.productTitleLabel.text = productTitle
self.dayOfTheWeekLabel.text = dayOfTheWeek
self.productPriceLabel.text = productPrice
self.productDescriptionLabel.text = productDescription
} else if error != nil {
println("Could not load data from Parse")
}
}
}
I assume not for all products you have an image, so you need to update your code as following:
if let productImageFile = productData!["productImage"] as? PFFile {
productImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
self.productImageView.image = image!
} else {println("Could not load image.")}
}
}
}
This will guarantee that productImageFile will be processed only if response of productImage is PFFile.
Ensure you are getting valid data if your error is not printing anything useful. Check the data length and see if it corresponds to the file size.
Also, check to see if the imageView is setting the image on the main thread, it will not show if called from a background thread.
dispatch_async(dispatch_get_main_queue(), ^ {
self.productImageView.image = image!
}

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