Why does my UITableView not update? Here is how I am attempting to update it.
- (void)viewWillAppear:(BOOL)animated
{
NSArray* arrValues = [self.defaults objectForKey:#"values"];
[self.tableScores insertRowsAtIndexPaths:arrValues withRowAnimation:UITableViewRowAnimationNone];
}
arrValues is now an array of NSNumbers. I am sure that it is not empty.
Call [tableScores reloadData]; in - (void)viewWillAppear:(BOOL)animated
Update 1
Also, you need to define arrValues in your header. Each time the viewWillAppear, you are creating a new instance, but you won't be able to use it throughout the rest of your controller. This is the main reason you aren't seeing anything besides at your breakpoint.
Update 2
According to your comment below, you have not implemented cellForRowAtIndexPath: which is how the cell is created. Below is an example, but you may want to search around the net for example projects because this UITableView's 101. There is still a lot more you need to learn when it comes to arrays and tableViews.
Example cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"FriendCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [arrValues objectAtIndex:indexPath.row];
return cell;
}
Related
I have an NSArray (called masterArray) with 2 different type of objects in it - object1 and object2. These objects require 2 different table view cells (each one made programmatically), I implemented this as such
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[masterArray objectAtIndex:indexPath.row] isKindOfClass:[object1 class]])
{
cell1 *cell = (cell1 *)[tableView dequeueReusableCellWithIdentifier:#"obj1Cell"];
if (cell == nil)
{
cell = [[cell1 alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"obj1Cell"];
}
//set up labels and stuff
return cell;
}
if ([[masterArray objectAtIndex:indexPath.row] isKindOfClass:[object2 class]])
{
cell2 *cell = (cell2 *)[tableView dequeueReusableCellWithIdentifier:#"obj2Cell"];
if (cell == nil)
{
cell = [[cell2 alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"obj2Cell"];
}
//set up labels and stuff
return cell;
}
return nil;
}
Now, this code displays the cells fine. When you tap a cell, I use this code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
newViewController *newVC = [[newViewController alloc]init];
newVC.item = [masterArray objectAtIndex:indexPath.row];
[self.navigationController pushViewController:newVC animated:YES];
}
When I tap however, I get the error message "UITableView failed to obtain a cell from its dataSource". I've looked at other questions answering this too. This one says to add the line
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
so I do so, replacing my "cell1 *cell = (cell1 *)..." line with
cell1 *cell = (cell1 *)[tableView dequeueReusableCellWithIdentifier:#"obj1Cell" forIndexPath:indexPath];
yet now I get a
"'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier obj1cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard"
error, which doesn't apply in my situation because my UITableViewCells are made programmatically (there is no storyboard). Also recommended in that post is to add "UITableViewDelegate, UITableViewDataSource", however this appears to only be an option in Swift, not Objective-C.
Before using dequeueReusableCellWithIdentifier with an ID, you need to register this ID like so:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView = ...;
[self.tableView registerClass:[cell1 class] forCellReuseIdentifier:#"obj1Cell"];
[self.tableView registerClass:[cell2 class] forCellReuseIdentifier:#"obj2Cell"];
}
Additional notes:
if (cell == nil) is redundant, because it is never nil in practice.
In Objective-C the convention is to have class names started from an app prefix (2-3 uppercase letters), and an uppercase letter, e.g.: MYCell1, MYCell2, MYObject1, etc. (replace MY with something meaningful for you).
I have a project with a tableView created on the storyboard. It's pretty simple and I was following a tutorial to do it, my view controller looks like this
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
cell.textLabel.text = restaurantDisplayNames[indexPath.row];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}else{
cell.accessoryType = UITableViewCellAccessoryNone;
}
[tableView deselectRowAtIndexPath:indexPath animated:YES
];
}
So when you tap on a cell it puts a check mark next to it. Now I want people to be able to search for things. Problem is, Apple changed the search bar for iOS 8 and supposedly made it simpler but I can't find any tutorials on the UISearchController which replaced the depricated method.
So I dragged and dropped a Search Bar and Search Display Controller into my view controller and added the UISearchControllerDelegate, UISearchResultsUpdating> protocol declarations, but I get a crash:
'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
Whenever I tap on the search bar.
I also have the method
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController{
}
But it is empty because I don't know what to put in there. Supposedly it is very easy and only requires a few lines of code to get it up and running, but the one tutorial I found here: http://www.stuartbreckenridge.com/blog/examining-the-new-uisearchcontroller-api now only is in swift but doesn't explain what goes in that method.
I figured it out. Man, some of these things are so ridiculously simple yet you have to dig deep to find answers. You'd think Apple could put some tips in XCode about these things, little warnings or something, but here's how I fixed it:
Apparently you need to add
if (!cell){
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
In the cellForRowAtIndexPath: method so it looks like this:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (!cell){
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
cell.textLabel.text = restaurantDisplayNames[indexPath.row];
return cell;
}
So it generates the cell if it doesn't exist. Not sure why, but oh well ...
I've created a cell with 2 UIButtons in the IB and subclassed it. How can I use it without reusing it over again? (for a small fixed table)
I tried doing something like:
RaffleResultCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
but that won't show my cell in the UITableView, just a blank one.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//Where we configure the cell in each row
id currentRaffle = [_winnings objectAtIndex:indexPath.row];
RaffleResultCell *cell = [tableView dequeueReusableCellWithIdentifier:#"raffleResCell"];
if (cell == nil) {
cell = [[RaffleResultCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"raffleResCell"];
}
return cell;
}
It is not a good practice to avoid Reusability,I will say dont do it
Reusability is done in this line
RaffleResultCell *cell = [tableView dequeueReusableCellWithIdentifier:#"raffleResCell"];
Remove that line and just call alloc method everytime without the check loop
Like
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
id currentRaffle = [_winnings objectAtIndex:indexPath.row];
cell = [[RaffleResultCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"raffleResCell"];
return cell;
}
You told you set up the UITableViewCell in IB then you need to get Nib file and then use that file as
// Get nib file from mainBundle
NSArray* topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"RaffleResultCell" owner:self options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[UITableViewCell class]]) {
RaffleResultCell *cell = (RaffleResultCell *)currentObject;
break;
}
}
now setup any text for your button and return cell
I have a UITableView with custom cells in it. This custom cell looks like a normal cell with Style "Detail Left". But my custom cell has a detail label and a text field, to allow the user to edit the text. I have a Save button where I want to save all data the user entered. I made up the custom cell using this tutorial: http://agilewarrior.wordpress.com/2012/05/19/how-to-add-a-custom-uitableviewcell-to-a-xib-file-objective-c/#comment-4883
To get the user data I set the text field's delegate to the table view controller (self) and implemented the textFieldDidEndEditing: method. In this method I ask for the text field's parent view (= cell) and ask for the indexPath of this cell. The problem is, I always get 0 for indexPath.row, no matter what cell I edit. What am I doing wrong?
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:[CustomCell reuseIdentifier] forIndexPath:indexPath];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = self.customCell;
self.customCell = nil;
}
cell.label.text = #"Some label text";
cell.editField.delegate = self;
cell.accessoryType = UITableViewCellAccessoryNone;
return cell;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
CustomCell *cell = (CustomCell *) textField.superview;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
NSLog(#"the section is %d and row is %d", indexPath.section, indexPath.row);
...
}
EDIT: I just found out, that the returned indexPath is nil. So if it is nil I get the standard values 0, thats okay. But now: Why do I get nil for indexPath?
I just found the solution myself. I found the code for the superview in stackoverflow, so I just copied it. But that does not work. You need to do two superviews like this:
CustomCell *cell = (CustomCell *) textField.superview.superview;
Now it works fine!
Could you just add an index property to your CustomCell and populate it with indexPath.row inside (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
I'm trying to have a dynamic table which expands on click to reveal some other content. I've got to the point where the table is populated with information from an NSMutableArray. I can also press each cell and it expands to double the size. Now, the next step, which is proving to be somewhat troublesome is to get it to display new/alternative text when they click on the cell. First of all, here's my setup method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
NSString *cellValue = [cellContent objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
return cell;
}
and after this, I have the method where the cell expands on press:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// If our cell is selected, return double height
if([self cellIsSelected:indexPath]) {
return kCellHeight * 2.0;
[cellContent replaceObjectAtIndex:[self cellIsSelected:indexPath] withObject:#"NEW STUFF HERE"];
}
I must be going about this in the wrong way as nothing changes when I touch the cell. How do I get it to display new/alternative text on touch? Any help would be super, I think it's likely to be something quite easy, but I can't see it at the moment.
Thanks!
Your second method is only triggered when the cell is built or rebuilt. You need to explicitly ask it to refresh itself in the following method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
And then within that method, you can call
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
and now your redraw methods will be triggered, so you can handle whatever you want there.