0

I know this is probably a simple answer for anybody who is skilled at Objective-C, but I'm new to using Objective-C and I've been stuck on this problem for the past few days. I've read a lot of stackoverflow threads on repeating cells and tried copying and pasting a lot of the answers but none seem to work for me.

I'm trying to put the data that I'm getting from Facebook SDK for iOS into a UITableView. I've managed to get the data from the Facebook GraphAPI and into the UITableView, but the problem I can't get past is that the data from the last record that the app fetches from Facebook keeps repeating in all the cells. In addition to that, when I scroll up or down, it takes a few seconds to populate the cell even though it already knows the data.

I've tried so many ways to get this to work, but solving this problem is beyond skill level. At this point I'll try anything to get this working properly and displaying the results I get from Facebook without causing the current problem, so any help will be very appreciated.

My NSLogs show two records but my UITableView will only show one. Here's an example of my logs.

The follow data is from the UITableViewCell method in FBLeaderBoardTableViewController.m
Full Name: John Smith 
High Score: 5666 
Facebook ID: 654321 
Image: <UIImage: 0xde12345>
IndexPath Row: 1

*************************************

2013-12-26 11:51:48.380 [7820:70b] CREATING NEW CELL
2013-12-26 11:51:48.380 [7820:70b] indexPath.row: 1
2013-12-26 11:51:48.468 [7820:70b] 

The follow data is from the UITableViewCell method in FBLeaderBoardTableViewController.m
Full Name: Figurative Art 
High Score: 2762 
Facebook ID: 123456 
Image: <UIImage: 0x1101234>
IndexPath Row: 1

*************************************

2013-12-26 11:51:48.468 [7820:70b] CREATING NEW CELL
2013-12-26 11:51:48.469 [7820:70b] indexPath.row: 1
2013-12-26 11:51:48.553 [7820:70b] 

The follow data is from the UITableViewCell method in FBLeaderBoardTableViewController.m
Full Name: John Smith 
High Score: 5666 
Facebook ID: 654321 
Image: <UIImage: 0xde12345>
IndexPath Row: 0

*************************************

2013-12-26 11:51:48.553 [7820:70b] CREATING NEW CELL
2013-12-26 11:51:48.553 [7820:70b] indexPath.row: 0
2013-12-26 11:51:48.637 [7820:70b] 

The follow data is from the UITableViewCell method in FBLeaderBoardTableViewController.m
Full Name: Figurative Art 
High Score: 2762 
Facebook ID: 123456 
Image: <UIImage: 0x1101234>
IndexPath Row: 0

*************************************

2013-12-26 11:51:48.637 [7820:70b] CREATING NEW CELL
2013-12-26 11:51:48.637 [7820:70b] indexPath.row: 0
2013-12-26 11:51:48.720 [7820:70b] 

The follow data is from the UITableViewCell method in FBLeaderBoardTableViewController.m
Full Name: John Smith 
High Score: 5666 
Facebook ID: 654321 
Image: <UIImage: 0xde12345>
IndexPath Row: 2

*************************************

2013-12-26 11:51:48.720 [7820:70b] CREATING NEW CELL
2013-12-26 11:51:48.720 [7820:70b] indexPath.row: 2
2013-12-26 11:51:49.290 [7820:70b] 

The follow data is from the UITableViewCell method in FBLeaderBoardTableViewController.m
Full Name: Figurative Art 
High Score: 2762 
Facebook ID: 123456 
Image: <UIImage: 0x1101234>
IndexPath Row: 2

*************************************

Here is the code from my LeadeboardViewController.m

#import "FBLeaderBoardTableViewController.h"
#import "FBLeaderBoardCell.h"
#import "ViewController.h"


@interface FBLeaderBoardTableViewController ()

@end

@implementation FBLeaderBoardTableViewController

@synthesize facebookID, facebookFullName, facebookFirstName, facebookHighScore, facebookProfilePicture, tableView, closeFacebookLeaderboard;
@synthesize FBArray;
@synthesize FBMutableArray;



- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self.tableView registerNib:[UINib nibWithNibName:@"FBLeaderBoardCell" bundle:nil ] forCellReuseIdentifier:@"FBLeaderBoardCellID"];

}


- (void)viewWillAppear:(BOOL)animated {

    [super viewWillAppear:animated];


}

- (IBAction)closeFacebookLeaderboard:(id)sender {

    [self dismissViewControllerAnimated:TRUE completion:nil];

}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{

//    [self.tableView reloadData];
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#warning Potentially incomplete method implementation.
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#warning Incomplete method implementation.
    // Return the number of rows in the section.
    return 10;

    //RESULTS THAT DIDN'T WORK
    //return [displayData count];
    //return [self.data count];
    //return [FBLeaderBoardData count];
    //return [facebookHighScore count];
    //return [data count];

}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    static NSString *CellIdentifier = @"fbcell";
    FBLeaderBoardCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil)
    {

        cell = [[[FBLeaderBoardCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]autorelease];
        {
            NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"FBLeaderBoardCell" owner:nil options:nil];
            for (id currentObject in topLevelObjects){
                if([currentObject isKindOfClass:[FBLeaderBoardCell class]]){
                    cell = (FBLeaderBoardCell *)currentObject;

                }
            }

        }            [FBRequestConnection startWithGraphPath:[NSString stringWithFormat:@"xxxx/scores?fields=score,user"] parameters:nil HTTPMethod:@"GET" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {

            if (result && !error)
            {
                // int index = 0;
                for (NSDictionary *dict in [result objectForKey:@"data"])
                {
                    NSString *name = [[dict objectForKey:@"user"] objectForKey:@"name"];
                    NSString *highScore = [dict objectForKey:@"score"];
                    NSString *FBID = [[dict objectForKey:@"user"] objectForKey:@"id"];
                    NSString *imageURL = [[NSString alloc] initWithFormat:@"https://graph.facebook.com/%@/picture",FBID];
                    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]]];
                    [imageURL release];
                    self.facebookFullName = name;
                    self.facebookHighScore = highScore;
                    self.facebookProfilePicture = image;

                    NSLog(@"\n\nThe follow data is from the UITableViewCell method in FBLeaderBoardTableViewController.m\nFull Name: %@ \nHigh Score: %@ \nFacebook ID: %@ \nImage: %@\nIndexPath Row: %ld\n\n*************************************\n\n", facebookFullName, facebookHighScore, FBID, facebookProfilePicture, (long)indexPath.row);
                    //                        NSLog(@"FBGraph Index Row: %d", indexPath.row);
                    NSLog(@"CREATING NEW CELL");
                    NSLog(@"indexPath.row: %d", indexPath.row);

                    cell.FBPlayerPhoto.image = facebookProfilePicture;
                    cell.FBPlayerName.text = facebookFullName;
                    cell.FBPlayerScore.text = [NSString stringWithFormat:@"$%@ Gold Coins",facebookHighScore];

                }
            }

        }];

//                [tableView reloadData];


    }

    return cell;
}



// Removes iOS 7 Staus bar from this view.
- (BOOL)prefersStatusBarHidden{
    return YES;
}

@end

Here's the code from my LeadeboardViewController.h

#import <UIKit/UIKit.h>
#import "FBLeaderBoardCell.h"
#import <FacebookSDK/FacebookSDK.h>

@interface FBLeaderBoardTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
    NSMutableArray *FBLeaderBoardData;
    IBOutlet UILabel *showScore;
    __block NSMutableArray* _itemsNamesFriend;
    __block NSArray* data;
    __block NSMutableArray* displayData;
//    IBOutlet FBProfilePictureView *facebookProfilePicture;
    IBOutlet UITableView *tableView;
    IBOutlet UIButton *closeFacebookLeaderboard;

}


@property (strong, nonatomic) NSArray *data;
@property (strong) NSString *facebookID;
@property (strong) NSString *facebookFullName;
@property (strong) NSString *facebookFirstName;
@property (strong) NSString *facebookHighScore;
@property (strong) UIImage *facebookProfilePicture;
//@property (strong) IBOutlet FBProfilePictureView *facebookProfilePicture;
@property (strong) IBOutlet UITableView *tableView;
@property (strong) IBOutlet UIButton *closeFacebookLeaderboard;
@property (strong, nonatomic) NSArray *FBArray;
@property (strong, nonatomic) NSMutableArray *FBMutableArray;

@end

I know the way I currently have the FBRequest in my - (UITableViewCell *) may not be the best way to call the data but when I tried to put it into the viewDidLoad method, I was getting NULL in my - (UITableViewCell *). Figuring out how to do it as an NSArray or NSMutableArray kept causing me problems so I just put everything into the - (UITableViewCell *).

Storm Factory
  • 388
  • 2
  • 10
  • Could you please post the code from your cellForRowAtIndexPath: method? – Lyndsey Scott Dec 26 '13 at 20:50
  • Could you also post the JSON format of what Facebook API return (give sample and not the actual data.. i.e., replace original facebookID with random numbers..) – chuthan20 Dec 26 '13 at 20:52
  • OK, never mind… I see that you posted that method after all (or just posted it?). But I'm already seeing some issues… Looking through everything now... – Lyndsey Scott Dec 26 '13 at 20:57

1 Answers1

1

The reason your code is repeating like that is because of this for loop in your cellForRowAtIndexPath method:

for (NSDictionary *dict in [result objectForKey:@"data"]) {

    NSString *name = [[dict objectForKey:@"user"] objectForKey:@"name"];
    NSString *highScore = [dict objectForKey:@"score"];
    NSString *FBID = [[dict objectForKey:@"user"] objectForKey:@"id"];
    NSString *imageURL = [[NSString alloc] initWithFormat:@"https://graph.facebook.com/%@/picture",FBID];
    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]]];
    [imageURL release];
    self.facebookFullName = name;
    self.facebookHighScore = highScore;
    self.facebookProfilePicture = image;
    cell.FBPlayerPhoto.image = facebookProfilePicture;
    cell.FBPlayerName.text = facebookFullName;
    cell.FBPlayerScore.text = [NSString stringWithFormat:@"$%@ Gold Coins",facebookHighScore];

}

You're going through the dictionary while loading each individual row, so you're ending up setting your cell with the results generated during the final iteration.

So replace the loop with the single dictionary value correlated with the current particular row. For example, you can just set dict = [[result objectForKey:@"data"] objectAtIndex:indexPath.row] and remove the for loop like so:

NSDictionary *dict = [[result objectForKey:@"data"] objectAtIndex:indexPath.row];

NSString *name = [[dict objectForKey:@"user"] objectForKey:@"name"];
NSString *highScore = [dict objectForKey:@"score"];
NSString *FBID = [[dict objectForKey:@"user"] objectForKey:@"id"];
NSString *imageURL = [[NSString alloc] initWithFormat:@"https://graph.facebook.com/%@/picture",FBID];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]]];
[imageURL release];
self.facebookFullName = name;
self.facebookHighScore = highScore;
self.facebookProfilePicture = image;
cell.FBPlayerPhoto.image = facebookProfilePicture;
cell.FBPlayerName.text = facebookFullName;
cell.FBPlayerScore.text = [NSString stringWithFormat:@"$%@ Gold Coins",facebookHighScore];

Edit: And a few more things, while we're at it…

(1) You're making a similar error here:

NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"FBLeaderBoardCell" owner:nil options:nil];
for (id currentObject in topLevelObjects){
    if([currentObject isKindOfClass:[FBLeaderBoardCell class]]){
        cell = (FBLeaderBoardCell *)currentObject;

    }
}

by repopulating the cell object during each iteration of the loop. That's unnecessary. Follow the method mentioned here to achieve loading a custom cell with a xib properly: https://stackoverflow.com/a/1939305/2274694

(2) You may actually want to fetch the graph path results in a different method in your code for 2 reasons: #1 It doesn't make sense to have your app call the Facebook graph path while loading every single row. And more importantly in your case, #2 in order to dynamically find out the number of rows during numberOfRowsInSection: you'll need to pre-fetch the results anyway.

So I suggest fetching the graph path results before each time the table loads, inserting a [tableView reloadData] statement within the block, keeping the results as a class variable, and setting the number of rows to results.count.

Community
  • 1
  • 1
Lyndsey Scott
  • 37,080
  • 10
  • 92
  • 128
  • I really appreciate your suggestions. I would love to fetch the graph path results in a different method in my code and not have to call up the Facebook Graph for every cell because each time I scroll up or down, the cells have to reload the images, names, and scores. What method would you suggest? I had previously tried putting the FBRequest code in `viewDidLoad` and `viewWillAppear`. I was able to see the data in my logs but couldn't figure out how to get it into my table. I'm heading over to the link you provided. – Storm Factory Dec 27 '13 at 01:14
  • Yeah, you could put the code in your viewDidLoad or viewWillAppear method then reload the table within the FBRequest block in order to get the data into your table after the data has loaded asynchronously. – Lyndsey Scott Dec 27 '13 at 18:00
  • Hey :) In addition to reloading the table within the async FBRequest block, you should initialize a class variable before entering the block so that your table knows it should initially be empty since it should initially load before the async block is complete. Specifically, your numberOfRowsInSection: could always just return results.count, so when results = 0 and your block is still fetching the data, the table is initially empty; then once results = 10 (or whatever number) the table will reload with the necessary length/data. – Lyndsey Scott Dec 27 '13 at 19:09
  • Oh, and by async block, I mean the FBRequest block. It's an asynchronous block since it'll perform the fetch in the background so your program can continue running even though the block isn't complete. – Lyndsey Scott Dec 27 '13 at 19:12
  • You suggestion to change what I had to `dict = [[result objectForKey:@"data"] objectAtIndex:indexPath.row] ` worked for making my cells not repeat. As for your other suggestions, I just could not get them to work so I'll just settle for now and submit the app as is and hope I can get the viewDidLoad and Arrays working for my next update. – Storm Factory Jan 01 '14 at 00:11
  • OK, but if you ever want to send me your code I'll take a look. I'd open up a chat, but I have no idea how. :\ – Lyndsey Scott Jan 01 '14 at 03:18
  • That would be awesome. I'm new and only have 5 points at this stage so I definitely have no idea how to open a chat. :-) I'll try you on Twitter. – Storm Factory Jan 05 '14 at 20:51