I'm trying to make a slideshow with this library
https://github.com/zvonicek/ImageSlideshow
So i'm making a query, i bring some images and i append them here
var sliderArray = [UIImage]()
var testimg:UIImage!
let idaki = recipeObj.objectId
let pointer2 = PFObject(outDataWithClassName:"Recipes", objectId: idaki!)
let galquery = PFQuery(className:"sliderRecipes")
galquery.whereKey("recipe", equalTo: pointer2)
galquery.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
print("Successfully retrieved \(objects!.count) scores.")
// Do something with the found objects
if let objects = objects {
for object in objects {
count += 1
let glrimg = object["sliderImage"] as! PFFile
glrimg.getDataInBackgroundWithBlock {
(imageData3: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData2 = imageData3 {
self.testimg = UIImage(data:imageData2)
print(self.testimg)
print(self.sliderArray)
self.sliderArray.append(UIImage(data:imageData2)!)
}
if count == objects.count { print(self.sliderArray) }
}
}
// print("test")
// print(self.sliderPinakas)
}
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
And after all this i DONT have an array with images. In the print above it first shows the empty array and after that it shows the prints of UI Images that i want to show them like this to a slider
slideshow.setImageInputs([ImageSource(image: UIImage(named: "myImage"))!, ImageSource(image: UIImage(named: "myImage2"))!])
but instead of having to do it with the name, UIImage(named: "myImage"))! i would like to do it this way
self.slider.setImageInputs([ImageSource(image: self.testimg)])
for all the images of the array, cause the above line shows only one.
Is there anyone that could help?
Thanks!
You have pass to self.slider.setImageInputs array of all objects ImageSource [ImageSource(image: self.testimg1), ImageSource(image: self.testimg2), ...] etc., not only one object [ImageSource(image: self.testimg)].
If you add images like:
self.slider.setImageInputs([ImageSource(image: self.testimg)])
self.slider.setImageInputs([ImageSource(image: self.testimg2)])
you'll simply replace first image with second.
Create new array, enumerate all images from sliderArray, and append they to array ImageSource(image: self.testimg1) objects.
Also remember, you have to call this from block glrimg.getDataInBackgroundWithBlock!
add before for
var count = 0
and add inside block
count +=1
if count == objects.count {
print(self.sliderArray)
}.
That will print array when all objects finished loads (they can loads in chaotic order).
Related
I try to do a query with Parse containing a few Strings and Images that will be added to an array. The strings in the array are all in the right order but not the images. I think its probably because some images are smaller than the other ones and so they get appended to the array earlier than they are supposed to. Is there any way to "save" space in the array for the images to keep them in the right order? It's probably not that hard to solve that but I am a Newbie :( Thank you!
query.findObjectsInBackground (block: { (objects:[PFObject]?, error: Error?) -> Void in
for object in objects! {
DispatchQueue.global(qos: .userInteractive).async {
// Async background process
if let imageFile : PFFile = self.bild.append(object.value(forKey: "Bild") as! PFFile) {
imageFile.getDataInBackground(block: { (data, error) in
if error == nil {
DispatchQueue.main.async {
// Async main thread
let image = UIImage(data: data!)
image2.append(image!)
}
} else {
print(error!.localizedDescription)
}
})
}
}
}
})
Your analysis is correct that the requests will complete in non-deterministic order, partly or mostly influenced by the amount of data that must be returned.
Instead of an array to which you append the UIImage (or data), use a mutable dictionary that maps strings to UIImage. A reasonable choice for the string key is the PFFile name.
EDIT I'm not a Swift writer, but I tried to express the idea below (don't depend on it compiling, but I think the idea is sound)
class MyClass {
var objects: [PFObject] = []
var images: [String: UIImage] = [:] // we'll map names to images
fetchObjects() {
// form the query
query.findObjectsInBackground (block: { (objects:[PFObject]?, error: Error?) -> Void in
self.objects = objects
self.fetchImages()
})
}
fetchImages() {
for object in self.objects! {
if let imageFile : PFFile = object["Bild"] as PFFile {
self.fetchImage(imageFile);
}
}
}
fetchImage(imageFile: PFFile) {
imageFile.getDataInBackground(block: { (data, error) in
if error == nil {
self.images[imageFile.name] = UIImage(data: data!)
// we can do more here: update the UI that with image that has arrived
// determine if we're done by comparing the count of images to the count of objects
} else {
// handle error
}
}
}
}
This will get the images in the background and keep them associated with their filenames using a dictionary. The OP code didn't explain what self.bild is, but I assumed it was an instance array of retrieved PFFiles. I replaced this with the images instance var.
Image file order is maintained by the object collection: to get the Nth image, get the Nth object, get it's "Bild" property, that PFFile's name is the key into your images dictionary.
var n = // some index into objects
var object : PFObject = self.objects[n]
var file : PFFile = object["Bild"]
var name : String = file.name
var nthImage = self.images[name] // is nil before fetch is complete
I have a problem and I could really use some help..
I have the method below and everything works fine, until line 907.. When it comes for the object3.saveInBackgroundWithBlock, it does nothing.. Not even errors! It never saves the object and it never goes inside the block..
Any idea why?
func addUserToThoseIFollow(sender: UIButton) {
//self.navigationItem.rightBarButtonItem?.enabled = false
sender.enabled = false
let userQuery = PFQuery(className: "_User")
let userQuery2 = PFQuery(className: "_User")
userQuery.getObjectInBackgroundWithId(PFUser.currentUser().objectId) { (object: PFObject!, error: NSError!) -> Void in
if error == nil {
// If I already follow some users, make
// an array with them, add the user I
// want to follow and save. Else,
// just save an array, with that one user.
if object["following"] != nil {
var thoseIFollow = object["following"] as! [String]
thoseIFollow.append(self.userID!)
object["following"] = thoseIFollow
}
else {
var myUsers = [String]()
myUsers.append(self.userID!)
object["following"] = myUsers
}
object.saveInBackgroundWithBlock({ (ok: Bool, error2: NSError!) -> Void in
if error2 == nil {
self.followButton.setTitle("Unfollow", forState: .Normal)
self.followButton.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.7)
sender.enabled = true
self.doIFollow = true
}
})
}
}
// Add me to his followers
userQuery2.getObjectInBackgroundWithId(self.userID) { (object3: PFObject!, error3: NSError!) -> Void in
if error3 == nil {
// If the user I just followed, has already followers, make
// an array with them and add the current user to
// them. Else, just save an array, with the current user.
if object3["followers"] != nil {
var hisFollowers = object3["followers"] as! [String]
hisFollowers.append(PFUser.currentUser().objectId)
object3["followers"] = hisFollowers
/* Line 907 */ object3.saveInBackgroundWithBlock({ (ok7: Bool, error7: NSError?) -> Void in // Line 907
if error7 == nil {
print("ok")
}
else {
print(error7)
}
})
}
else {
var hisFollowers = [String]()
hisFollowers.append(PFUser.currentUser().objectId)
object3["followers"] = hisFollowers
object3.saveInBackgroundWithBlock( { (ok5: Bool, error7: NSError!) -> Void in
print("otinanai")
if error7 != nil {
print(error7.localizedDescription)
}
})
}
}
}
}
Attempt #1
What PFUser.currentUser().objectId return ? If it returns nil so it doesn't work.
Attempt #2
Available Parse Types
So far we've used values with type NSString, NSNumber, and PFObject. Parse also supports NSDate, and NSNull.
You can nest NSDictionary and NSArray objects to store more structured data within a single PFObject.
Try to use var hisFollowers = [NSString]() instead of var hisFollowers = [String]()
self.userID
Where exactly is this coming from?
Did your check, whether it is an optional?
Comment out the first query and see if it works.
Each Parse object can only have one background thread running for it at a time. Say you save an object, then immediately in the next line (not inside of its call back), edit it and then save it again. The second save will not be called, since the first save is still running. You don't even get an error. You get zero notification whatsoever that this call didn't happen.
My guess is that you have objects being saved inside both the first query and the second query, and because of that, the second query's save is being skipped.
The solution would be to stick the second query inside of the first's callback.
I think that there's a PromiseKit library you can download that adds javascript functionality in iOS, making it more similar to how you'd chain these calls in cloud code, but I haven't used it.
I am making a running app and would like to have a viewController in which the user has running tips and facts randomly display on the field. I would like to query parse for the objectIds to then reference the id and assign the label the tip or fact. Currently I have hard coded the Ids into the app but I would like that array to contain the results from the query the code is as follows:
func GetObjectID(){
ObjectIDs = ["id1","id2","id3","id4","id5","id6","id7","id8"]
RandomID = Int(arc4random_uniform(UInt32(ObjectIDs.count)))
}
func TheInfo(){
GetObjectID()
var query : PFQuery = PFQuery(className: "FactsandTips")
query.getObjectInBackgroundWithId(ObjectIDs[RandomID]){
(ObjectHolder : PFObject?, error: NSError?) -> Void in
if (error == nil) {
self.fact = ObjectHolder!.valueForKey("Fact") as? String
self.tips = ObjectHolder!.valueForKey("Tips") as? Array
if(self.tips.count > 0){
self.factLabel.text = self.fact
self.Button1.setTitle(self.tips[0], forState: UIControlState.Normal)
self.Button2.setTitle(self.tips[1], forState: UIControlState.Normal)
self.Button3.setTitle(self.tips[2], forState: UIControlState.Normal)
self.Button4.setTitle(self.tips[3], forState: UIControlState.Normal)
}
} else {
print("There is something wrong!")
}
}
}
I am using swift, Xcode7, and parse as my backend
Below is the code I use to query a Parse table, retrieve all results and add it all into an array. I then use the array as the source for a pickerView.
var query:PFQuery = PFQuery(className: "Drivers")
query.addAscendingOrder("Name")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
self.astrDrivers.append(object["Name"]! as! String)
}
}
self.pkvDriverTrack.reloadAllComponents()
} else {
print("Error: \(error) \(error!.userInfo)")
}
}
Note the line self.astrDrivers.append(object["Name"]! as! String). This is adding the Name column of each record to my self.astrDrivers array.
If you wanted to do retrieve multiple columns, your best bet is to create a custom object like below:
class ObjectNewFact:NSObject {
var column1:String = String() // You might want to choose more descriptive variable names (I just don't know what your column names are).
var column2:Int = Int()
// etc.
}
You could then create an array of ObjectNewFacts with a line like
var aFacts:[ObjectNewFact] = [ObjectNewFact]()
Then you could amend your routine to retrieve the data from Parse to:
var query:PFQuery = PFQuery(className: "Drivers")
query.addAscendingOrder("Name")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
var NewFact:ObjectNewFact = ObjectNewFact()
NewFact.column1 = object["Column1"] as! String
NewFact.column2 = object["Column2"] as! Int
self.aFacts.append(NewFact)
}
}
self.pkvDriverTrack.reloadAllComponents()
} else {
print("Error: \(error) \(error!.userInfo)")
}
}
Now you have an array full of facts. You might want to go down this custom object approach because you can also include things like the Fact ID or how many times the fact has been shown (if you're keeping track of that sort of thing). It provides a more flexible solution for any changes in the future.
I hope this helped.
I want to know how I could store the entire custom column (the user Pointer<_User> column from a custom class) and put them all in an array variable so that I can see if a the user exists in that class or not. This is what I have:
Old Code
var objectUserIdArray = [String]()
let objectUserIdQuery : PFQuery = PFQuery(className: "Scores")
objectUserIdQuery.findObjectsInBackgroundWithBlock {
(objects : [PFObject]? , error : NSError?) -> Void in
var objectID = objects! as [PFObject]
for i in 0..<objectID.count {
objectUserIdArray.append(objectID[i].objectId!)
}
for _ in objectID {
print(objectUserIdArray)
}
New Code
func saveScoresOnParse() {
objectUserIdQuery.whereKey("User", equalTo: PFObject(withoutDataWithClassName: "_User", objectId: userID))
objectUserIdQuery.findObjectsInBackgroundWithBlock {
(objects : [PFObject]? , error : NSError?) -> Void in
if error == nil {
//var objectID = objects! as [PFObject]
/*for i in 0..<objectID.count {
self.objectUserIdArray.append( objectID[i].objectId! )
}*/
for _ in objects! {
print(objects)
}
// The score key has been incremented
for (var i = 0 ; i < self.objectUserIdArray.count ; i++) {
if self.userID != objects![i] {
print("New Scores")
print("R: \(self.rightAnswers)")
print("W: \(self.wrongAnswers)")
print("S: \(self.skippedQuestions)")
self.scores["User"] = PFUser.currentUser()
self.scores["Right"] = self.rightAnswers
self.scores["Wrong"] = self.wrongAnswers
self.scores["Skipped"] = self.skippedQuestions
self.scores.saveInBackground()
} else if self.userID == objects![i] {
print("Updated Scores")
self.scores.incrementKey("Right", byAmount: 1)
self.scores.incrementKey("Wrong", byAmount: 1)
self.scores.incrementKey("Skipped", byAmount: 1)
print("R: \(self.rightAnswers)")
print("W: \(self.wrongAnswers)")
print("S: \(self.skippedQuestions)")
self.scores.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// The score key has been incremented
} else {
// There was a problem, check error.description
}
}
} else {
print("Error")
}
}
} else {
print(error)
}
}
But it only stores the objectId column and not the Pointer<_User> column. I know this because when I print the stuff that is inside, it prints out the objectIds.
This is what happens, instead of just updating the current user's scores, it just makes new ones. I want the if statement to check if the user already exists in that column and if it does updates the scores and if it doesn't, make new ones. (The new code's if statement doesn't work, i have to bring it out for it to save...)
Your updated question make clearer what you are actually wanting to do;
Save or update a user's scores in your Parse Score object. To do this, there is no reason to retrieve any object Ids or loop through any results. More often than not you don't do use Object Ids explicitly when using Parse; you can simply pass the object itself with Parse working out the references for you.
I am not sure how you exactly want to change the scores; in your code above you increment in one case but set the scores explicitly in another, but the code below shows the general approach.
If you are frequently or repeatedly going to update a score record then you could make your code more efficient by holding a reference to the Scores object in a property after you find it the first time and simply update & save it subsequently.
func saveScoresOnParse() {
if let currentUser=PFUser.currentUser() {
let scoreQuery= PFQuery(className: "Scores")
scoreQuery.whereKey("User",equalTo:currentUser)
scoreQuery.getFirstObjectInBackgroundWithBlock {
(object : PFObject? , error : NSError?) -> Void in
if error == nil {
var scoreObject=object ?? PFObject.objectWithClassName("Scores")
if (scoreObject["User"]==nil) {
scoreObject["User"]=currentUser
}
scoreObject["Right"]=self.rightAnswers
scoreObject.saveInBackground()
} else {
print(error)
}
}
} else {
print("No current user!")
}
}
I have an array that starts out empty but is filled with PFFiles (image data) with a PFQuery. A UIImageView has its image set using the data in the PFFile Array. However, if the array is empty then there is an error saying array index is out of range. Therefore, I need something testing to see if array is empty and I can't find a way to do that.
var imageFiles = [PFFile]()
And then in the viewDidLoad
self.imageFiles[self.imageCounter].getDataInBackgroundWithBlock{
(imageData, error) -> Void in
if error == nil {
let image = UIImage(data: imageData!)
self.mainPic.image = image
}else {
}
}
I would like to be able do something like:
If let testVariable = self.imageFiles[self.imageCounter] as PFFile {
}
Or more simply:
If self.imagesFiles[self.imageCounter] == nil {
}
But neither of those work
The problem is you're going out of the bounds of the array, as you said. You can void this by checking the size of the array before trying to access an element. The following should do the trick:
if (self.imagesFiles.count > self.imageCounter) {
//myImage = self.imageFiles[self.imageCounter];
}