I am trying to display Facebook JSON from a correctly authenticated account onto a TableView, however no information is displayed despite Json being logged. Here is how I am attempting to display the information onto the TableView:
- (void)fetchFacebookPosts {
[FBRequestConnection startWithGraphPath:#"me/feed" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
NSLog(#"%#",result);
facebookResponse = [result mutableCopy];
[self.facebookPosts addObjectsFromArray:result[#"data"]];
[self.tableView reloadData];
} else {
NSLog(#"Failure: %#", error);
}
}];
}
#pragma mark - Table view data source
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
FacebookCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"FacebookCell"];
cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"Background.png"]];
if (cell == nil) {
cell = [[FacebookCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"FacebookCell"];
}
NSDictionary *entry = facebookPosts[indexPath.row];
// Set Image
// Set User Name
NSString *user = entry[#"story"];
[cell.textLabel setText:user];
return cell;
}
In this code facebookResponse is a NSMutableDictionary
and facebookPosts is a NSMutableArray.
Related
I'm using UITableViewController for displaying data from Parse. It runs perfectly on my Xcode Simulator as i think there's no latency in network. But whenever i'm uploading the code to AppStore for Testing. The very first time i run the app it has to load a couple of restaurant's from Parse and display in UITableViewController. Upon clicking a row the first rows data is being loaded into the 3rd row and 4th row data loading in 6th row data irregularly. Why is the data being loaded very unevenly ? Here's my
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellIdentifier = #"restaurantIdentifier";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
PFObject *tempObject = [self.objectArray objectAtIndex:indexPath.row];
PFFile *imageFile = [tempObject objectForKey:#"RestaurantIcon"];
PFImageView *imageView = [[PFImageView alloc] init];
imageView.file = imageFile;
[imageView loadInBackground:^(UIImage *img,NSError *error){
if(!error){
cell.imageCell.image = imageView.image;
}
}];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.imageView.contentMode = UIViewContentModeScaleAspectFit;
cell.imageView.layer.masksToBounds = YES;
cell.imageView.layer.cornerRadius = 4;
cell.imageView.frame = self.view.bounds;
cell.cellLabel.text = [tempObject objectForKey:#"RestaurantName"];
[self.hotelNamesArray addObject:[tempObject objectForKey:#"RestaurantName"]];
cell.cellLabel.lineBreakMode = NSLineBreakByWordWrapping;
return cell;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
_restaurantName = [self.hotelNamesArray objectAtIndex:indexPath.row];
self.restaurantMenuNameArray = [[NSMutableArray alloc] init];
PFQuery *query = [PFQuery queryWithClassName:[self.hotelNamesArray objectAtIndex:indexPath.row]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *obj in objects) {
if (![_restaurantMenuNameArray containsObject:[obj objectForKey:#"RestaurantMenuName"]]) {
NSLog(#"restaurantmenunames are %#",[obj objectForKey:#"RestaurantMenuName"]);
if ([obj objectForKey:#"RestaurantMenuName"] ==nil) {
[self performSegueWithIdentifier:#"restaurantDetail" sender:self];
return;
}else {
[_restaurantMenuNameArray addObject: [obj objectForKey:#"RestaurantMenuName"]];
}
}
}
}else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
[self.tableView reloadData];
NSLog(#"restaurantMenuNames is %#",_restaurantMenuNameArray);
[self performSegueWithIdentifier:#"restaurantDetail" sender:self];
}];
}
Thanks in advance.
If you mean the images get in the wrong cell, you have to consider that cells are recycled when you scroll, and that if the image loading takes a bit too long, you may get the result after the cell has been reused.
You need to check that the cell is still for the item/row you want (you could store the row in the cell's tag and check it before setting the image in the completion handler, for instance).
If it's other data that is mixed up, then you'll need to show us the code that loads that data.
I'm using a storyboard and my custom UICollectionViewCell is not appearing. I played around with it for a few hours and have googled a ton of different solutions but none worked. Just to clarify, the data exists, and the UICollectionView is appearing, but the cell is not. Here is my code. Any suggestions would be appreciated!
- (NSInteger)collectionView:(UICollectionView *)mutualFriendsView numberOfItemsInSection:(NSInteger)section {
return [self.resultsDictionary count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)mutualFriendsView
cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"PCRRequesterMutualFriendsCollectionViewCell";
PCRRequesterMutualFriendsCollectionViewCell *cell = [self.mutualFriendsView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
NSArray *idArray = [self.resultsDictionary objectForKey:#"id"];
NSArray *nameArray = [self.resultsDictionary objectForKey:#"name"];
cell.profileName.text = [nameArray objectAtIndex:indexPath.row];
cell.backgroundColor = [UIColor redColor];
return cell;
}
- (void)collectionView:(UICollectionView *)mutualFriendsView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"cell #%d was selected", indexPath.row);
}
- (BOOL)shouldAutorotate {
[mutualFriendsView.collectionViewLayout invalidateLayout];
BOOL retVal = YES;
return retVal;
}
EDIT Here is my viewDidLoad
self.mutualFriendsView.dataSource = self;
self.mutualFriendsView.delegate = self;
self.mutualFriendsView.pagingEnabled = YES;
// [self.mutualFriendsView registerClass:[PCRMutualFriendsCollectionViewCell class] forCellWithReuseIdentifier:#"PCRMutualFriendsCollectionViewCell"];
Edit I think I figured out the problem. I don't think the dictionary is being populated after the completion block finishes. Any suggestions for saving the value of the dictionary from the block to be used outside of it?
__block NSMutableDictionary *mutualFriends = nil;
__block NSNumber *total;
NSString *u = [NSString stringWithFormat:#"%#",self.details[#"Requester"][#"profile"][#"facebookId"]];
/* make the API call */
[FBRequestConnection startWithGraphPath:(#"/%#", u)
parameters:params
HTTPMethod:#"GET"
completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
/* handle the result */
if (!error){
NSLog(#"RESULT OF FB %#", result);
if (result == nil){
NSLog(#"No shared friends");
} else {
total = result[#"context"][#"mutual_friends"][#"summary"][#"total_count"];
NSLog(#"TOTAL FRIENDS %#", total);
for(int i=0;i<[result[#"context"][#"mutual_friends"][#"data"] count];i++)
{
mutualFriends = result[#"context"][#"mutual_friends"][#"data"][i];
NSLog(#"FRIENDDATA %#", mutualFriends);
}
}
} else {
NSLog(#"ERROR %#", error);
}
}];
self.resultsDictionary = mutualFriends;
self.number = total;
NSLog(#"NUMBER %#", self.number);
NSLog(#"RESULTS DICTIONARY %#", self.resultsDictionary);
NSString *friends = [NSString stringWithFormat:#"You have %# friends in common including:", self.number];
After this code:
for(int i=0;i<[result[#"context"][#"mutual_friends"][#"data"] count];i++)
{
mutualFriends = result[#"context"][#"mutual_friends"][#"data"][i];
NSLog(#"FRIENDDATA %#", mutualFriends);
}
// add
self.resultsDictionary = mutualFriends;
mutualFriendsView.reloadData();
All within that completion block. So when FB finally does return and you've accumulated all the mutualFriends, then you tell the collectionView to reload.
I have a label in cell that I want to update with a data from the JSON array that I parse. The problem is the label is blank after getting the data and will only show after I scroll up and down the tableview. I've tried to force reload the view with [self.view setNeedsDisplay]; and it still doesn't work. How do I approach this?
Here is the snippets of the code with some comments in it:
#implementation outletView
#synthesize outletInfo;
- (void)viewDidLoad
{
[super viewDidLoad];
...
// Do any additional setup after loading the view.
[[AFHTTPRequestOperationManager manager] GET:[SERVER_URL stringByAppendingString:#"api/points"] parameters:[NSDictionary dictionaryWithObjectsAndKeys:[[NSUserDefaults standardUserDefaults] objectForKey:#"authToken"],#"auth_token",nil] success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *outletID = [[outletInfo valueForKeyPath:#"preference"] objectForKey:#"outlet_id"];
NSArray *outletArray = (NSArray*)responseObject;
NSLog(#"array: %#", outletArray);
for(NSDictionary *diction in outletArray) {
NSString *dictionID = [diction objectForKey:#"outlet_id"];
if ([dictionID isEqualToString:outletID]) {
pointOutlet = [diction objectForKey:#"total"];
}
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error : %#",[error description]);
}];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = indexPath.section==0 ? #"outletViewCell" : #"categoryCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Here is the cell
if(indexPath.section==0){
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[(UIImageView *)[cell viewWithTag:1] sd_setImageWithURL:[NSURL URLWithString:outletInfo[#"backgroundImageUrl"]] placeholderImage:nil];
[(UIImageView *)[cell viewWithTag:2] sd_setImageWithURL:[NSURL URLWithString:outletInfo[#"logoUrl"]] placeholderImage:nil];
[(UILabel *)[cell viewWithTag:3] setText:outletInfo[#"name"]];
[(UILabel *)[cell viewWithTag:4] setText:outletInfo[#"address"]];
//Here is the UILabel that I want to update
[(UILabel *)[cell viewWithTag:6] setText:pointOutlet];
} else {
...
}
return cell;
}
#end
Do following change:-
[[AFHTTPRequestOperationManager manager] GET:[SERVER_URL stringByAppendingString:#"api/points"] parameters:[NSDictionary dictionaryWithObjectsAndKeys:[[NSUserDefaults standardUserDefaults] objectForKey:#"authToken"],#"auth_token",nil] success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *outletID = [[outletInfo valueForKeyPath:#"preference"] objectForKey:#"outlet_id"];
NSArray *outletArray = (NSArray*)responseObject;
NSLog(#"array: %#", outletArray);
for(NSDictionary *diction in outletArray) {
NSString *dictionID = [diction objectForKey:#"outlet_id"];
if ([dictionID isEqualToString:outletID]) {
pointOutlet = [diction objectForKey:#"total"];
}
}
//Here you need to call tableView reload. This will reload your tableView and show the label.
[tableView reload];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error : %#",[error description]);
}];
Reload the table on the main thread when you parse the response.
dispatch_async(dispatch_get_main_queue(), ^{
});
Reload the table in this block.
This kind of problem appear when data not come and your tableView methods call before, reload your table view after fetching all data from server.
I trying to do an online search using searchBar, I doing next:
//-=-=-=-=Methods for search Bar=-=-=-=
-(void)searchThroughData {
self.result=nil;
//Send searched substrint ti server
if (self.searchBar.text.length > 0) {
NSString *searchParams = [NSString stringWithFormat:#"<request<user_name>%#</user_name></request>", self.searchBar.text];
[self initRequest:searchParams];
}
}
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
[self searchThroughData];
}
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *dataString = [[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding];
// -=-=-=-=-=-=-=-=-=-=Parse the XML into a dictionary-=-=-=-=-=-=-=-=-=-=
NSError *parseError = nil;
_xmlDictionary= [XMLReader dictionaryForXMLString:dataString error:&parseError];
NSDictionary * dict=[_xmlDictionary objectForKey:#"result"];
NSDictionary *dict1 = [dict valueForKey:#"user"];
_result = [[NSMutableArray alloc] initWithArray:[[dict1 valueForKey:#"name"] valueForKey:#"text"]];
NSLog(#"res: %#", _result);
[self.tableView reloadData];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
/* !!!This never get called!!!
if (tableView == self.searchDisplayController.searchResultsTableView) {
NSLog(#"in search");
}
*/
if(!self.searchDisplayController.isActive) {
return myCategories.count;
}
else
{
NSLog(#"in search === %d", _result.count);
return _result.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
if (!self.searchDisplayController.isActive) {
cell.textLabel.text = myCategories[indexPath.row];
return cell;
}
else {
NSLog(#"search cllFrRwAt %#", _result[indexPath.row]);
cell.textLabel.text = _result[indexPath.row];
return cell;
}
}
I can see in console that "in search === 5", and "search cllFrRwAt john" (5 times), but on the screen I see "no result". Only if I tap Cancel button on searchBar I'm see my downloaded result. How can I show download result immediatly as data was downloaded and update searchDisplay?
EDIT:
I've changed searchThroughData method with :
-(void)searchThroughDataWithString: (NSString *)searchingStr {
…
}
and
[[NSOperationQueue mainQueue] addOperationWithBlock:^ {
NSLog(#"Main Thread Code");
[self.tableView reloadData];
}];
But this doesnt help
I am recieving this error when i scroll to the bottom of my TableView, I dont think its any error with actually retrieving the pictures from the server.:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFArray objectAtIndex:]: index (15) beyond bounds (15)'
Here is my .m file I cut it to only the actually needed parts of the file:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self entries] count] + tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row % 2 == 0) {
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *created = [tweet objectForKey:#"created_at"];
NSLog(#"%#", created);
static NSString *CellIdentifier = #"TweetCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *text = [tweet objectForKey:#"text"];
NSString *name = [[tweet objectForKey:#"user"] objectForKey:#"name"];
cell.textLabel.text = text;
cell.detailTextLabel.text = [NSString stringWithFormat:#"by %#", name];
return cell;
}else {
static NSString *CellIdentifier = #"InstagramCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *entry = [self entries][indexPath.row];
NSString *imageUrlString = entry[#"images"][#"low_resolution"][#"url"];
NSURL *url = [NSURL URLWithString:imageUrlString];
[cell.imageView setImageWithURL:url];
return cell;
}
}
- (void)fetchTweets {
self.twitterClient = [[AFOAuth1Client alloc] initWithBaseURL:[NSURL URLWithString:#"https://api.twitter.com/1.1/"] key:#"TWEETER_KEY" secret:#"TWEETER_SECRET"];
[self.twitterClient authorizeUsingOAuthWithRequestTokenPath:#"/oauth/request_token" userAuthorizationPath:#"/oauth/authorize" callbackURL:[NSURL URLWithString:#"floadt://success"] accessTokenPath:#"/oauth/access_token" accessMethod:#"POST" scope:nil success:^(AFOAuth1Token *accessToken, id responseObject) {
[self.twitterClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self.twitterClient getPath:#"statuses/home_timeline.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *responseArray = (NSArray *)responseObject;
[responseArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"Success: %#", obj);
tweets = responseArray;
[self.tableView reloadData];
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
} failure:^(NSError *error) {
NSLog(#"Error: %#", error);
}];
}
There needs to be tight coordination between the return value from numberOfRowsInSection and the array access that the code does in cellForRowAtIndexPath.
Consider this, your entries array and tweets array each have 4 elements. So numberOfRowsInSection returns 8. The cellForRowAtIndexPath method gets called to configure row 6. Your code will do this: NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
But wait... that array has only 4 elements, right? Asking for something at index 6 will generate the crash you see.
It might be simpler to write a methods to interleave the arrays into a single array, then answer the count of the combined array in numberOfRowsInSection. In cellForRowAtIndexPath, the array elements themselves should be able to tell you what kind of row you have (not the index). Dereference the combined array and configure the table accordingly.
EDIT - I'll try to make my advice more explicit in code: Let's say, for simplicity, that "entries" and "tweets" are both arrays of NSDictionaries and that your app wants to organize them in the UI entries first, then tweets.
// in interface:
#property (nonatomic, strong) NSArray *myModel;
// in code:
- (NSArray *)myModel {
if (!_myModel) {
NSMutableArray *array = [NSMutableArray arrayWithArray:[self entries]];
[array addObjectsFromArray:tweets];
_myModel = [NSArray arrayWithArray:array];
}
return _myModel;
}
We call this 'myModel' for a reason. It's the datasource of the table. The datasource protocol is asking explicitly about this array (and no other).
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.myModel.count;
}
Now cellForRowAtIndexPath is going to ask you to configure that many (myModel count) rows, numbered 0..count-1. You must dereference the same array -- myModel -- for all datasource methods:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *myModelForThisRow = self.myModel[indexPath.row];
// get the cell = deque...
cell.textLabel.text = myModelForThisRow[#"someKey"];
return cell;
}
What if your tweets or entries array changes? No problem, just rebuild the model like this:
- (IBAction)tweetsOrEntriesDidChange:(id)sender {
self.myModel = nil; // the "lazy" getter will rebuild it
[self.tableView reloadData]; // this will call the datasource which will call the lazy getter
}
You are trying to go read into an array outside of it's bounds.
That array access look very suspicious
if (indexPath.row % 2 == 0) {
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
as well as this one
NSDictionary *entry = [self entries][indexPath.row];
From what I've seen your array tweets and [self entries] don't contain as many object each as there is row in your table section.
I take my assomption from here :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self entries] count] + tweets.count;
}
NSRangeException is thrown because you are trying to access an index which is not within the valid range for your array. Try setting an "Exception breakpoint" in Xcode to see where it's coming from. Check here to know more about Exception breakpoints
This is typically caused by an off by one error.