Back on Navigation Controller causes Collection View items to duplicate - ios

I think this is probably a simple fix, but I am struggling to find the right solution
I have the following:
ContactList - a UICollectionView that loads the content with viewDidAppear: method. When a item is selected in the collection view I fire this method:
[self performSegueWithIdentifier:#"pushContactDetail" sender:self];
So far so good. Everything works great. However when I navigate back from the ContactDetail page using the Navigation Controller button my ContactList duplicates the content.
Is there something I should be doing to prevent the viewDidAppear: from running again?
I don't think I want to set the collection to nil when I push the ContactDetail page as that would cause the content to be reloaded each time...
Here is the code:
-(void) viewDidAppear
{
[super viewDidAppear];
[self.view setBackgroundColor:myColorLightGrey];
_contactList = [[NSMutableArray alloc] init];
[self displayLoadingAndDisableTableViewInteractions];
[self queryData];
}
- (void) queryData
{
//Find the Data
PFQuery *query = [PFUser query];
PFUser *consumer = [PFUser currentUser];
[query includeKey:User_followers];
[query whereKey:#"email" equalTo:consumer.email];
query.maxCacheAge = 60 * 60 * 24; // One day, in seconds
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
for (PFUser *tmpConsumer in objects)
{
for (PFUser *publisher in [tmpConsumer objectForKey:User_followers])
{
[_contactList addObject:publisher];
}
}
[_collectionView reloadData];
[self hideLoadingAndEnableTableViewInteractions];
}];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"contactCell";
ContactCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
//SNIPPED REMAINDER OF CODE
...
}

In your queryData method, nil out the array first
_contactList = nil;
_contactList = [NSMutableArray array]; // or [[NSMutableArray alloc] init];
Move the alloc/init method for _contactList like this:
- (void) queryData
{
_contactList = nil;
_contactList = [[NSMutablArray alloc] init];
//Find the Data
PFQuery *query = [PFUser query];
PFUser *consumer = [PFUser currentUser];
[query includeKey:User_followers];
[query whereKey:#"email" equalTo:consumer.email];
query.maxCacheAge = 60 * 60 * 24; // One day, in seconds
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
for (PFUser *tmpConsumer in objects)
{
for (PFUser *publisher in [tmpConsumer objectForKey:User_followers])
{
[_contactList addObject:publisher];
}
}
[_collectionView reloadData];
[self hideLoadingAndEnableTableViewInteractions];
}];
}

Navigate back with Animation true. It will solve your problem.

Related

UITableViewCell button doesn't show selected or deselected state until scroll

I am working on application with a "like" button in its uitableviewcell, however the state of the cell doesnt show unless the uitableview is scrolled and the cell is off the screen and reappeared. Here is how I am attempting to display the button:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"ProfileCell";
PFObject *data = self.posts[indexPath.row];
NSLog(#"Data: %#", data);
ProfileTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[ProfileTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier];
}
for(UIView *view in cell.contentView.subviews){
if ([view isKindOfClass:[UIView class]]) {
[view removeFromSuperview];
}
}
#pragma mark - Heart Button
heartButton = [[UIButton alloc] init];
[heartButton setImage:[UIImage imageNamed:#"heartButton"]
forState:UIControlStateNormal];
[heartButton setImage:[UIImage imageNamed:#"heartButtonSelected"]
forState:UIControlStateSelected];
dispatch_async(dispatch_get_main_queue(), ^ {
PFQuery *query = [PFQuery queryWithClassName:#"OrganizationLike"];
[query whereKey:#"Post" equalTo:data];
[query whereKey:#"liker" equalTo:[PFUser currentUser]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject * _Nullable object, NSError * _Nullable error) {
if (!object) {
[heartButton setSelected:NO];
}else{
[heartButton setSelected:YES];
}
}];
});
[heartButton addTarget:self action:#selector(heartButton:) forControlEvents:UIControlEventTouchDown];
[heartButton setTranslatesAutoresizingMaskIntoConstraints:NO];
[cell.contentView addSubview:heartButton];
[heartButton autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:80];
[heartButton autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:7.0];
[heartButton autoSetDimension:ALDimensionHeight toSize:15];
[heartButton autoSetDimension:ALDimensionWidth toSize:16];
PFQuery *lquery = [PFQuery queryWithClassName:#"OrganizationLike"];
[lquery whereKey:#"Post" equalTo:data];
[lquery findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (objects.count>0) {
UILabel *likeLabel = [[UILabel alloc] init];
[likeLabel setNeedsDisplay];
[likeLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
likeLabel.backgroundColor = [UIColor clearColor];
likeLabel.textColor = [UIColor colorWithRed:0.643 green:0.655 blue:0.667 alpha:1];
likeLabel.font = [UIFont fontWithName:#"OpenSans-Light" size:8.881];
likeLabel.textAlignment = NSTextAlignmentCenter;
likeLabel.text = [NSString stringWithFormat:#"%d", objects.count];
[likeLabel setNeedsDisplay];
[cell.contentView addSubview:likeLabel];
[likeLabel autoAlignAxis:ALAxisHorizontal toSameAxisOfView:heartButton withOffset:0];
[likeLabel autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:heartButton withOffset:3];
}
}];
return cell;
}
Here is how a button tap is handled:
- (void)heartButton:(UIButton *)sender {
sender.selected = !sender.selected;
if (sender.selected) {
NSIndexPath *i=[self indexPathForCellContainingView:sender.superview];
PFObject *data = self.posts[i.row];
PFQuery *query = [PFQuery queryWithClassName:#"OrganizationLike"];
[query whereKey:#"Post" equalTo:data];
[query whereKey:#"liker" equalTo:[PFUser currentUser]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject * _Nullable object, NSError * _Nullable error) {
if (!object) {
PFObject *like = [PFObject objectWithClassName:#"OrganizationLike"];
like[#"liker"] = [PFUser currentUser];
like[#"Post"] = data;
[like saveInBackground];
[self.tableView reloadData];
}
}];
} else {
NSIndexPath *i=[self indexPathForCellContainingView:sender.superview];
PFObject *data = self.posts[i.row];
PFQuery *query = [PFQuery queryWithClassName:#"OrganizationLike"];
[query whereKey:#"Post" equalTo:data];
[query whereKey:#"liker" equalTo:[PFUser currentUser]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject * _Nullable object, NSError * _Nullable error) {
[object deleteInBackground];
[self.tableView reloadData];
}];
}
}
reload the table view after the selection or update layout display
First of all try by calling [self.tableView reloadData] in main thread.
Second check if [self.tableView reloadData] gets called. You can check by adding breakpoints.
Rather than reloading the whole tableView cell, you can reload tableView at specific indexpath by calling
[self.tableView reloadRowsAtIndexPaths:indexPath withRowAnimation:nil];
check this this might work,
in - (void)heartButton:(UIButton *)sender
get updated object first and replace it in your main array object
[self.posts replaceObjectAtIndex:index withObject:newObj];
Than use
[self.tableView reloadRowsAtIndexPaths:indexPath withRowAnimation:nil];
And more suggestion there are no need to remove all object first from cell view and than create new in cellForRowAtIndexPath: method instead just check if view object is already exist than just update its value/property

Get an array from Parse query [duplicate]

This question already has an answer here:
Pass data from Parse tableview to WatchKit
(1 answer)
Closed 8 years ago.
I'm using Parse to create this table view, and am trying to figure out how to get the Parse table data into an array, so I can pass it into the WatchKit InterfaceController to show the exact same thing?
So I want to show in the WatchKit interface exactly what shows in the iPhone interface.
Here is what I have, let me know if I can add anything that would be helpful:
TableVC.m:
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if (self) {
self.parseClassName = #"na";
self.textKey = #"dateTime";
self.pullToRefreshEnabled = YES;
self.paginationEnabled = NO;
}
return self;
}
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
return query;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *simpleTableIdentifier = #"RecipeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
UILabel *homeLabel = (UILabel*) [cell viewWithTag:101];
homeLabel.text = [object objectForKey:#"test"];
UILabel *dateLabel = (UILabel*) [cell viewWithTag:102];
dateLabel.text = [object objectForKey:#"dateTime"];
return cell;
}
Parse data:
TableVC.m:
I already have the basic WatchKit files and Storyboard set up. I hard coded an array to test that it was generally working. But now I just need to get the data from Parse into there, and not sure if I need to do a query and then turn that into a public array?
EDIT:
Here is my query:
PFQuery *query2 = [PFQuery queryWithClassName:#"nba"];
[query2 findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Objects 2: %#", objects);
}
} else {
// Log details of the failure
NSLog(#"Error 2: %# %#", error, [error userInfo]);
}
}];
Here is my NSLog:
NSLog(#"Objects 2: %#", objects);
Console:
2015-02-09 21:06:30.845 SimpleTable[8373:1284663] Objects 2: (
"<na: 0x7ff3f8e40880, objectId: cOrjeAmwJh, localId: (null)> {\n away = Cav;\n date = \"04/19/2015\";\n dateTime = \"April 19, 2015, 16:00\";\n gNumber = 1;\n home = Bul;\n matup = \"Ca\";\n ro = \"Ro \";\n test = \"Test 2\";\n tv = T;\n}",
If you need the array, fetch it asynchronously in a method outside of the queryForTable method, get it like this:
PFQuery *query = [self queryForTable];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// objects is the array for this table
NSMutableArray *array = [#[] mutableCopy];
for (PFObject *object in objects) {
NSLog(#"we got an object with dateTime = %#", [object objectForKey:#"dateTime"]);
[array addObject:[object objectForKey:#"dateTime"]];
// you can prove this with any of your keys: away, number, home, mat up, etc.
}
}
}];
if you just want to pass the objects into an array you can do so like this or a variation of:
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
self.someArray = [query findObjects];
return query;
}
REFERNECE

UitableViewCell not properly displaying accessories

I have a friendslist view controller that allows the user to see pending friend requests. The isPending method is first used to see if a particular user has a pending friend request with the current user.
-(BOOL)isPending:(PFUser *)user
{
for (PFUser *pending in self.Pending){
if ([pending.objectId isEqualToString:user.objectId]){
return YES;
}
}
return NO;
}
This method is called within cellForRowAtIndexPath method inside the TableViewController. THe problem that I am experiencing is that If I run the query inside the tableViewController users who are pending are properly displayed. When I moved the query to a singleton datasource class, I have setup a delegate method that is called when data is returned from the query.
-(void)pendingFriendsQuarryDidFinishWithData{
self.Pending = [NSMutableArray arrayWithArray:dataHolder.getPendingFriendsList];
[self.tableView reloadData];
}
Calling reloadData does not cause the checkmarks to appear next to the users names.
EDIT: Here is the code for cellForRowAtIndexPath. It does not change with the singleton.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// Configure the cell...
PFUser *user = [self.allUsers objectAtIndex:indexPath.row];
cell.textLabel.text = user.username;
cell.detailTextLabel.text = user.email;
cell.imageView.image = [UIImage imageNamed:#"Treehouse.png"];
if([self isPending:user]){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
return cell;
}
What does change is how the data is obtained. Without the singleton here is the code
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
/*
self.currentUser = [PFUser currentUser];
if (self.currentUser != nil){
PFQuery *userQuery = [PFUser query];
PFQuery *pendingUser = [PFUser query];
PFRelation *friendRelation = [self.currentUser relationForKey:#"friendRelation"];
PFQuery *existingFriends = [friendRelation query];
PFQuery *pendingFriends = [PFQuery queryWithClassName:#"FriendRequest"];
userQuery.cachePolicy = kPFCachePolicyCacheThenNetwork;
pendingFriends.cachePolicy = kPFCachePolicyCacheThenNetwork;
[pendingFriends whereKey:#"fromUser" equalTo:self.currentUser.objectId];
[pendingFriends whereKey:#"status" equalTo:#"Pending"];
[userQuery whereKey:#"objectId"doesNotMatchKey:#"toUser" inQuery:pendingFriends];
[userQuery whereKey:#"objectId" doesNotMatchKey:#"objectId" inQuery:existingFriends];
[userQuery orderByAscending:#"username"];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error){
NSLog(#" Error %# %#", error, [error userInfo] );
}else{
NSLog(#"All Users Array Starts Here: %#", objects);
self.allUsers = objects;
[self.tableView reloadData];
}
}];
[pendingUser whereKey:#"objectId" matchesKey:#"toUser" inQuery:pendingFriends];
[pendingUser orderByAscending:#"username"];
[pendingUser findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error){
NSLog(#"%# %#", error, [error userInfo]);
}else{
[self.Pending addObjectsFromArray:objects];
NSLog(#"Pending Friends Array Stars here: %#", self.Pending);
}
}];
}
*/
}
With singleton
- (void)viewDidLoad
{
dataHolder = [DataHolder sharedInstance];
dataHolder.delegate = self;
self.friends = [NSMutableArray new];
self.allUsers = [NSMutableArray new];
self.allUsers = [NSMutableArray arrayWithArray:dataHolder.getAllUsersWithQuarry];
self.Pending = [NSMutableArray new];
self.Pending = [NSMutableArray arrayWithArray:dataHolder.getPendingFriendsListWithQuarry];
[super viewDidLoad];
NSLog(#"Now in Connection Editor");
}
-(void)allUsersQuarryDidFinishWithData{
self.allUsers = [NSMutableArray arrayWithArray:dataHolder.getAllUsers];
[self.tableView reloadData];
}
-(void)pendingFriendsQuarryDidFinishWithData{
self.Pending = [NSMutableArray arrayWithArray:dataHolder.getPendingFriendsList];
[self.tableView reloadData];
}
Silly question, did you set the datasource of your tableviewcontroller to point to your singleton datasource?
tableview.datasource = [your singleton class instance]
and make sure that you step into it in the debugger.
Hope this helps.

tableView cellForRowAtIndexPath: runs before ViewDidLoad?

I am trying to display some arrays in a UITableViewController. I have a custom cell with a NIB. The problem I'm running into is my table rows try to populate before my arrays are loaded in my ViewDidLoad . Setting breakpoints show that my table rows are being created and then directly after my arrays are given values (which are set in my ViewDidLoad.
Can anyone tell me why the tableView method is running before the ViewDidLoad? What do I need to do to ensure my arrays are loaded before the table rows are populated?
Here is my ViewDidLoad :
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"go");
PFQuery *getCartQuery = [PFQuery queryWithClassName:#"chwCart"];
[getCartQuery whereKey:#"userId" equalTo:[PFUser currentUser].objectId];
[getCartQuery whereKey:#"status" equalTo:#"open"];
[getCartQuery getFirstObjectInBackgroundWithBlock:^(PFObject *currentCartObject, NSError *error) {
// NSLog([currentCartObject objectId]);
currentCart = [currentCartObject objectForKey:#"cartContents"];
dictFoodId = [currentCart valueForKey:#"foodId"];
dictQty = [currentCart valueForKey:#"qty"];
}];
NSLog(#"Should be loaded");
[self.tableView registerNib:[UINib nibWithNibName:#"foodCell" bundle:nil] forCellReuseIdentifier:#"foodCell"];
}
Here is my tableView cellForRowAtIndexPath:
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"foodCell";
foodCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[foodCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.foodTitle.text = [dictQty objectAtIndex:indexPath.row];
return cell;
}
I think it's because you are updating the array into block which is running into background and your delegates called, just simply reload your tableView into block completion
[getCartQuery getFirstObjectInBackgroundWithBlock:^(PFObject *currentCartObject, NSError *error) {
// NSLog([currentCartObject objectId]);
currentCart = [currentCartObject objectForKey:#"cartContents"];
dictFoodId = [currentCart valueForKey:#"foodId"];
dictQty = [currentCart valueForKey:#"qty"];
[self.tableView reloadData];
}];
You are using blocks which does the process in background so your tableview is created and the delegate methods are called first you just add this line of code to your viewdidload code which will again call tableview after arrays are loaded
**[self.tableView reloadData];**
so your code in viewdidLoad should look like this
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"go");
PFQuery *getCartQuery = [PFQuery queryWithClassName:#"chwCart"];
[getCartQuery whereKey:#"userId" equalTo:[PFUser currentUser].objectId];
[getCartQuery whereKey:#"status" equalTo:#"open"];
[getCartQuery getFirstObjectInBackgroundWithBlock:^(PFObject *currentCartObject, NSError *error) {
// NSLog([currentCartObject objectId]);
currentCart = [currentCartObject objectForKey:#"cartContents"];
dictFoodId = [currentCart valueForKey:#"foodId"];
dictQty = [currentCart valueForKey:#"qty"];
[self.tableView reloadData];
}];
NSLog(#"Should be loaded");
[self.tableView registerNib:[UINib nibWithNibName:#"foodCell" bundle:nil] forCellReuseIdentifier:#"foodCell"];
}

How to query the user's post?

I currently have my system set up through parse.com
What Im trying to do is to be able to in a tableView or a PFQuerTableViewController be able to query the post which is a PFObject that my user has posted. Currently I have this code below and its only saying "Loading.." instead of displaying their postings. Why is it not displaying my user's last posts?
This is the query in which Im trying to fetch the users post:
- (PFQuery *)queryForTable {
PFQuery *postQuery = [PFQuery queryWithClassName:#"New"];
[postQuery whereKey:#"author" equalTo:[PFUser currentUser]];
// Run the query
[postQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
//Save results and update the table
postArray = objects;
[self.tableView reloadData];
}
}];
}
And here is its cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
// If you want a custom cell, create a new subclass of PFTableViewCell, set the cell identifier in IB, then change this string to match
// You can access any IBOutlets declared in the .h file from here and set the values accordingly
// "Cell" is the default cell identifier in a new Master-Detail Project
static NSString *CellIdentifier = #"Cell";
PFTableViewCell *cell = (PFTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell
//cell.textLabel.text = [object objectForKey:self.textKey];
//cell.imageView.file = [object objectForKey:self.imageKey];
PFObject *post = [postArray objectAtIndex:indexPath.row];
[cell.textLabel setText:[post objectForKey:#"title"]];
return cell;
}
Also here is the code for how I am posting the PFObject:
-(IBAction)done:(id)sender {
PFUser *user = [PFUser currentUser];
PFObject *quoteNew = [PFObject objectWithClassName:#"New"];
[quoteNew setObject:user forKey:#"user"];
[quoteNew setObject:[[self quoteText] text] forKey:#"quoteText"];
[quoteNew setObject:[[self attributionTitle] text] forKey:#"title"];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeIndeterminate;
hud.labelText = #"Uploading";
[hud show:YES];
[quoteNew saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
//[self done:self];
[hud hide:YES];
[self dismissViewControllerAnimated:YES completion:nil];
} else {
[hud hide:YES];
}
}];
You're trying to run the query in the queryForTable method. That's not what it's for. In queryForTable, you're supposed to build the query with the constraints that you want (like the author thing that you added) then return the query. So for your case:
- (PFQuery *)queryForTable {
PFQuery *postQuery = [PFQuery queryWithClassName:#"New"];
[postQuery whereKey:#"author" equalTo:[PFUser currentUser]];
// Don't run the query, return it so PFQueryTableViewcontroller can use it
return postQuery;
}
If you want to programmatically tell your PFQueryTableViewController to load new posts, you can run [self loadObjects].
Note: This only applies if you are subclassing PFQueryTableViewController
I'd recommend reading the documentation, it lays everything out fairly well here.
You are setting the key as user
[quoteNew setObject:user forKey:#"user"];
Then querying for author
[postQuery whereKey:#"author" equalTo:[PFUser currentUser]];
Try instead
[postQuery whereKey:#"user" equalTo:[PFUser currentUser]];

Resources