So, I'm using RestKit to access a webservice and retrive data from there.
So far I have two views, and that part of retrieving the data is fine, and I get the 100 objects I need, saving them into an array called "songs".
Here is didLoadObjects:
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
NSLog(#" Reached didLoadObjects: %d", [objects count]);
self.songs = objects;
NSLog(#"%#",self.songs);
}
Ok, I have two views, and the problem is, of course, on the second one. I'm using Storyboards. I gave the tableView cell an identifier called "TopListCellIdentifier".
So, I get the objects, all the 100 of them are "printed" in the command line, but the problem starts when I try to access the data from the array inside cellForRowAtIndexPath, something which has to be done because I have a custom tableViewCell displaying the info I need (sons, artists, covers, stuff like that). So, when I start the app, the first view is fine, but the second has 100 cells, but no info. Here is the cellForRowAtIndexPath code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *TopListCellIdentifier = #"TopListCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TopListCellIdentifier];
// Loading the Cover Images in the Background
// Cover Image: Tag 1
[((HJManagedImageV*)[cell viewWithTag:1]) clear];
HJManagedImageV* thumbImage = ((HJManagedImageV*)[cell viewWithTag:1]);
NSString *thumbUrl = [[self.songs objectAtIndex:[indexPath row]] thumbnail];
thumbImage.url = [NSURL URLWithString:thumbUrl];
[[ImageHandler sharedHandler].imgManager manage:thumbImage];
//Song and Artist: Tag 2 and 3
((UILabel*)[cell viewWithTag:2]).text = [[self.songs objectAtIndex:[indexPath row]] title];
((UILabel*)[cell viewWithTag:3]).text = [[self.songs objectAtIndex:[indexPath row]] artist];
//Arrow Up/Down/Same: Tag 4
//TODO
//Position Number: Tag 5
((UILabel*)[cell viewWithTag:5]).text = [NSString stringWithFormat:#"%d.", [indexPath row]+1];
return cell;
}
I've tried to put a debugger in the first line of cellRow(...) but the program doesn't enter there. I feel like i'm forgetting about something very simple, but I can't seem to figure out what.
Can someone help me, please?
You're never initing a new cell. You need to add a line like :
if (nil == cell)
cell = [[UITalbeViewCell alloc] init...
after your call to
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TopListCellIdentifier];
Related
I have a custom tableview cell. But when I want to call this cell in my cellForRowAtIndexpath, it gives the following error.
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableViewCell lblPosition]: unrecognized selector sent to instance 0xa62a600'
I checked a 100 times if everthing is hooked up correctly and named correctly but still doesn't find what is wrong. This is what I am doing in code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"KlassementCell";
KlassementCell *cell = (KlassementCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"KlassementCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
// ask NSFetchedResultsController for the NSMO at the row in question
Klassement *klassement = [self.fetchedResultsController objectAtIndexPath:indexPath];
// Then configure the cell using it ...
NSString *pointDiff = [NSString stringWithFormat:#"%#", klassement.goalsDiff];
NSLog(#"position %#",klassement.position);
NSLog(#"name %#",klassement.name);
cell.lblPosition.text = klassement.position;
cell.lblName.text = klassement.name;
cell.lblgamesPlayed.text = klassement.gamesPlayed;
cell.lblGamesWon.text = klassement.gamesWon;
cell.lblGamesTied.text = klassement.gamesTied;
cell.lblGamesLost.text = klassement.gamesLost;
cell.lblGoalsPos.text = klassement.goalsPos;
cell.lblGoalsNeg.text = klassement.goalsNeg;
cell.lblGoalsDiff.text = pointDiff;
cell.lblPoints.text = klassement.points;
return cell;
}
Can anybody help?
Kind regards.
Try
if ((cell == nil) || (![cell isKindOfClass: KlassementCell.class])) {
The cell returned in the queue might not be KlassementCell.
Have you set the class of 0 Index object in "KlassementCell" nib as KlassementCell and mapped all the subviews??
If not then do that and it may work.
maybe this helps in your viewDidLoad (or any other place for setup...) method:
UINib* myCellNib = [UINib nibWithNibName:#"KlassementCell" bundle:nil];
[tableView registerNib:myCellNib forCellReuseIdentifier:#"KlassementCell"];
and then instantiate the cell like this
cell = (KlassementCell*)[myCellNib instantiateWithOwner:nil options:nil];
myCellNib should be available in the class scope
Make sure in the nib that the class of the cell is set to "KlassementCell" AND also that the identifier is set to "KlassementCell".
You should not need to do anything else since only use 1 type of tableviewcell and only 1 identifier in cellForRowAtIndexPath
The identifier has to be the exact same as the class name, so both have to be "KlassementCell". That way there are no regular reusable UIcells in the queue. That's also why if ((cell == nil) || (![cell isKindOfClass: KlassementCell.class])) { works because you make sure you are not assigning things to properties that don't exist on a regular cell like lblPosition, lblName and lblgamesPlayed..
I want to display the date picked from UIDatePicker on the static cell.
The problem code is below. I want to change self.label.text by changing the date with UIDatePicker, but it doesn't change.
- (IBAction)inputDate:(id)sender {
self.label.text = [NSString stringWithFormat:#"%#", self.datePicker.date];
}
Method inputDate: is wired to UIDatePicker.
Property datePicker is also wired to UIDatePicker.
Is there any specific way to change static cells? Thanks for your kindness.
I would recommend you store the cell that you want to change as an ivar in your table view:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//...
// configure your birth date cell
self.birthDateCell = cell;
//...
}
Then your method would look something like:
- (IBAction)inputDate:(id)sender {
self.birthDateCell.label.text = [NSString stringWithFormat:#"%#", sender.date];
}
And that should do it. Additionally, make sure you are calling the inputDate function with the appropriate event action for the date picker.
EDIT: Additionally, you may need to reload your cell to present the data like so:
- (IBAction)inputDate:(id)sender {
self.label.text = [NSString stringWithFormat:#"%#", sender.date];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0]
withRowAnimation:NO];
}
Just one row code solved the Problem. Stunner's advice is very helpful for this final answer.
- (IBAction)inputDate:(id)sender {
self.label.text = [NSString stringWithFormat:#"%#", self.datePicker.date];
[self.tableView reloadData]; // <- add this row !
}
I have a UITableView with a custom cell which i fill (with array infoservices) after parsing the xml data.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
ApplicationCell *cell = (ApplicationCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[self.cellNib instantiateWithOwner:self options:nil];
cell = tmpCell;
self.tmpCell = nil;
}
infoService *e = [self.infoservices objectAtIndex:indexPath.row];
cell.name = [e infoName];
NSString *infodetails = [e infoDetails];
if ( infodetails == nil ) {
cell.details = #"Loading...";
[self startInfoDownload:e forIndexPath:indexPath];
NSLog(#"Loading...");
} else {
cell.details = infodetails;
NSLog(#"Show info detail: %#", infodetails );
}
return cell;
}
- (void)infoDidFinishLoading:(NSIndexPath *)indexPath
{
infoDownloader *infoserv = [imageDownloadsInProgress objectForKey:indexPath];
if (infoserv != nil)
{
[infoservices replaceObjectAtIndex:[indexPath row] withObject:infoserv.appRecord];
NSIndexPath *a = [NSIndexPath indexPathForRow:indexPath.row inSection:0]; // I wanted to update this cell specifically
ApplicationCell *cell = (ApplicationCell *)[self.tableView cellForRowAtIndexPath:a];
cell.details = [[infoservices objectAtIndex:[indexPath row]] infoDetails];
NSLog(#"Updating=%#", [[infoservices objectAtIndex:[indexPath row]] infoDetails]);
}
}
For each cell i'm using NSURLConnection sendAsynchronousRequest to retrieve and parse xml data from object infoDownloader with
- (void)startDownload
for each individual cell.
After data has been successfully parsed delegate method from infoDownloader is called
- (void)infoDidFinishLoading:(NSIndexPath *)indexPath
The problem is that, while the
- (void)infoDidFinishLoading:(NSIndexPath *)indexPath
gets called after parsing each cell and i can see the
NSLog(#"Updating=%#", [[infoservices objectAtIndex:[indexPath row]] infoDetails]);
in the debugger with the correct details, the cell does not get refreshed immediately but after 6 or 7 seconds. Also cellForRowAtIndexPath does not get called from
- (void)infoDidFinishLoading:(NSIndexPath *)indexPath
for some reason because there is not debug output after the infoDidFinishLoading. Also i don't understand how the cell.details gets actually refreshed since cellForRowAtIndexPath isn't called again.
I've tried to setup this function using Apple's LazyTableImages loading example, which i have used successful, but i don't know what's going wrong.
You may need to call reloadRowsAtIndexPaths once the data has been loaded. What could be happening is that the cell data has been loaded but the cell drawing is not updated.
Also I believe your code can request the same data multiple times because if [e infoDetails] is nil a request is made, but the cell could be requested multiple times before the data is loaded so [self startInfoDownload:e forIndexPath:indexPath] would be called multiple times, downloading the same data. You should look into keeping track of which rows you have requested data for.
Check out this code for some ideas on how to solve this: https://github.com/kgn/Spectttator/blob/master/SpectttatorTest-iOS/RootViewController.m#L123
Any changes affecting UI must be performed on main thread.
Refreshing tableView, by changing cells details, realoading tabe view, realoding rows... are changes affecting UI.
In particular, you should perform your changes in the main thread using :
dispatch_async(dispatch_get_main_queue(), ^{
cell.details = [[infoservices objectAtIndex:[indexPath row]] infoDetails];
}
or prior to iOS 4 :
cell performSelectorOnMainThread:#selector(setDetails:) withObject:[[infoservices objectAtIndex:[indexPath row]] infoDetails];
Hopefully this will be a quick fix. I have been trying to figure out the error that i keep getting. The error is listed below and the appdelagate is below that.
Any help is appreciated.
Thanks
2012-04-12 21:11:52.669 Chanda[75100:f803] --- Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1914.84/UITableView.m:1037
2012-04-12 21:11:52.671 Chanda[75100:f803] --- Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize databaseName,databasePath;
- (BOOL)application: (UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
self.databaseName = #"Customers.db";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDir = [documentPaths objectAtIndex:0];
self.databasePath = [documentDir stringByAppendingPathComponent:self.databaseName];
[self createAndCheckDatabase];
return YES;
}
- (void)createAndCheckDatabase {
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
success = [fileManager fileExistsAtPath:databasePath];
if (success) return;
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:self.databaseName];
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
}
#end
I don't see the reason for you to show us this part of code. Your error must be connected to this part in your code I assume
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
Probably you are making a mistake in one of these data source methods. Currently it's impossible to say what exactly is wrong but I assume it could be something like: You are telling the table view in the numberOfRowsInSection you would like to have n rows reserved and setup and in the cellForRowAtIndexPath you then only handle n - 1 rows for example.
Sorry that this answer can't be as precise as it should be. If you show us your implementation of your data source it would be much easier to tell what's going on.
Like Sun Tzu said: it's best to win without fighting. In my case whenever I see this kind of error message (ie discrepancy between rows added deleted etc).. I don't even debug anything.. I simply avoid making that extra call where I reload the rows etc.. that's 99% of the cases where this error happens.
This is a common scenario where this bug happens: I have a UINavigationController and it has a UITableView, when I click on a row it pushes a new UITableView and so on. This error always happens to me when I pop the last UITableview and go back to the UITableView before it, at this point I make an unnecessary call to the loadIt function which basically inserts the rows and relaods the UITableView.
The reason this happens is because I erroneously place my loadIt function in viewDidAppear:animated rather than viewDidLoad. viewDidAppear:animated is called every time the UITableView is displayed, viewDidLoad is only called once.
When removing rows, remember that it also checks sections when updating, in:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)theTableView
If you want to remove a row that is the last item in a section you need to remove the whole section instead (otherwise it might get section count wrong and throw this exception).
Don't forget to update your array which determines numberOfRowsInSection. It needs to be updated before you animate and remove
We check if number of rows in section is 1 because we will have to delete the entire section.
Do correct me if anyone can make this answer clearer.
[self.tableView beginUpdates];
if ([tableView numberOfRowsInSection:indexPath.section] == 1) {
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
} else {
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
[self.tableView endUpdates];
I put each section elements in separated arrays. Then put them into another array( arrayWithArray). My solution here for this problem:
[quarantineMessages removeObject : message];
[_tableView beginUpdates];
if([[arrayWithArray objectAtIndex: indPath.section] count] > 1)
{
[_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indPath] withRowAnimation:UITableViewRowAnimationBottom];
}
else
{
[_tableView deleteSections:[NSIndexSet indexSetWithIndex:indPath.section]
withRowAnimation:UITableViewRowAnimationFade];
}
[_tableView endUpdates];
I had the same error.
I was using the following lines
UINib *myCustomCellNib = [UINib nibWithNibName:#"CustomNib" bundle:nil];
[tableView registerNib:myCustomCellNib forCellReuseIdentifier:#"CustomNib"];
to register the nib in the viewDidLoad method, since I had a different nib that was also associated with the same class. Hence, the line
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"GBFBLoadingCell"];
was returning nil unless I registered the nib in the viewDidLoad.
My problem was that I forgot to set the identifier in the attributes inspector for my file "CustomNib.xib" and "CustomNib~iphone.xib". (Or more precisely, that I forgot to press enter after typing the identifier in the attribute inspector in XCode, so that the new name failed to save.)
Hope this helps.
If you're using an NSFetchedResultsController like me and updating data in a background thread, don't forget to begin and end updates in the delegate:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
I Had the same error , which when trying with [tableView reloadData] was working fine .
The error was actually in the line
[TabView insertRowsAtIndexPaths:indexPathsArray withRowAnimation:UITableViewRowAnimationRight];
When i tried to check the indexPath values , they weren't correct as required .
I fixed it by changing values in indexPathsArray .
Hope this helps .
Its could be one of UITableViewDataSource protocol methods
For
- tableView:numberOfRowsInSection:
it should return an integer equal to the sum or result of
-insertRowsAtIndexPaths:withRowAnimation: and/or -deleteRowsAtIndexPaths:withRowAnimation:
For
- numberOfSectionsInTableView:
it should return an integer equal to the sum or result of
-insertRowsAtIndexPaths:withRowAnimation: and/or
-deleteSections:withRowAnimation:
I had the same problem with a Core Data base. If your using many FRC, you just need to reload the tableview inside each condition in numberOfSectionsInTableView.
I just had this happen to me while using Swift and a FRC backed data store to manage information. Adding a simple check to the delete operation to evaluate the current indexPath.section allowed me to avoid an extraneous call. I think I understand why this problem occurs... Basically I load a message into the top row whenever my dataset is empty. This creates an off by one issue as there is a faux row.
My Solution
... delete my entity, save the datastore and call reloadData on tableView
//next I add this simple check to avoid calling deleteRows when the system (wrongly) determines that there is no section.
if indexPath.section > 0 {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .None)
}
Simply check that you call [yourTableView reloadData]; after modify array of values.
I am trying to asynchronously download images for a UITableViewCell, but it is currently setting the same image to each cell.
Please can you tell me the problem with my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
SearchObject *so = (SearchObject *)[_tableData objectAtIndex:indexPath.row];
cell.textLabel.text = [[[[so tweet] stringByReplacingOccurrencesOfString:#""" withString:#"\""] stringByReplacingOccurrencesOfString:#"<" withString:#"<"] stringByReplacingOccurrencesOfString:#">" withString:#">"];
cell.detailTextLabel.text = [so fromUser];
if (cell.imageView.image == nil) {
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:[so userProfileImageURL]]];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
[conn start];
}
if ([_cellImages count] > indexPath.row) {
cell.imageView.image = [UIImage imageWithData:[_cellImages objectAtIndex:indexPath.row]];
}
return cell;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[_cellData appendData:data];
[_cellImages addObject:_cellData];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.tableView reloadData];
}
You are appending the data from every image downloaded to the same data object. So in the best case the data object ends up with the data for image #1 immediately followed by the data for image #2 and so on; the image decoder is apparently taking the first image in the chunk of data and ignoring the garbage after. You also seem to be unaware that NSURLConnections' connection:didReceiveData: will not necessarily be called in the order that the connections were started, that connection:didReceiveData: can be called zero or multiple times per connection (and probably will if your images are more than a few kibibytes), and that tableView:cellForRowAtIndexPath: is not guaranteed to be called for every cell in the table in order. All of which are going to totally screw up your _cellImages array.
To do this right, you need to have a separate NSMutableData instance for each connection, and you need to add it to your _cellImages array just once, and at the correct index for the row rather than at the arbitrary next available index. And then in connection:didReceiveData: you need to figure out the correct NSMutableData instance to append to; this could be done by using the connection object (wrapped in an NSValue using valueWithNonretainedObject:) as the key in an NSMutableDictionary, or using objc_setAssociatedObject to attach the data object to the connection object, or by making yourself a class that handles all the management of the NSURLConnection for you and hands you the data object when complete.
I don't know if this is causing the problem or not, but in your connection:didReceiveData: method you're just appending the image data to the array; you should be storing the image data in such a way that you can link it to the cell it's supposed to be shown in. One way to do this would be use an NSMutableArray populated with a bunch of [NSNull]s, then replace the null value at the appropriate index when the connection has finished loading.
Also, you're appending the _cellData to the _cellImages array when the connection hasn't finished loading, you should only be doing this in the connection:didFinishLoading method.