So here is my Problem.. I've retrieved the user data from _User class in Parse and showing it in the app as follows:
var data:NSMutableArray = NSMutableArray()
func loadData() {
data.removeAllObjects()
var userQuery = PFUser.query()
userQuery?.orderByAscending("createdAt")
userQuery?.findObjectsInBackgroundWithBlock({ (objects, erroe) -> Void in
if let objects = objects {
for object in objects {
if let user = object as? PFUser {
if user.objectId != PFUser.currentUser()?.objectId {
self.data.addObject(object)
}
self.tableView.reloadData()
}
}
}
})
}
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return data.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("users", forIndexPath: indexPath) as! userListTableViewCell
let userData:PFObject = self.data.objectAtIndex(indexPath.row) as! PFObject
// Usernames and gender..
myCell.fullName.text = userData.objectForKey("fullName") as! String!
myCell.genderLabel.text = userData.objectForKey("gender") as! String!
// Profile pictures..
let profilePics = userData.objectForKey("profilePicture") as! PFFile
profilePics.getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
myCell.dp.image = downloadedImage
}
}
return myCell
}
Now I want to add a follow button to follow a particular user and want to save that data in Parse. So my questions are:
How can I add the follow button effectively which would not mess up things if there are so many users.. I've tried giving them tag like myCell.followButton.tag = indexPath.row but it dint work well. so I want to know about some other way for achieving my goal.
What is the best possible way to save the follower list in Parse.. I'm thinking to make a class named Followers having the columns user : the user being followed and follower where we can add the PFUser.currentUser().objectId . Is there anything better than this or this is a nice method to do it?
here is the screen shot of my userListTableViewController..
Here you can see the followButton which i've already connected to userListTableViewCell.. Please help me out.. Thanks for your time..
Lets make that button you want a UILabel instead and add a UIGestureRecognizer to it, one like this
Create a swift file named PFUserTapGestureRecognizer.swift
import UIKit
class PFUserTapGestureRecognizer: UITapGestureRecognizer
{
var tapper: PFUserTapper
init(id: Int, onTap: (id: Int) -> Void)
{
self.tapper = PFUserTapper()
self.tapper.id = id
self.tapper.onTap = onTap
super.init(target: self.tapper, action: Selector("userTapped"))
}
}
class PFUserTapper : NSObject
{
var id = 0
var onTap: ((idUse: Int) -> Void)?
func userTapped()
{
onTap?(idUser: id)
}
}
Now, when you load your cell in your view controller where you are loading your UITableView, in the delegate tableView:cellForRowAtIndexPath:, do this:
// Check if you are following that user already
// if im NOT following this user then
{
let idUser = userData.objectForKey("id") as! Int
myCell.labelFollow.tag = idUser
myCell.labelFollow.text = "Follow"
addFollowBehavior(follow: true, idUser: idUser, label: myCell.labelFollow)
}
// else if i'm following the user
{
myCell.labelFollow.text = "Unfollow"
addFollowBehavior(follow: false, idUser: idUser, label: myCell.labelFollow)
}
// else.. you should consider the case when you click Follow and it takes time to get an answer from your service
And create this function
func addFollowBehavior(follow follow: Bool, idUser: Int, label: UILabel)
{
if let recognizers = label.gestureRecognizers {
for recognizer in recognizers {
label.removeGestureRecognizer(recognizer as! UIGestureRecognizer)
}
}
label.addGestureRecognizer(PFUserTapGestureRecognizer(idUser,
onTap: { (idUser) -> Void in
// THIS IS WHERE YOU SAVE TO PARSE, WEB SERVICE OR WHATEVER
// if follow then
// Do Follow
// else
// UnFollow...
// On the callback write this to protect from reused cells:
if label.tag = idUser {
myCell.labelFollow.text = follow ? "Unfollow" : "Follow" // NOW (UN)FOLLOWING!
addFollowBehavior(follow: !follow, idUser: idUser, label: myCell.labelFollow)
}
}
)
}
I didn't had the chance to test it but I have a very similar implementation on a requirement like that and it works perfectly
Note: this works if the follow item is an UIImageView or whatever inherits from UIView for a cooler look, instead of changing the text, you change the UIImage
Related
I've created a profile page to display all of the users blog post. In my viewController I've used a PFQueryTableViewControllerto query the users blogs. The problem I'm having is when I run my app to see the current users blogs, I see the current users post first in the order it was created, then under I see everyone else's blogs that is coming from the className I created in Parse. The post are also repeating when I'm on this page. Is there something I need to do in my code to only show the current signed in users blogs instead of all of the post from Parse? I've pasted my code below to get a better idea.
import UIKit
import Parse
import ParseUI
class ProfileVC: PFQueryTableViewController {
var profilePage:NSMutableArray! = NSMutableArray()
override init(style: UITableViewStyle, className: String!) {
super.init(style: style, className: className)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.parseClassName = "BlogPost"
self.textKey = "blogging"
self.pullToRefreshEnabled = true
self.objectsPerPage = 200
}
override func viewDidLoad() {
super.viewDidLoad()
let user = PFObject(className:"BlogPost")
user["writer"] = PFUser.currentUser()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func objectsDidLoad(error: NSError?) {
super.objectsDidLoad(error)
profilePage = NSMutableArray(array: objects!)
self.tableView.reloadData()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let query = PFQuery(className: "BlogPost")
if let user = PFUser.currentUser() {
query.whereKey("writer", equalTo: PFUser.currentUser()!.username!)
query.addAscendingOrder("createdAt")
query.findObjectsInBackgroundWithBlock {(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil{
for object in objects!{
let blog : PFObject = object as! PFObject
self.profilePage.addObject(blog)
}
let array : NSArray = self.profilePage.reverseObjectEnumerator().allObjects
self.profilePage = NSMutableArray(array: array)
self.tableView.reloadData()
}
}
}
}
// MARK: - Table view data source
override func objectAtIndexPath(indexPath: NSIndexPath!) -> PFObject? {
var obj : PFObject? = nil
if(indexPath.row < self.profilePage.count){
obj = self.profilePage[indexPath.row] as? PFObject
}
return obj
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return profilePage.count
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == 0 {
return 110
}
else {
return 90
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath?) -> PFTableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("PCell", forIndexPath: indexPath!) as! ProfileCell
if let object : PFObject = self.profilePage.objectAtIndex(indexPath!.row) as? PFObject {
cell.WritersName.text = object["writer"] as? String
cell.WritersBlog.text = object["Blog"] as? String
let dateCreated = object.createdAt! as NSDate
let date = NSDateFormatter()
dateFormat.dateFormat = "h:mm a"
cell.timeProfile.text = NSString(format: "%#", date.stringFromDate(dateCreated)) as String
}
return cell
}
// }
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
}
I can see a couple of potential problems with your code.
First, you are adding to the same array every time you perform a query.
If you initialize your array at the time you perform the query, the array will be limited to the current query's results and will not include results from previous fetches.
To do this, add an initialization statement into the section where you are setting up your query.
if let user = PFUser.currentUser() {
profilePage = NSMutableArray() // Reset the profile page array.
query.whereKey("writer", equalTo: PFUser.currentUser()!.username!)
query.addAscendingOrder("createdAt")
...
Additionally, the code in objectsDidLoad: may be overwriting the results that you are creating through your query.
Try disabling the assignment to profilePage in there if you are still not getting the correct results.
override func objectsDidLoad(error: NSError?) {
super.objectsDidLoad(error)
// profilePage = NSMutableArray(array: objects!)
self.tableView.reloadData()
}
I am retrieving objects from a relation in parse. The objects I want are successfully retrieved and printed in the output box, but when I run the app my UITable only presents one of the six objects. Any suggestions on how to get all of them up onto my view? I would greatly appreciate it.
class MyGroupsHomePage: UITableViewController {
let cellidentifer = "MyGroupsCell"
var mygroupsdata: NSMutableArray = NSMutableArray()
func findcurrentuserobjects () {
var currentuser = PFUser.query()
currentuser!.whereKey("username", equalTo: PFUser.currentUser()!.username!)
currentuser!.findObjectsInBackgroundWithBlock { (object:[AnyObject]?, error: NSError?) -> Void in
if error == nil && object != nil {
if let object = object as? [PFObject] {
for objects in object {
self.mygroupsdata.addObject(objects)
}
}
}
self.tableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
findcurrentuserobjects()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.mygroupsdata.count
}
var groupnamearray: NSMutableArray = NSMutableArray()
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellidentifer, forIndexPath: indexPath) as! UITableViewCell
let mygroupdata: PFObject = self.mygroupsdata.objectAtIndex(indexPath.row) as! PFObject
let relation = mygroupdata.relationForKey("UserGroups")
let query = relation.query()
query?.findObjectsInBackgroundWithBlock({ (objet:[AnyObject]?, erro: NSError?) -> Void in
if erro == nil && objet != nil {
if let objet = objet as? [PFObject] {
for objets in objet {
println(objets.objectForKey("GroupName")!)
cell.textLabel?.text = objets.objectForKey("GroupName")! as? String
}
}
} else {
println("Error, could not retrieve user groups \(erro)")
}
})
return cell
}
}
As Paulw11 stated, this is the problem:
for objets in objet {
println(objets.objectForKey("GroupName")!)
cell.textLabel?.text = objets.objectForKey("GroupName")! as? String
}
You keep updating the same property "text" in the same textLabel, which I assume is an IBOutlet in the UITableViewCell subclass that you use to define the apparence of your cell. Without knowing more of how you want this text to be layed out it it difficult to suggest an answer. A quick and dirty way could be (I haven't tested):
for objets in objet {
println(objets.objectForKey("GroupName")!)
let obj = objets.objectForKey("GroupName")! as? String
let newString = "\(obj) "
cell.textLabel?.text = "\(cell.textLabel?.text)\(newString)"
}
But, according to what you want to acheive, you might need to add subviews to your UITableViewCell subclass (either on your cell prototype in Storyboard or programmatically).
I have a JSON Data which I want to get into UITable. The data is dynamic so table should update every time view loads. Can anyone help?
{
data = (
{
id = 102076330;
name = "Vicky Arora";
}
)
}
try this....
When you receive response,get the whole array of dictionary
if let arr = response["data"] as? [[String:String]] {
YourArray = arr
// Define YourArray globally
}
Then in tableview cell,cellForRowAtIndexPath method
if let name = YourArray[indexpath.row]["name"] as? String{
label.text = name
}
//Same You can done with id
And don't forget to set number of rows
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return YourArray.count
}
Try this one. But this sample i'm using Alamofire and SwitfyJSON. Import it using CocoaPod.
import UIKit
import Alamofire
class TableViewController: UITableViewController{
var users: [JSON] = []
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "http://xxxxx/users.json").responseJSON { (request, response, json, error) in
if json != nil {
var jsonObj = JSON(json!)
if let data = jsonObj["data"].arrayValue as [JSON]?{
self.users = data
self.tableView.reloadData()
}
}
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return users.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("UserCell", forIndexPath: indexPath) as! UITableViewCell
let user = users[indexPath.row]
if let idLabel = cell.viewWithTag(100) as? UILabel {
if let id = user["id"].string{
idLabel.text = id
}
}
if let nameLabel = cell.viewWithTag(101) as? UILabel {
if let name = user["name"].string{
nameLabel.text = name
}
}
return cell
}
}
If you are up to using Core Data, I would suggest using the NSFetchedRequest.
Every time you are getting the data from the server, save it to Core data, and that will automatically update the table view.
Here is a tutorial from Ray Wenderlich
These are my code in swift
This is the code of retrieving the current user's friend list object with parse
class UserViewController: UITableViewController {
var userArray: NSMutableArray = []
#IBOutlet weak var friendListTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
retrieveMessages()
}
func retrieveMessages() {
var query = PFUser.query()
if let username = PFUser.currentUser().username {
query.whereKey("username", equalTo: username)
query.findObjectsInBackgroundWithBlock {
(objects, error) -> Void in
for object in objects! {
let usernames:String? = (object as PFObject)["Friends"] as? String
println(usernames) // It prints "nil"
if usernames != nil {
self.userArray.addObject(usernames!)
}
}
dispatch_async(dispatch_get_main_queue()) {
self.friendListTableView.reloadData()
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return userArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Update - replace as with as!
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = userArray[indexPath.row] as? String
return cell
}
and this is the code of saving current user's friend when the current user add them via username with parse
class addUserViewController: UIViewController {
#IBOutlet weak var usernameTextField: UITextField!
#IBAction func addUser(sender: AnyObject) {
var query = PFUser.query()
query.whereKey("username", equalTo: usernameTextField.text)
println("Pass")
query.getFirstObjectInBackgroundWithBlock{ (object:PFObject!, error: NSError!) -> Void in
if error == nil {
let currentUser = PFUser.currentUser()
let friendList = currentUser.relationForKey("Friends")
var addFriend = object
if addFriend != nil {
friendList.addObject(addFriend)
println("added")
}
PFUser.currentUser().saveInBackgroundWithBlock{
(succeeded: Bool!, error: NSError!) in
if error != nil {
println("Error")
}
else {
println("saved")
}
}
}
}
}
I want to retrieve current user's friend list to show it in tableview but the tableview won't update to show current user's friend list, It's empty and there's no user list in tableview at all. I've tried to fix it and check if method get any object.
The problem is when I use println(usernames) ,It prints "nil" which the method doesn't get any object at all. First I use username as NSArray then I changed it into NSMutableArray and it doesn't have append method like NSArray did so I did a research and add the "add object" line of code in it and change a few things. Right now I'm not sure at all what's wrong with my code and I've been stuck at this for a week now, If my code's wrong somehow can you please guide me where? or fix it would be great. Any help is appreciated
here's the screenshot of my User's class table in parse
https://www.dropbox.com/s/6xp48v3yn0l2hje/Screen%20Shot%202015-06-03%20at%202.10.13%20PM.png?dl=0
here's the screenshot of my current user's friend in parse which is saveāļ with PFRelation method as seen above
https://www.dropbox.com/s/pd8mt8sf35u1m0v/Screen%20Shot%202015-06-03%20at%202.10.55%20PM.png?dl=0
Thanks in advance!!
I am trying to create a voting system and store it to my backend and have it come up for each individual picture and be stored for each picture. I created a column in my backend Parse called "count" but i cant seem to get the votes to be brought up or saved in the back and added. I am using a swipeGestureRecognizer to initiate the voting right for one up left for one down but i cant get the correct syntax at the switch statement for the -= and += and i get the error of Binary operator '+='/'-=' cannot be applied to operands of type '[(Int)]' and 'Int'what can i do to make the voting system work to both save in the backend and be brought up and shown each individual pics votes?
import UIKit
import Parse
class HomePage: UITableViewController {
var images = [UIImage]()
var titles = [String]()
var imageFile = [PFFile]()
var count = [Int]()
override func viewDidLoad() {
super.viewDidLoad()
println(PFUser.currentUser())
var query = PFQuery(className:"Post")
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock {(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
println("Successfully retrieved \(objects!.count) scores.")
println(objects!)
for object in objects! {
if let title = object["Title"] as? String {
self.titles.append(title)
}
if let imgFile = object["imageFile"] as? PFFile {
self.imageFile.append(imgFile)
}
if let voteCounter = object["count"] as? Int {
self.count.append(voteCounter)
}
self.tableView.reloadData()
}
} else {
// Log details of the failure
println(error)
}
}
}
/* println("Successfully retrieved \(objects!.count) scores.")
for object in objects! {
self.titles.append(object["Title"] as! String)
self.imageFile.append(object["imageFile"] as! PFFile)
self.tableView.reloadData()
}*/
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles.count
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 500
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var myCell:cell = self.tableView.dequeueReusableCellWithIdentifier("myCell") as! cell
myCell.rank.text = "21"
myCell.votes.text = "\(count)"
myCell.postDescription.text = titles[indexPath.row]
imageFile[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
myCell.postedImage.image = downloadedImage
}
}
var swipeRight = UISwipeGestureRecognizer(target: self, action: "respondToSwipeGesture:")
swipeRight.direction = UISwipeGestureRecognizerDirection.Right
myCell.postedImage.userInteractionEnabled = true;
myCell.postedImage.addGestureRecognizer(swipeRight)
var swipeLeft = UISwipeGestureRecognizer(target: self, action: "respondToSwipeGesture:")
swipeRight.direction = UISwipeGestureRecognizerDirection.Left
myCell.postedImage.userInteractionEnabled = true;
myCell.postedImage.addGestureRecognizer(swipeLeft)
return myCell
}
func respondToSwipeGesture(gesture: UIGestureRecognizer) {
if let swipeGesture = gesture as? UISwipeGestureRecognizer {
switch swipeGesture.direction {
case UISwipeGestureRecognizerDirection.Right:
count += 1
println("Swiped right")
case UISwipeGestureRecognizerDirection.Left:
count -= 1
println("Swiped Left")
default:
break
}
}
}
}
this is what i have now but the voting still wont get logged into parse and the post.count += 1 and post.count-=1 is receiving error messages of 'PFObject' does not have a member named 'count' where am i going wrong?
First of all, what you display on screen should depend entirely on your Parse model. What I mean is - you increment a count property every time a user votes. What will happen if the user stays in this screen for an hour and by that time 10 more users also vote? This won't be updated in the current screen and the user will see votes that are not up to date.
So what you can do is to create an object which inherits from PFObject. This object will be tied to Parse and will be always up to date.
What you can start with is reading the documentation. This can help you too.
So the main idea is to have your Parse columns as properties of a PFObject subclass:
class Post: PFObject, PFSubclassing {
#NSManaged var count: Int
class func parseClassName() -> String! {
return "Post"
}
}
In your AppDelegate's application(_:didFinishLaunchingWithOptions:) method register your Parse class like this:
Post.registerSubclass()
When you want to change the count property you'll have to set it and then update the screen:
let post = PFObject(className: "Post")
//increment like this
post.count += 1
//decrement like this
post.count -= 1
//refresh screen
//call reloadData()