Insert New UITableViewCell in Master from Detail - iphone

I have a simple master-detail application and I'm using the detail view to configure data to insert into the master list.
I have a custom class called "Calculation," which is the object I need to insert into each row.
I can pass the object from the detail view to the master view just fine, it just won't insert a new row.
In the detail view I Initiate my object, populate it with data and send it to the "insertNewObject" method in the MasterViewController using a custom segue:
- (IBAction)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSString *s = #"1";
NSNumber *n = [f numberFromString:s];
Calculation *calc = [[Calculation alloc] initWithValue:#"$1661.70" descr:#"1 Ounce 24k Gold" textInput:#"1" percentDisplay:#"0%" purityIndex:n metalIndex:n weightUnitIndex:n sliderValue:n];
[segue.destinationViewController insertNewObject:calc];
}
And in the MasterViewController, here is my insert code:
-(void)insertNewObject:(NSObject *)newCalculation {
if (!_objects) {
_objects = [[NSMutableArray alloc] init];
}
[_objects insertObject:newCalculation atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
NSObject *object = [_objects objectAtIndex:indexPath.row];
cell.textLabel.text = [object valueForKey:#"value"];
cell.detailTextLabel.text = [object valueForKey:#"descr"];
cell.textLabel.shadowColor = [UIColor blackColor];
cell.textLabel.shadowOffset = CGSizeMake(0, -1);
return cell;
}
I can print the object all day with NSLog in the MVC, but not matter how many times I add an object from the detail view controller, the array count is always 1, and no cell ever gets added to the tableview.
The same exact code works fine if I instantiate the object in MVC, and insert it.

A segue is really the wrong mechanism for sending data from detail to master. (It works great the other direction!) You'd be better off defining a delegate protocol that has -(void)insertNewObject:(NSObject *)newCalculation; as a required method, making your master implement that protocol, and passing it as a delegate to your detail controller.
Then the detail controller just calls [delegate insertNewObject:calc];.

Related

How to use delegation to communicate between 2 View Controllers? [duplicate]

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 8 years ago.
I have a Assignment ViewController and a TableViewController.
The assignment View Controller takes input and saves the information in an object.
What I need is , using delegation, alert the tableviewcontroller that an assignment was created, and have the tableviewcontroller add the object to a NSMutableArray, and archive it.
It seems easy but I am having a hard time understanding delegation.
Here is the save Method - AssignmentViewController.m :
- (IBAction)Save:(UIButton *)sender {
self.homeworkAssignment = [[Homework alloc] init];
self.homeworkAssignment.className = self.ClassNameField.text;
self.homeworkAssignment.assignmentTitle = self.AssignmentTitleField.text;
self.homeworkAssignment.assignmentDiscription = self.DiscriptionTextView.text;
self.homeworkAssignment.pickerDate = self.DatePicker.date;
NSMutableArray *MyHomeworkArray = [[NSMutableArray alloc] init];
[MyHomeworkArray addObject:self.homeworkAssignment];
NSString *filePath = [self dataFilePath];
//Archive my object
[NSKeyedArchiver archiveRootObject:MyHomeworkArray toFile:filePath];
}
My save method currently saves the info, adds to an array, and archives. But I need to use delegation between my TableViewController and my AssignmentViewController, and have my tableViewCOntroller alerted when save is pressed, and then add to the array and archive it itself.
Can someone please help me set this up correctly using delegation?
Add property for your array in UITableViewController:
#property (nonatomic,retain) NSMutableArray *homeworkArray;
now on save method:
-(IBAction)Save:(UIButton *)sender {
UITableViewController *tabVC = [[UITableViewController alloc] init];
tableVC.homeworkArray = self. MyHomeworkArray; // send a message to tableview
[self.navigationcontroller pushViewController:tableVC];
}
Now in TableViewController you need to set override your datasource method to show text on cell
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.homeworkArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if(!cell){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
//update your cell here
cell.textLabel.text = self.homeworkAssignment.className
cell.detailTextLabel.text = self.AssignmentTitleField.text;
return cell;
}

NSIndexPath associated with UITableView

I have a table view on my master view, and a textField on my detail view. The app is simple. Add a table view cell with some info. Then if you press the cell, it pushes the detail view and shows the cell's title/text in the text field. In the master view, im using an NSFetchedResultsController. And in the second view, im trying to load the data, but for some reason i am not using my own indexPath variable correctly, because every cell that gets pressed shows the first cells text on the detail view. Code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Set up the cell...
[self configureCell:cell atIndexPath:indexPath];
self.path = indexPath;
//I have tried this as well.
//self.path = [NSIndexPath indexPathForRow:indexPath.row inSection:0];
return cell;
}
Detail View:
-(void)viewWillAppear:(BOOL)animated
{
if (self.context == nil)
{
self.context = [(FBCDAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
NSFetchRequest *request = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"FailedBankInfo" inManagedObjectContext:self.context];
[request setEntity:entity];
NSError *error;
NSMutableArray *array = [[self.context executeFetchRequest:request error:&error] mutableCopy];
[self setTableArray:array];
FailedBankInfo *infoData = [self.tableArray objectAtIndex:self.root.path.row];
NSString *string = [NSString stringWithFormat:#"%#", infoData.name];
self.nameField.text = string;
string = [NSString stringWithFormat:#"%#", infoData.city];
self.cityField.text = string;
string = [NSString stringWithFormat:#"%#", infoData.state];
self.stateField.text = string;
[super viewWillAppear:YES];
}
Why not just pass the index path into the detail view controller in the
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
method, with a custom initializer:
- (id)initDetailViewControllerWithIndexPath:(NSIndexPath*)indexPath
?
I'm not familiar with a "root" property on a UIViewController, what is that? It seems to be an attempt to reference the first view controller from the detail view controller. Have you checked to see what self.root.path.row is returning?
Anyway the path property of your first view controller is independent of what ever cell is selected the way you have your code. It is simply set to the indexPath of the last cell that is loaded. If you did want to some how access this property from your detail view controller, it would have to be set in the didSelectRowAtIndexPath method, not the cellForRowAtIndexPath method to be accurate.
I think that if you set the path in didSelectRowAtIndexPath your way should work. But to write the custom initializer, add a property to your detail view controller called path, and then add a custom initializer to the detail view controller like this:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil andIndexPath:(NSIndexPath*)indexPath
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.path = indexPath;
}
return self;
}
and call this from the didSelectRowAtIndexPath method of the first view controller using
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:nil bundle:[NSBundle mainBundle] andIndexPath:indexPath];
[self presentViewController:detailViewController animated:YES completion:nil];
(if you're not initializing from a xib, customize which ever initializer that you're using).
Or, instead of passing in the index path, you could pass in an NSDictionary containing the data that you need in the detail view controller (the title and the text).

Inserting data from one table to another table

I have two UITableViewController classes, MainTableController and SubTableController.
From AppDelegate class I am calling MainTableController class.
At first this class is empty, and there is button named "show list" in this class.
When I click on this button I will go to SubTableController and there I have a list of actions in form of table.
Now if I choose to first cell action then that action name has to come on my first cell of table in MainTableController. But I am not able to print that name in table of MainTableController class.
In SubTableController:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ActionList * actionListObj = [appDelegate.actionArray objectAtIndex:indexPath.row];
self.chooseActions = actionListObj.actionName;
MainTableController * mainViewController = [[MainTableController alloc] init];
[mainViewController getAction:self.chooseActions];
[self.navigationController dismissModalViewControllerAnimated:YES];
}
In MainTableController:
-(void) viewWillAppear:(BOOL)animated{
[self reloadData];
}
-(void) reloadData{
[self.myTableView reloadData];
}
-(void) getAction: (NSString *) actionChoose{
self.action = actionChoose;
[self reloadData];
}
-(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] autorelease];
}
// Configure the cell...
cell.textLabel.text = self.action;
return cell;
}
When I debug, in MainTableController I am getting the action in getAction method but in table cell text string is null.
Can anyone please help me regarding this?Where am I going wrong?
You are allocating and initializing a new view controller each time you select a cell in SubTableController.
MainTableController * mainViewController = [[MainTableController alloc] init];
and of course, it isn't the one in place in the navigation stack.
You need to make these two controllers communicate.
I suggest that the sub view controller define a property on the main one, in order to message it when needed.
In SubTableController, add a property and synthesize it :
#property(readwrite, assign) MainViewController *mainViewController;
// and ...
#synthesize mainViewController;
Of course when you push the sub view controller, don't forget to set the property.
// in main view controller
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// alloc/init the sub VC
subVCInstance.mainViewController = self;
[self pushViewController:subVCInstance ......
Now when a row is selected in the sub one, message the main one, without alloc/init a new MainViewController object :
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ActionList * actionListObj = [appDelegate.actionArray objectAtIndex:indexPath.row];
self.chooseActions = actionListObj.actionName;
//MainTableController * mainViewController = [[MainTableController alloc] init];
[self.mainViewController getAction:self.chooseActions];
[self.navigationController dismissModalViewControllerAnimated:YES];
}
This should work just fine.
In didSelectRowAtIndexPath of the SubTableController class, you are allocating new MainTableController to send the data.
MainTableController * mainViewController = [[MainTableController alloc] init];
[mainViewController getAction:self.chooseActions];
You should not do like that. Because the existing mainview will be different from the newly allocated one. You should use delegate to give back the data. For more info on Protocols and delegates, see here
More example here

Cant bind data to a table view

I have retrieved data from Json URL and displayed it in a table view. I have also inlcuced a button in table view. On clicking the button the data must be transferred to a another table view. The problem is that i could send the data to a view and could display it on a label. But i couldnt bind the dat to table view ... Here's some of the code snippets...
Buy Button...
-(IBAction)Buybutton{
Product *selectedProduct = [[data products]objectAtIndex:0];
CartViewController *cartviewcontroller = [[[CartViewController alloc] initWithNibName:#"CartViewController" bundle:nil]autorelease];
cartviewcontroller.product= selectedProduct;
[self.view addSubview:cartviewcontroller.view];
}
CartView...
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
data = [GlobalData SharedData];
NSMutableArray *prod =[[NSMutableArray alloc]init];
prod = [data products];
for(NSDictionary *product in prod){
Cart *myprod = [[Cart alloc]init];
myprod.Description = [product Description];
myprod.ProductImage =[product ProductImage];
myprod.ProductName = [product ProductName];
myprod.SalePrice = [product SalePrice];
[data.carts addObject:myprod];
[myprod release];
}
Cart *cart = [[data carts]objectAtIndex:0];
NSString *productname=[cart ProductName];
self.label.text =productname;
NSLog(#"carts");
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [data.carts count];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 75;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"cellforrow");
static NSString *CellIdentifier = #"Cell";
ProductCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell ==nil)
{
cell = [[[ProductCell alloc]initWithFrame:CGRectZero reuseIdentifier:CellIdentifier]autorelease];
}
NSUInteger row = [indexPath row];
Cart *cart = [[data carts]objectAtIndex:row];
cell.productNameLabel.text = [cart ProductName];
return cell;
}
I am also getting the following error in the console
2010-06-11 18:34:29.169 navigation[4109:207] *** -[CartViewController tableView:numberOfRowsInSection:]: message sent to deallocated instance 0xcb4d4f90
Your view controller is autoreleased that's why you have the error in the console.
CartViewController *cartviewcontroller =
[[[CartViewController alloc]
initWithNibName:#"CartViewController"
bundle:nil] ***autorelease***];
You should store your view controller in a member variable.
This is depreciated:
cell = [[[ProductCell alloc]initWithFrame:CGRectZero reuseIdentifier:CellIdentifier]autorelease];
Use initWithStyle:reuseIdentifier: instead.
Autorelease isn't a convenience function. It has a specific use. You're only supposed to use it when you are immediately handing an object off to an another external object. Don't use autorelease unless the sending object longer cares if the receiving object lives or dies.
A good rule of thumb is if you ever refer to the receiving object again in the sending object, don't autorelease the receiving object.

IPhone SDK, Display an NSMutable array in Table view?

i have tryed to display my NSMutableArray in a Table View by following a tutorial. It has completley failed for some reason, i think i have a good idea why but cannot get around it, this is my code:
- (void) scoreSystem {
scoreArray = [[NSMutableArray alloc] init];
NSNumber *onescore = [NSNumber numberWithInteger:score];
[scoreArray addObject:onescore];
NSNumber *twoscore = [NSNumber numberWithInteger:score];
[scoreArray addObject:twoscore];
NSNumber *threescore = [NSNumber numberWithInteger:score];
[scoreArray addObject:threescore];
NSNumber *fourscore = [NSNumber numberWithInteger:score];
[scoreArray addObject:fourscore];
NSNumber *fivescore = [NSNumber numberWithInteger:score];
[scoreArray addObject:fivescore];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [scoreArray count];
}
- (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];
}
cell.textLabel.text = [scoreArray objectAtIndex:indexPath.row];
return cell;
}
I think it is because it wont let me link up everything properly in IB, it lets me put the data source and delegate to the Files owner, but then when i drag from the files owner to my view it says 'delegate' instead of 'view', i think its because i am doing it in the 'main window' not VC.
Is there any way round this?
Thanks!
harry.
You want to set the class that your code is in as the tableview's datasource. Create an instance of your class in IB (use the NSObject, and rename its class to YourClass).
This will create an instance of your class that will be available when the nib is decoded.
Then, control-drag from the tableview to your class, and set the datasource.
That's it! You should be able to set breakpoints in your -numberOfRowsInSection: method above, and see it called as soon as the table view comes in view. If you don't, check your connections and check for typos: the runtime is case-sensitive.
Well for some reason someone bumped this old thread. I might as well chime in. The reason this code has problems is because it is trying to set the text property of the cell to a NSNumber.
cell.textLabel.text = [scoreArray objectAtIndex:indexPath.row];
Try this instead:
cell.textLabel.text = [[scoreArray objectAtIndex:indexPath.row] stringValue];