I'm having some trouble figuring out of how make the connection between my app and Parse when a user completes a task in my to-do app. I have created the PFObject for it and I have connected the label up and that works great but I am trying to figure out how to work with Bools in Parse.
let obj = PFObject(className: "Tasks")
obj.setValue(task, forKey: "task")
obj.setValue(self.team, forKey: "team")
obj.setValue(self.checked.isChecked, forKey: "done")
obj.saveInBackgroundWithBlock() {success,error in
if error != nil {
print(error)
return
}
if success {
self.loadTasks()
// self.tasks.insert(obj, atIndex: 0)
}
}
}
}))
"checked" is the class instance of CheckBox (all of the brains of the button/checkbox) and "isChecked" is a Bool with a didSet property that changes the checkbox to checked or unchecked based on whether the button is tapped.
The checkbox works great in the app, it just doesn't sync that action.
This is what I have tried so far but to no avail...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath)
// Configure the cell...
cell.selectionStyle = .None
let idx = tasks[indexPath.row]
let task = idx["task"] as! String
if let label = cell.viewWithTag(1) as? UILabel {
label.text = task
}
let done = idx["done"] as! Bool
if let checkBox = cell.viewWithTag(2) as? UIButton {
checked.isChecked = done
}
return cell
}
Any and all help is welcome.
EDIT
I figured out how to create the value and save it to Parse but now I'm having trouble editing that value and sending that new info to Parse. When I refresh the data it just goes back to the default value (false).
let done = idx["done"] as! Bool
if let checkBox = cell.viewWithTag(2) as? CheckBox {
CheckBox.isChecked = done
}
How do I save the new value?
Related
I have a table view where depending on the cell class it will download an image from Firebase. I've noticed when using the app that cells with the same cell identifier will show the previous downloaded image before showing the new one. This is what I have before changing it.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableData[indexPath.row]["Image"] != nil {
let cell = tableView.dequeueReusableCell(withIdentifier: "imageNotesData", for: indexPath) as! ImageNotesCell
cell.notes.delegate = self
cell.notes.tag = indexPath.row
cell.notes.text = tableData[indexPath.row]["Notes"] as! String
guard let imageFirebasePath = tableData[indexPath.row]["Image"] else {
return cell }
let pathReference = Storage.storage().reference(withPath: imageFirebasePath as! String)
pathReference.getData(maxSize: 1 * 1614 * 1614) { data, error in
if let error = error {
print(error)
} else {
let image = UIImage(data: data!)
cell.storedImage.image = image
}
}
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "notesData", for: indexPath) as! NotesCell
//let noteString = tableData[indexPath.row]["Notes"] as! String
cell.notes.text = tableData[indexPath.row]["Notes"] as! String
cell.notes.delegate = self
cell.notes.tag = indexPath.row
return cell
}
}
Knowing that this is not a good user experience and that it looks clunky, I tried to move the pathReference.getData to where I setup the data but the view appears before my images finish downloading. I have tried to use a completion handler but I'm still having issues.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
getSectionData(userID: userID, city: selectedCity, completion: {(sectionString) in
self.setupTableCellView(userID: userID, city: selectedCity, section: sectionString) { (tableData) in
DispatchQueue.main.async(execute: {
self.cityName?.text = selectedCity
self.changeSections.setTitle(sectionString, for: .normal)
self.currentSectionString = sectionString
self.setupTableData(tableDataHolder: tableData)
})
}
})
}
func setupTableCellView(userID: String, city: String, section: String, completion: #escaping ([[String:Any]]) -> () ) {
let databaseRef = Database.database().reference().child("Users").child(userID).child("Cities").child(city).child(section)
var indexData = [String:Any]()
var indexDataArray = [[String:Any]]()
databaseRef.observeSingleEvent(of: .value, with: { (snapshot) in
for dataSet in snapshot.children {
let snap = dataSet as! DataSnapshot
//let k = snap.key
let v = snap.value
indexData = [:]
for (key, value) in v as! [String: Any] {
//indexData[key] = value
if key == "Image" {
//let pathReference = Storage.storage().reference(withPath: value as! String)
print("before getImageData call")
self.getImageData(pathRef: value as! String, completion: {(someData) in
print("before assigning indexData[key]")
indexData[key] = someData
print("after assigning indexData[key]")
})
} else {
indexData[key] = value
}
}
indexDataArray.append(indexData)
}
completion(indexDataArray)
})
}
func getImageData(pathRef: String, completion: #escaping(UIImage) -> ()) {
let pathReference = Storage.storage().reference(withPath: pathRef as! String)
pathReference.getData(maxSize: 1 * 1614 * 1614, completion: { (data, error) in
if let error = error {
print(error)
} else {
let image = UIImage(data:data!)
print("called before completion handler w/ image")
completion(image!)
}
})
}
I don't know if I am approaching this the right way but I think I am. I'm also guessing that the getData call is async and that is why it will always download after showing the table view.
You can't do this.
Make the request from Firebase.
Over time, you will get many replies - all the information and all the changing information.
When each new item arrives - and don't forget it may be either an addition or deletion - alter your table so that it displays all the current items.
That's OCC!
OCC is "occasionally connected computing". A similar phrase is "offline first computing". So, whenever you use any major service you use every day like Facebook, Snapchat, etc that is "OCC": everything stays in sync properly whether you do or don't have bandwidth. You know? The current major paradigm of device-cloud computing.
Edit - See Fattie's comments about prepareForReuse()!
With reusable table cells, the cells will at first have the appearance they do by default / on the xib. Once they're "used", they have whatever data they were set to. This can result in some wonky behavior. I discovered an issue where in my "default" case from my data, I didn't do anything ecause it already matched the xib, but if the data's attributes were different, I updated the appearance. The result was that scrolling up and down really fast, some things that should have had the default appearance had the changed appearance.
One basic solution to just not show the previous image would be to show a place holder / empty image, then call your asynchronous fetch of the image. Not exactly what you want because the cell will still show up empty...
Make sure you have a local store for the images, otherwise you're going to be making a server request for images you already have as you scroll up and down!
I'd recommend in your viewDidLoad, call a method to fetch all of your images at once, then, once you have them all, in your success handler, call self.tableview.reloadData() to display it all.
In my Xcode Project I will like to have a similar view like Snapchat's "Send To..." screen (I have attached a screenshot). I have already made a tableview and populate it and have allowed multiple selection on. I am currently having trouble with two things:
1) Multiple Selection: I can select an cell I want, but when I tap on the search bar and start typing, all my previous selections go away. I am assuming that I need to add all of the names in a array and somehow communicate the array with the table so it shows if this username is in the array then make it selected in the tableview. But I am not sure how to do that. How can I do this?
2) Sending to Bottom Bar (blue in photo): As you may know, in Snapchat as you press on which users you want to send the snap to, their names get added to the bar at the bottom, as you fill up the bar, it because swipe able where you can horizontally scroll through the names you have added. I can append the names to an array and show the array in a label like theirs, but I do not know how to make it so a user can horizontally scroll through it.How do I implement this same feature?
Feel free to answer ANY of the questions! You do not need to do all of them, I just need them answered. Here's my code so far:
class User {
var userID:String?
var userFullName:String?
var userUsername:String?
var userProfileImage:PFFile?
var isPrivate:Bool
init(userID : String, userFullName : String, userUserName : String, userProfileImage : PFFile, isPrivate : Bool) {
self.userID = userID
self.userFullName = userFullName
self.userUsername = userUserName
self.userProfileImage = userProfileImage
self.isPrivate = isPrivate
}
}
var userArray = [User]()
func loadFriends() {
//STEP 1: Find friends
let friendsQuery = PFQuery(className: "Friends") //choosing class
friendsQuery.whereKey("friendOne", equalTo: PFUser.current()?.objectId ?? String()) //finding friends
friendsQuery.limit = self.page //number of users intitally showing
friendsQuery.findObjectsInBackground (block: { (objects, error) -> Void in
if error == nil { //if no error
//clean up
self.friendsArray.removeAll(keepingCapacity: false)
//STEP 2: Find related objects depending on query setting
for object in objects! {
self.friendsArray.append(object.value(forKey: "friendTwo") as! String) //hold array info of friend
}
//STEP 3: Find friend info
let query = PFUser.query()
query?.whereKey("objectId", containedIn: self.friendsArray)
query?.addDescendingOrder("createdAt") //how to order users
query?.findObjectsInBackground(block: { (objects, error) -> Void in
if error == nil {
for object in objects! {
var user : User
let fullname = (object.value(forKey: "fullname") as! String)
let username = (object.object(forKey: "username") as! String)
let profilePhoto = (object.object(forKey: "profilePhoto") as! PFFile)
let objectID = (object.objectId!)
let isPrivate = (object.object(forKey: "isPrivate") as! Bool)
user = User(userID: objectID, userFullName: fullname, userUserName: username, userProfileImage: profilePhoto, isPrivate: isPrivate)
self.userArray.append(user)
}
self.tableView.reloadData()
} else {
print(error!)
}
})
} else {
print(error!)
}
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! FriendCell
let user = userArray[indexPath.row]
//add user info to cells
cell.fullnameLabel.text = user.userFullName
cell.usernameLabel.text = user.userUsername
cell.objectID = user.userID!
cell.isPrivate = user.isPrivate
user.userProfileImage?.getDataInBackground (block: { (data, error) in
if error == nil {
cell.profilePhoto.image = UIImage(data: data!)
}
})
})
}
1) Multiple Selection:
You should have a User class (e.g User) that holds user properties instead of maintaining array for each property. Store User object in a Array. User class could be like below:
class User {
var userID:String
var userFullName:String
var userName:String
var userProfileImageUrl:String
init(userID:String,userFullName:String,userName:String,userProfileImageUrl:String) {
self.userID = userID
self.userFullName = userFullName
self.userName = userName
self.userProfileImageUrl = userProfileImageUrl
}
}
You could have a User extension to check if that user is selected or not(e.g isSelected).
import UIKit
import Foundation
private var selectedKey: UInt8 = 0
extension User {
var isSelected:Bool{
get {
return objc_getAssociatedObject(self, &selectedKey) as! Bool
}
set {
objc_setAssociatedObject(self, &selectedKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Now in your func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell check that user.isSelected == true/false and update your selected/deselected image accordingly.
And update the value of isSelected in func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
2) Sending to Bottom Bar:
For bottom bar add a UICollectionView as a subview in UIView. Create a class overriding UICollectionViewCell that holds a UILabel. You can add flow layout in UICollectionView.
I have given just an idea to start with.Hope it will help you.
I think, you set bool check for every cell in tableView. If cell load again, it will not show check. Because, It check is false.
I am creating a basic note taking though parse using Swift. I want to save the attributed text to parse, but I can't figure out how. I've look online and I was not successful. Does anyone have any ideas? Here is my code:
NotesTableViewController.swift:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! NotesTableViewCell
let object: PFObject = self.notObjects.objectAtIndex(indexPath.row) as! PFObject
cell.masterCell?.text = object["title"] as? String
cell.masterTextLabel?.text = object["text"] as? String
return cell
addNoteTableController.swift:
override func viewDidLoad() {
super.viewDidLoad()
self.colorPicker.alpha = 0
self.fontPicker.alpha = 0
if (self.object != nil) {
self.titleField?.text = self.object["title"] as? String
self.textView?.text = self.object["text"] as? String
}else {
self.object = PFObject(className: "Note")
}
}
#IBAction func saveAction(sender: UIBarButtonItem) {
self.object["username"] = PFUser.currentUser()?.username
self.object["title"] = self.titleField?.text
self.object["text"] = self.textView?.text
self.object.saveEventually { (sucess, error) -> Void in
if (error == nil) {
}else {
print(error?.userInfo)
}
}
self.navigationController?.popViewControllerAnimated(true)
}
Note: Please be very specific in your answers. Don't just give me a link. I really need the code written for me at this point.... THANKS!!!!!
You have an NSAttributedString, which supports coding, so you can use an NSKeyedArchiver to create an NSData instance representing the raw attributed string. This includes the text and all of the formatting.
Using this data you can create a PFFile and save it, you also need to associate this file with another object in your parse store so you can get it back later. When you do you can get the NSData from it and use an NSKeyedUnarchiver to unpack it back into an NSAttributedString.
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 have some issue to display the 'author's username of each posts in my TableViewController.
It actually display the current user's username for all display posts, how to display each poster's username ?
I'm using Xcode 6.3 and Parse.com API.
The timeLabel is displayed correctly, but the userLabel display the current user who is logged in instead of the author of the post.
If I logged out and login with a different username all the userLabel change to the new user. The debug console display Optional("theNameOfTheCurrentUser") as many times as there are posts displayed.
Parse host 2 DB one for users (User) and one for posts (Poemes), there is a pointer in Poemes table to the specific user.
I update to Xcode 6.3 lately and had an error on var findLover:PFQuery = PFUser.query()
Value of optional type 'PFQuery?' not unwrapped
I add the exclamation mark (!) at the end of this line, which remove the error, is this causing the issue ?
I read Parse documentation and follow some exemples but looks like I'm a bit lost here, any help and suggestions will be highly appreciated, thanks.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:DisplayTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! DisplayTableViewCell
let poeme:PFObject = self.timelineData.objectAtIndex(indexPath.row) as! PFObject
cell.poemeLabel.text = poeme.objectForKey("content") as? String
var dateFormatter:NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd HH:mm"
cell.timeLabel.text = dateFormatter.stringFromDate(poeme.createdAt!)
var findUser:PFQuery = PFUser.query()!
findUser.findObjectsInBackgroundWithBlock {
(objects, error)->Void in
if var objects = objects {
let author:PFUser = (objects as NSArray).lastObject as! PFUser
cell.userLabel.text = author.username
println(author.username)
})
}
return cell
}
The function findUser.findObjectsInBackgroundWithBlock happens in the background, while the main thread still running, so by the time you get the response from parse with the values you need the cell you are trying to return in the function is long gone.
The easiest way to fix it is to fetch all the data you need before hand and safe it in a array and use this array to populate the cell.
Finally get it work for Xcode 6.3.2 changes, here is the result :
unwrap and optional seams to be my main problem :
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {
let cell:DisplayTableViewCell = tableView!.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath!) as! DisplayTableViewCell
let poeme:PFObject = self.timelineData.objectAtIndex(indexPath!.row) as! PFObject
cell.poemeLabel.text = poeme.objectForKey("content") as! String
var dateFormatter:NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd HH:mm"
cell.timeLabel.text = dateFormatter.stringFromDate(poeme.createdAt!)
var findUser:PFQuery = PFUser.query()!
findUser.whereKey("objectId", equalTo: poeme.objectForKey("user")!.objectId!!)
findLover.findObjectsInBackgroundWithBlock {
(objects, error)->Void in
if var objects = objects {
let author:PFUser = (objects as NSArray).lastObject as! PFUser
cell.userLabel.text = author.username
println(author.username)
})
}
}
return cell
}