Save checkmark in NSUserDefaults - dynamic TableView - objective-c

Solution!!
Thanks Josh for all the patience :D
So the code in the initial question always ticks the selected cell and deselects a previously selected cell without saving anything to the NSUserDefaults. After a lot of help from Josh the new code (right below) is the following (exactly the same as in his reply/I only clened it up a little and removed #properties defined in previous attempts to make it work).
The user taps one of the 7 cells. The indexPath of tapped cell is then saved in the NSUserdefaultsand when the TableView is open again a checkmark is displayed next to the previously tapped cell. The indexpath is always saved in the same NSObjectso only one checkmark will ever be displayed.
#import "CycleTableViewController.h"
#import "SettingsTableViewController.h"
#interface CycleTableViewController ()
#property (nonatomic) int selectedRow;
#end
#implementation CycleTableViewController
#synthesize array;
- (void)viewDidLoad {
//load array containing options
array=#[#"1", #"2", #"3", #"4", #"5", #"6", #"7"];
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) viewWillDisappear:(BOOL)animated{
//save indexpath of tapped cell to NSUserdefaults
[[NSUserDefaults standardUserDefaults] setObject:#(indexPath.row) forKey:#"cycle"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIndentifier=#"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIndentifier forIndexPath:indexPath];
cell.textLabel.text = [array objectAtIndex:indexPath.row];
//display checkmark next to the tapped cell
if(indexPath.row == self.selectedRow){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else{
ell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//assign indexpath to selectedRow, so NSUserdefaults can save them
self.selectedRow = indexPath.row;
[self.tableView reloadData];
}
//save indexpath of tapped cell to NSUserdefaults
[[NSUserDefaults standardUserDefaults] setObject:#(indexPath.row) forKey:#"cycle"];
[[NSUserDefaults standardUserDefaults] synchronize];
#end
Initial question
I've been reading through tons of posts on here on saving an index path to the user defaults, but none of them has worked for me...
In the following TableView I have an array of data (1-7) that is displayed and then passed to another ViewController once a cell is pressed. The pressed cell gets a checkmark and if I go back using the Navigation Controller I can see the checkmark. Only one item is selected at a time. If I press another row, the checkmark changes.
So now I want to save the indexpath of the selected row to the UserDefaults (in the didselectrowat indexpath) and the load it in the viewdidload but I always get a build failed, whatever I try...
Can somebody please point me in the right direction? This should be easy, but I somehow just can't get it to work :/
Updated Code! Once I get it working I'll upload the finished answer!
#import "CycleTableViewController.h"
#import "SettingsTableViewController.h"
#interface CycleTableViewController (){
NSString *currentCategory; // pointer to the currently checked item
}
// other #property declarations here or in your .m file
#define CHECKED_KEY #"kCheckedBoxKey"
#property (nonatomic) int selectedRow;
#end
#implementation CycleTableViewController
#synthesize array;
- (void)viewDidLoad {
//load tickmark according to newCell;
array=#[#"21+7", #"22+6", #"23+5", #"24+4", #"25+3", #"26+2", #"28"];
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void) viewWillAppear:(BOOL)animated {
self.selectedRow = [[[NSUserDefaults standardUserDefaults] objectForKey:#"cycle"] intValue];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIndentifier=#"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIndentifier forIndexPath:indexPath];
cell.textLabel.text = [array objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[[NSUserDefaults standardUserDefaults] setObject:#(indexPath.row) forKey:#"cycle"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSIndexPath *IndexPath = [NSIndexPath indexPathForRow:self.selectedRow inSection:0];
UITableViewCell *Cell = [tableView cellForRowAtIndexPath:IndexPath];
Cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
#end

#import "CycleTableViewController.h"
#import "SettingsTableViewController.h"
#interface CycleTableViewController (){
NSString *currentCategory; // pointer to the currently checked item
}
// other #property declarations here or in your .m file
#define CHECKED_KEY #"kCheckedBoxKey"
#property (nonatomic) int selectedRow;
#end
#implementation CycleTableViewController
#synthesize array;
- (void)viewDidLoad {
//load tickmark according to newCell;
array=#[#"21+7", #"22+6", #"23+5", #"24+4", #"25+3", #"26+2", #"28"];
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) viewWillAppear:(BOOL)animated {
self.selectedRow = [[[NSUserDefaults standardUserDefaults] objectForKey:#"cycle"] intValue];
}
- (void) viewWillDisappear:(BOOL)animated{
[[NSUserDefaults standardUserDefaults] setObject:#(indexPath.row) forKey:#"cycle"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIndentifier=#"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIndentifier forIndexPath:indexPath];
cell.textLabel.text = [array objectAtIndex:indexPath.row];
if(indexPath.row == self.selectedRow){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.selectedRow = indexPath.row;
[self.tableView reloadData];
}
#end

Where in the code are you saving the user defaults?
In didSelectRowAtIndexPath, you could add:
[[NSUserDefaults standardUserDefaults] setObject:#(indexPath.row) forKey:#"nameOfSetting"];
[[NSUserDefaults standardUserDefaults] synchronize];
In the cellForRowAtIndexPath you can check if
indexPath.row == [[[NSUserDefaults standardUserDefaults] objectForKey:#"nameOfSetting"] intValue]
and if so, add the checkmark accessory. If not, show no accessory.
Note that I would actually create a property that holds the selectedValue, and only upon leaving the settings page or tapping a save button do you actually update the user defaults. Then in viewWillAppear I would set this property equal to the setting in the user defaults and call reload on the table.
EDIT
To expand on the final note, you can do the following:
In the interface...
#property (nonatomic) int selectedRow;
In the implementation...
self.selectedRow = [[NSUserDefaults standardUserDefaults] objectForKey:#"nameOfsetting"] intValue];
You can place this code in viewWillAppear, viewDidAppear or even in prepareForSegue in the previous view controller that you are segueing from as long as the selectedRow property is declared in the header file. I would not put this code in viewDidLoad.
Then separately in cellForRowAtIndexPath I would add something like:
if(indexPath.row == self.selectedRow){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else{
cell.accessoryType = UITableViewCellAccessoryNone;
}
EDIT #2
In the OptionsViewController where the current setting is displayed you can load the setting from standardUserDefaults. You can save a number like I mentioned as the setting (i.e. if you have setting options, you can number them 0 - 6 and use a typedef to make the code more clear) or you can store a string. If you store your setting as a number, you just need a dictionary to map the number to a setting description. If you store your setting as a string, you could just display the string.
Regardless of the method chosen, in the OptionsViewController do this:
#import "SettingsViewController.h"
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.destinationViewController isKindOfClass:[SettingsViewController class]]){
SettingsViewController *settingsViewController = (SettingsViewController *) segue.destinationViewController;
settingsViewController.selectedSetting= <INSERT YOUR SETTING FROM STANDARDUSERDEFAULTS>
}
}
Basically, you are handing off your setting from the options to the settings view which contains your table. You can make your selectedSetting in the SettingsViewController a number or a string as above. Just as some point, you have to convert your setting into a number so that the table knows which row has the checkmark. I would keep the rest of the code the same, i.e. the if statement in the cellForRowAtIndexPath. You need to somehow check if the indexPath.row matches the selectedSetting (i.e. if it is a string, you need to convert it into a number). And in the viewWillAppear or viewDidAppear reload the tableView so the checkmark will be displayed.
When the user selects a different row, you can update the selectedSetting property and then in viewWillDisappear or prepareForSegue (i.e. before the unwind segue) you can update standardUserDefaults.

Related

Xcode - Segue - Pass Variable From Cell class to TableViewController class

I'm struggling to pass a variable from one class to another. I've done some reading and the Segue method seems the most appropriate for me, I just can't get it to work. A little help would be greatly appreciated.
I have a UITableView (TableViewController1), which contains a cell (Cell1), which contains a label (ReferenceLabel 1). Table 1 is populated from an array; each cell contains a reference number (contained within ReferenceLabelLabel 1). The reference number from the selected cell needs to be passed to TableViewController2.
What I have so far:
TableViewController1.h
#import <UIKit/UIKit.h>
#define kGETUrl #"http://localhost/RC.php"
#interface TableViewController : UITableViewController {
NSMutableArray *json;
}
#end
TableViewController1.m
#import "TableViewController1.h"
#import "TableViewController2.h"
#import "TableViewCell1.h"
#interface TableViewController1 ()
#end
#implementation TableViewController1
- (id)initWithStyle:(UITableViewStyle)style {
self = [super initWithStyle:style];
if (self) {
}
return self;
}
- (void) getData:(NSData *) data {
json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
[self.tableView reloadData];
}
- (void) start {
NSURL *url = [NSURL URLWithString:kGETUrl];
NSData *data = [NSData dataWithContentsOfURL:url];
[self getData:data];
}
#pragma mark - View Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
[self start];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [json count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TableViewCell1";
TableViewCell1 *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[TableViewCell1 alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSDictionary *info = [json objectAtIndex:indexPath.row];
cell.ReferenceLabel1.text = [info objectForKey:#"id"];
return cell;
}
//I'm stuck with the following... any help appreciate... i cant figure how to send the selected row's ReferenceLabel.text value
-(void)TableViewController1:(UITableView *)TableViewController1 didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"TVC1RefNumberTVC2" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"TVC1RefNumberTVC2"])
{
NSIndexPath *ip = [self.tableView indexPathForSelectedRow];
DetailObject *detail = [self detailForIndexPath:path];
[segue.destinationViewController setDetail:detail];
}
}
#end
As for TableViewController2, I'm guessing I simply need to create a property for the variable I'm sending from TableViewController1?
You're right that you need to declare a property in your second view controller so that you can access it from your first view controller's prepareForSegue method.
You can declare a property in TableViewController2's header file that looks something like this:
#property (nonatomic, strong) DetailObject *detail;
And then set its detail property within the if clause within your TableViewController1's prepareForSegue method like this:
TableViewController2 *tableViewController2 = segue.destinationViewController
tableViewController2.detail = detail;
You can also add an additional check before casting the segue.destinationViewController by checking if the destinationVC's class matches that of TableViewController2.

Load information from text field to table view

Hi I am trying to make an app that has a text field, tableview and a button.
When you press the button it adds the information from the text field to de tableview.
Do any one know when I can find a tutorial for this? I had one on a book but I cant find it.
Thanks in advance.
Here is my code so far:
code of .h
#interface BlockNotasViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
IBOutlet UITableView *tableNota;}
#property (strong, nonatomic) IBOutlet UITextField *textonota;
#property (nonatomic, retain) IBOutlet UITableView *tableNota;
#property (nonatomic, strong) NSMutableArray * arrayNota;
- (IBAction)AddNota:(id)sender;
#end
code of .m
- (void)viewDidLoad{
[super viewDidLoad];
self.textonota.delegate = self;
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return NO;
}
- (IBAction)AddNota:(id)sender {
[arrayNota addObject: textonota.text];
[tableNota reloadData];
}
//TableView------------------------------------
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [arrayNota count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (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] init];
}
cell.textLabel.text = [arrayNota objectAtIndex: indexPath.row];
return cell;
}
//----------------------------------------------
#end
No more errors but the button doesnt do anything
You can set up a NSMutableArray property that contains all the cells you want to add to the tableView.
When that button is pressed, you create a cell with a tag, and set text of that cell to the text of your UITextField:
cell.textLabel.text = aUITextField.text;
then add the cell into that NSMutableArray, and call [tableView reloadData] to refresh the table view.
In cellForRowAtIndexPath:indexPath delegate, you can use that tag to determine which cell in NSMutableArray to return, for example:
[return [self.cellArray objectAtIndex:indexPath.row]];
Hope this helps! :)
You should keep mutable array with strings. And use this array for fill content of tableview as data source. In delegate method - (UITableViewCell*) tableView:cellForRowAtIndexPath: you will create UITableViewCell if needed like this:
- (UITableViewCell*) tableView: (UITableView*) tableView
cellForRowAtIndexPath: (NSIndexPath*) indexPath
{
UITableViewCell* cell = [tableViewdequeueReusableCellWithIdentifier: #"identifier"];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: #"identifier"] autorelease];
}
cell.textLabel.text = [myMutableArray objectAtIndex: indexPath.row];
return cell;
}
- (NSInteger)tableView: (UITableView*) tableView numberOfRowsInSection:(NSInteger)section
{
return [myMutableArray count];
}
- (IBAction) pressButton: (id) sender
{
[myMutableArray addObject: textField.text];
[myTableView reloadData];
}
Every book for iOS developers contains information about UITableView.
Please read https://stackoverflow.com/questions/3403049/best-book-resources-for-learning-ios-programming

cellForRowAtIndexPath returns null but really need similar method

I have a UITableView in my UITableViewController (lol, obviously) but I need to get a cell at a given index inside the - (void)viewWillAppear:(BOOL)animated method.
Now, my cells are static and I create them in the interface builder. If I call
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:previously_selected_cell.integerValue inSection:0]];
it returns null for the cell. I only have 3 static cells in 1 sections. I tried both sections 0 and 1 and both return null.
Currently I have removed the - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method because if I add it, it will clear the UITableView of all my static cells.
Is there a method I can call in - (void)viewWillAppear:(BOOL)animated that will return a cell at a given index?
Thanks in advance!
EDIT: I checked out this stackoverflow question but I'm using static cells without cellForRowAtIndexPath so that question didn't help. :(
EDIT2: I'm trying to set the accessory type of the cell when the view loads. But only on a certain cell, that cell being the one the user selected before he quit the app.
#import "AutoSyncSettings.h"
#import "CDFetchController.h"
#implementation AutoSyncSettings
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
CDFetchController *cdfc = [[CDFetchController alloc] init];
NSFetchedResultsController *results = [cdfc getFetchedResultsControllerWithEntityName:#"SETTINGS"];
NSArray *objects = [results fetchedObjects];
NSNumber *sync_setting;
if(objects.count > 0)
{
NSManagedObject *object = [objects objectAtIndex:0];
sync_setting = [object valueForKey:#"wifi_setting"];
NSLog(#"(Settings)sync_setting: %#",sync_setting);
NSLog(#"(Settings)sync_setting int value: %i",sync_setting.integerValue);
NSLog(#"(Settings)TableView: %#",self.tableView);
//cell is null, even after this.
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:wifi_settings.integerValue inSection:0]];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
//cell is still null. WHY OH WHY? :(
objects = nil;
}
cdfc = nil;
results = nil;
objects = nil;
sync_setting = nil;
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
for (int i = 0; i < [tableView numberOfRowsInSection:indexPath.section]; i++)
{
if([[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:indexPath.section]] accessoryType] == UITableViewCellAccessoryCheckmark)
{
[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:indexPath.section]].accessoryType = UITableViewCellAccessoryNone;
}
}
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
CDFetchController *cdfc = [[CDFetchController alloc] init];
NSFetchedResultsController *results = [cdfc getFetchedResultsControllerWithEntityName:#"SETTINGS"];
NSManagedObjectContext *context = [results managedObjectContext];
NSArray *objects = [results fetchedObjects];
if(objects.count > 0)
{
NSManagedObject *object = [objects objectAtIndex:0];
NSNumber *sync_setting = [NSNumber numberWithInt:indexPath.row];
[object setValue:sync_setting forKey:#"sync_interval"];
[object setValue:[NSNumber numberWithInt:0] forKey:#"id"];
[ErrorHandler saveMoc:context];
}
else
{
//INSERT NEW OBJECT
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:#"SETTINGS" inManagedObjectContext:context];
NSNumber *sync_setting = [NSNumber numberWithInt:indexPath.row];
[object setValue:sync_setting forKey:#"sync_interval"];
[object setValue:[NSNumber numberWithInt:0] forKey:#"id"];
[ErrorHandler saveMoc:context];
}
}
#end
I have a project doing exactly this and it works perfectly. However, it doesn't work unless you call [super viewWillAppear:animated] before trying to access the cells in this manner.
The base implementation presumably loads in the cells from the storyboard.
I want to add a checkmark accessory to the previously selected cell,
the previously_selected_cell variable gets stored even if the app
quits/crashes
The way to go will be to control the indexPath in your cellForRowAtIndexPath implementation and act if it's equal to previously_selected_cell.integerValue:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// usual cache lookup, allocation of the cell, etc
if (indexPath.row == previously_selected_cell.integerValue) {
// add checkbox here
} else {
// remove checkbox
}
}

How to Programmatically Create UITableView and Populate with NSArray in Xcode 4.2?

I have been struggling to understand the difference between Storyboards and coding in Objective-C. It appears you can create a UITableView by both dragging the object into your Storyboard, or coding in a new view within Objective-C.
The problem is that I want to keep my Storyboard as slim as possible. So I'm trying to build and populate a UITableView with an NSArray of 5 strings. My code will only run 1 row before returning a compiler error... I am going to scrap the whole project and start fresh.
I would be very grateful if somebody familiar with the new Xcode 4.2/iOS5/Storyboards can provide a reasonable solution for building the UITableView. I know this is such a basic task which is why it's so frustrating to begin with. I can get the Table View working, but I cannot seem to get an Array to dynamically fill and create #X number of rows...
Let me know if I can provide any more info. I've tried to be as straightforward as possible - just need to get a TableView working and populate with an Array :)
EDIT - here is my project source code you can download to check out where I'm at.
The reason it crashes is that in the storyboard you have to change the tableview to dynamic prototypes instead of static cells.
For some reason Static Cells is the default setting. Once you get the hang of the Storyboards it's great, especially when dealing with tableviews. Your initial View is set up as the NavigationController which has your MasterviewController as the RootViewController, so it's being loaded as the firstView. Click on the TableView in the MainStoryboard and change the Cels to Dynamic Prototypes or it will use the static ones that you create right in the storyboard. You can make custom cells right on the tableview in the storyboard. One more thing to note is the re-use identifier has to be set to the same name in the storyboard and the TableViewController.
You can also just up the count of static cells to the number you want if you know it will always be the same.
Here's a trivial sample with subclassing UITableViewController populated with an NSArray (NSMutableArray) from my sample code. It doesn't use story boards but you said that's OK in your comment. Hopefully my sample code helps you.
Header:
#interface MainTableViewController : UITableViewController
{
NSMutableArray *_items;
}
#end
Implementation:
#implementation MainTableViewController
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Lifetime
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self)
{
// datastore
_items = [[NSMutableArray alloc] init];
for (int index=0; index < 5; index++)
{
[_items addObject:[NSString stringWithFormat:#"item #%d", index]];
}
}
return self;
}
- (void)dealloc
{
[_items release];
[super dealloc];
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Table View DataSource
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// a typical table has one section
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// number of rows
return [_items count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// NSIndexPath contains an array of indexes. For UITableView:
// indexAtPosition:0 is the section number
// indexAtPosition:1 is the row number
// create an identifier for this type of cell
static NSString *CellIdentifier = #"Cell";
// get a cell of this type from the re-use queue or create one
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
NSString *title = [_items objectAtIndex:[indexPath indexAtPosition:1]];
[[cell textLabel] setText:title];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
return cell;
}
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Delete the row from the data source
NSLog(#"delete section: %d rol: %d", [indexPath indexAtPosition:0], [indexPath indexAtPosition:1]);
[_items removeObjectAtIndex:[indexPath indexAtPosition:1]];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert)
{
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
NSLog(#"insert section: %d rol: %d", [indexPath indexAtPosition:0], [indexPath indexAtPosition:1]);
}
}
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
NSString *fromItem = [_items objectAtIndex:[fromIndexPath indexAtPosition:1]];
[_items removeObjectAtIndex:[fromIndexPath indexAtPosition:1]];
[_items insertObject:fromItem atIndex:[toIndexPath indexAtPosition:1]];
}
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark UITableViewDelegate
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"selected section: %d rol: %d", [indexPath indexAtPosition:0], [indexPath indexAtPosition:1]);
// get the selected cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
// navigate to detail
DetailedTableViewController *detailedView = [[DetailedTableViewController alloc] init];
[[self navigationController] pushViewController:detailedView animated:YES];
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark View lifecycle
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
[[self navigationItem] setRightBarButtonItem: [self editButtonItem]];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end

typical UITableView unrecognized selector sent to instance

I'm having this "tableView:cellForRowAtIndexPath:]: unrecognized selector sent to instance" error, I'm almost sure is some kind of memory management issue but right now I'm pretty much tired of having to find the bug.
The TableView displays the cells well as long as you don't scroll down, if you do..
app crashes.
The only property I use is called "places", I've already checked if I didn't miss a "self.".
so.. here's my code:
#import "PlacesViewController.h"
#import "FlickrFetcher.h"
#import "SinglePlaceViewController.h"
#implementation PlacesViewController
#synthesize places;
- (NSArray *)places
{
if (!places) {
NSSortDescriptor *content = [[NSSortDescriptor alloc] initWithKey:#"_content" ascending:YES];
NSArray *unsortedPlaces = [FlickrFetcher topPlaces];
places = [unsortedPlaces sortedArrayUsingDescriptors:[NSArray arrayWithObjects: content, nil]];
}
return places;
}
#pragma mark -
#pragma mark Initialization
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"Top Places";
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return YES;
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return self.places.count;
}
- (NSDictionary *)placeAtIndexPath:(NSIndexPath *)indexPath
{
return [self.places objectAtIndex:indexPath.row];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"PlacesTableViewCell";
NSLog(#"%#", [FlickrFetcher topPlaces]);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
NSString *location = [[self placeAtIndexPath:indexPath] objectForKey:#"_content"];
NSArray *splitLocation = [location componentsSeparatedByString:#", "];
cell.textLabel.text = [splitLocation objectAtIndex:0];
if (splitLocation.count == 2)
cell.detailTextLabel.text = [splitLocation objectAtIndex:1];
if (splitLocation.count == 3)
cell.detailTextLabel.text = [[[splitLocation objectAtIndex:1] stringByAppendingString:#","] stringByAppendingString:[splitLocation objectAtIndex:2]];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
SinglePlaceViewController *spvc = [[SinglePlaceViewController alloc] init];
// Pass the selected object to the new view controller.
spvc.placeId = [[self placeAtIndexPath:indexPath] objectForKey:#"place_id"];
[self.navigationController pushViewController:spvc animated:YES];
[spvc release];
}
#pragma mark -
#pragma mark Memory management
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
- (void)dealloc {
[places release];
[super dealloc];
}
#end
Thank You very much for your help and I'm really sorry for making you all guys do a bit of work for me.
You didn't provide much info about the error so i can only guess:
The error indicates that the object that get the message cellForRowAtIndexPath: don't actually have this method implemented.
But since you DID implemented it, the only reason i can think of is that you are messing with your tableView's "dataSource" property and changing it to something you shouldn't.
Make sure that the dataSource is your PlacecViewController.
From what it looks like to me, the getter method for your places property is not retaining the value it returns. You are setting it to an autoreleased instance of NSArray you're getting from the sortedArray method. I'm willing to bet it's being released after it displays your initial tableview cells.