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.
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 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.
I'm new to iOS development and I am trying to figure out what the best solution to my problem would be. I have a UITableViewController class which calls a method named fetchModules in the viewDidLoad. This fetches all the data I need for my table using AFNetworking 2.
However, my table delegate methods such as numberOfRowsInSectionand cellForRowAtIndexPath are failing because the AFNetworking call has not finished yet and the array I am using to store the data has not been populated.
The actual error I am getting is
Terminating app due to uncaught exception 'NSRangeException'
Here's my code:
#import "HistoryTableViewController.h"
#interface HistoryTableViewController ()
#property NSArray *modules;
#end
#implementation HistoryTableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.modules = [[NSArray alloc] init];
[self fetchModules];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)fetchModules
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager.requestSerializer setValue:self.token forHTTPHeaderField:#"X-Auth-Token"];
[manager GET:#"http://myurl.com/" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject)
{
//NSLog(#"JSON: %#", responseObject);
self.modules = [responseObject objectForKey:#"data"];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.modules && self.modules.count) {
return self.modules.count;
} else {
return 0;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = #"Cell Identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
}
NSDictionary *module = [self.modules objectAtIndex:indexPath.row];
cell.textLabel.text = [module objectForKey:#"code"];
return cell;
}
Suggestions?
It's pretty much common solution for async network call.
Add [self.tableView reloadData] inside the AFNetworking success block:
- (void)fetchModules
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager.requestSerializer setValue:self.token forHTTPHeaderField:#"X-Auth-Token"];
[manager GET:#"http://myurl.com/" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject)
{
//NSLog(#"JSON: %#", responseObject);
self.modules = [responseObject objectForKey:#"data"];
[self.tableView reloadData];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}`enter code here`
I think the conditions should be as below.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.modules != nil && self.modules.count > 0) {
return self.modules.count;
} else {
return 0;
}
}
Also do not forget to reload table after you get the response.
Few corrections may help you. First synthesize property "modules". Also in delegate method for creating Table Cell ensure that if "modules" is empty then that method should do no operation on "modules" property and just return "cell". I hope this helps you
I have an app where you can add comments to a post. I'm using [sender tag] to get the index but it's always returning the same post. So no matter what post cell I click the comment button on it always adds it to the same cell and not the one I clicked on.
Any help is super appreciated.
Here is my code(note I've stripped my code to only the parts I think will matter to make reading easier as some functions have a lot of code. If you need to see some more just let me know):
Setting the comment button on each cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
[cell.commentButton addTarget:self action:#selector(commentButtonClick:) forControlEvents:(UIControlEvents)UIControlEventTouchDown];
return cell;
}
Comment button. Just performing a segue:
- (void)commentButtonClick:(id)sender {
[self performSegueWithIdentifier:#"addCommentSegue" sender:sender];
}
Prepare for segue(I send them to a basic view controller with a text field and a save button):
else if ([segue.identifier isEqualToString:#"addCommentSegue"]) {
GFAddCommentViewController *secondDestViewController = [[segue destinationViewController] topViewController];
NSInteger index = [sender tag];
NSDictionary *rootObject = self.posts[index];
NSDictionary *post = rootObject[#"post"];
NSDictionary *group = post[#"group"];
secondDestViewController.postId = [post[#"id"] copy];
secondDestViewController.groupName = [group[#"name"] copy];
secondDestViewController.postBody =[post[#"body"] copy];
}
When they click send on the new view controller this is the function:
-(void)addComment:(id)sender {
GFCredentialStore *credentialStore = [[GFCredentialStore alloc] init];
NSString * authToken = [credentialStore authToken];
NSString * addCommentURL = [NSString stringWithFormat:#"%s%s/%#/%s", kBaseURL, kPostURL, self.postId, kCommentURL];
NSString * commentBody = self.commentTextField.text;
NSMutableDictionary *mutableParams = [NSMutableDictionary dictionary];
if (commentBody) {
[mutableParams setObject:commentBody forKey:#"comment[body]"];
}
[SVProgressHUD showWithStatus:#"Adding Comment"];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager.requestSerializer setValue:authToken forHTTPHeaderField:#"auth_token"];
[manager POST:addCommentURL parameters:mutableParams success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
[SVProgressHUD showSuccessWithStatus:#"Comment Added"];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
Just to clarify it's successfully adding comments to the database just the post.id is incorrect.
Are you sure you set buttons tag correctly? It seem that you should set like that
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
[cell.commentButton addTarget:self
action:#selector(commentButtonClick:)
forControlEvents:(UIControlEvents)UIControlEventTouchDown];
cell.commentButton.tag = indexPath.row;
return cell;
}
- (void)fetchImages {
if (self.profileImages == nil) {
self.profileImages = [[NSMutableDictionary alloc] initWithCapacity:200];
}
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (id tweet in self.timeline) {
TWRequest *fetchUserImageRequest = [[TWRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://api.twitter.com/1/users/profile_image/%#", [tweet valueForKeyPath:#"user.screen_name"]]] parameters:nil requestMethod:TWRequestMethodGET];
[fetchUserImageRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if ([urlResponse statusCode] == 200) {
[self.profileImages setObject:[UIImage imageWithData:responseData] forKey:[tweet valueForKeyPath:#"user.screen_name"]];
NSArray *indexPath = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:[self.timeline indexOfObject:tweet] inSection:0]];
[self.tableView reloadRowsAtIndexPaths:indexPath withRowAnimation:UITableViewRowAnimationNone];
}
}];
}
});
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"FavoriteCell"];
// configure cell
id tweet = [self.timeline objectAtIndex:[indexPath row]];
UILabel *tweetLabel = (UILabel *)[cell viewWithTag:102];
tweetLabel.text = [tweet objectForKey:#"text"];
UILabel *usernameLabel = (UILabel *)[cell viewWithTag:101];
usernameLabel.text = [tweet valueForKeyPath:#"user.name"];
UIImageView *profileImage = (UIImageView *)[cell viewWithTag:100];
profileImage.image = [self.profileImages objectForKey:[tweet valueForKeyPath:#"user.screen_name"]];
UILabel *dateLabel = (UILabel *)[cell viewWithTag:103];
NSString *labelString = [[tweet objectForKey:#"created_at"] substringToIndex:10];
dateLabel.text = labelString;
return cell;
}
I get the timeline then want to get the profile images for all of users in the timeline. I need to loop through the tweets and get the image. I'm curious how I can determine when all of the images have been fetched then reload the tableview. As of now this isn't happening. The TWRequest is running after the table is reloaded. What am I doing wrong here? Maybe there is a better way to do this?
Thanks a lot.
That's because [TWRequest performRequestWithHandler:] is an async method. Why do you need to reload the entire table? Why not just reload the cell when you get a image (in the end of your handler block).
If you really want to reload the entire table just keep a count of all your finished requests, and reload the table when all is done.
If you want to reload a single cell you can do something like:
dispatch_sync(dispatch_queue_get_main(), ^{
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:0];
}