0

My root controller is a TableViewController with static cells and a navigation controller. Depending on which cell is selected I want to push another ViewController (with an embedded TableView) that will change its configuration accordingly (buttons enabled or not, cells data from a db table or an NSArray, etc.). Selecting some "Controller A" cells will call the "Controller B" or "Controller C" and pass some data.

The code I'm trying looks like this:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
   switch (indexPath.row) {
        case 0:
        {
            [self performSegueWithIdentifier:@"segueToType" sender:self];
        }
            break;
        case 1:
        {
            [self performSegueWithIdentifier:@"segueToType" sender:self];
        }
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{

if ([[segue identifier] isEqualToString:@"segueToType"])
{
    NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
    NSInteger rowNumber = selectedIndexPath.row;
    switch (rowNumber) {
        case 0:
        {
            TypeSelectController *selCon = [segue destinationViewController];
            selCon.myPet = self.myPet;
            selCon.sel = @"tipo";
            selCon.delegate = self;
        }
            break;
        case 1:
        {
            TypeSelectController *selCon = [segue destinationViewController];
            selCon.myPet = self.myPet;
            selCon.sel = @"razza";
            selCon.delegate = self;
        }
            break;
        default:
            break;
    }

} 
}

In this case, for example, I'm calling only the "Controller B", but depending on the selected cell of "Controller A", it configures itself in different ways (depending on "selCon.sel" string).

In the "Controller B", I have this kind of code:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([sel isEqualToString:@"tipo"]) {
    NSString *itemToPassBack = [tipi objectAtIndex:indexPath.row];
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack ofType:@"tipo"];
    [self.navigationController popViewControllerAnimated:YES];
} else if ([sel isEqualToString:@"razza"]) {
    NSString *itemToPassBack = [razze objectAtIndex:indexPath.row];
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack ofType:@"razza"];
    [self.navigationController popViewControllerAnimated:YES];
}
}

So again, depending on the string "sel" value, I'm returning different data to "Controller A".

When I select the first cell in Controller A, I correctly get the Controller B scene. Then I select a cell and go back to Controller A with correct data. Now if I try to select the second cell in Controller A and select a cell in Controller B, the navigation do not respond as I want to and I get the Controller C scene, then the Controller A and the following errors:

nested push animation can result in corrupted navigation bar

Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.

Unbalanced calls to begin/end appearance transitions for .

What am I doing wrong?

Aleph72
  • 877
  • 1
  • 13
  • 40
  • I'm not sure if there is enough code here to help you. what you have looks ok, except what are you doing with this line: [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack ofType:@"razza"]; I don't understand passing self back in addItemViewController delegate call. are you injecting that into your view controllers or navigation flow at all? – CocoaEv Aug 05 '12 at 17:15
  • That's part of the code needed to pass data back to Controller A using delegate method. I've used the code showed at this link: http://stackoverflow.com/questions/5210535/passing-data-between-view-controllers – Aleph72 Aug 05 '12 at 17:25
  • That example is good in theory but is not appropriate when you are using storyboards and segues. what do you need to do with "item to pass back?" - specifically when using protocols and delegates, you typically set a property or pass a property to a method back to the original vc. I'll write up a answer for you. – CocoaEv Aug 05 '12 at 17:38
  • 1
    @Aleph72 In answer to your comment, above, the child controller, B, does not necessarily need to pass itself back to the parent controller, A. I can imagine a few situations where you might need to, but generally I don't. – Rob Aug 05 '12 at 17:40
  • @Aleph72 A few questions: 1. I assume you are using push segues. 2. Are both controller B and controller C instances of `TypeSelectController`? 3. Can you show us your A controller's `addItemViewController` code? 4. I assume your B and C controllers are not, themselves, embedded in navigation controllers. – Rob Aug 05 '12 at 17:44
  • @CocoaEv The Controller A presents a list of personal details (ie. hair color, height, weight, etc.). When selecting "hair color", for example, the Controller B loads a list (from a db table) of all the colors available. Then when a color is selected, the Controller B should go away and the Controller A should show the chosen color in the "hair color" cell. – Aleph72 Aug 05 '12 at 17:59
  • @RobertRyan 1. yes. 2. No. Controller B is a TypeSelectController and Controller C is a DataSelectController. 3. if ([type isEqualToString:@"tipo"]) { myPet.tipo = item; } else if ([type isEqualToString:@"razza"]) { myPet.razza = item; } 4. I don't understand the question... – Aleph72 Aug 05 '12 at 18:03
  • @Aleph72 Your code is saying that both row 0 and row 1 are doing a `segueToType` and that `[segue destinationController]` will be of type `TypeSelectController`. Your narrative refers to a "controller C", but I'm seeing only two types of controller here, the parent controller and the `TypeSelectController`. Is that right? – Rob Aug 05 '12 at 18:22
  • @Aleph72 If you're getting controller C when you click on the second cell, it makes me wonder if you have, in addition to your `didSelectRowAtIndexPath`, segues defined between the static cells and the B and C scenes in your storyboard. Generally you do one (let the storyboard do the segues to your next scene, and do not have a `didSelectRowAtIndexPath`), or the other, but not both. – Rob Aug 05 '12 at 18:28

2 Answers2

0

quick example of using protocol and delegates in storyboard example. just hitting the highlights here because you are most of the way already.

Controller a (the parent).has a NSString property called category and one called partNumber. Has a tableview. row selected to initiate a segue. [self performSegueWithIdentifier:@"segueToType" sender:self];

  • (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

if ([[segue identifier] isEqualToString:@"segueToType"]) {
ControllerB *b = segue.destinationViewController; b.category = self.category; b.delegate = self; }

View Controller B

@protocol -(void)doSomethingWithPickedPartNumber:(NSString *)partNumber; @end

//setup tableview with partnumbers of the category passed in.

//row selected so we have the partNumber we want to send back.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *pn = [self.listOfParts objectAtIndex:indexPath.row];

 //options - set the variable in the parent or call a method in the parent
 self.delegate.partNumber = pn;
 //if you set the variable in the parent - you can check for it in the -(void)viewWillAppear  method and reconfigure the parent view if needed

 //or do this
 [self.delegate doSomethingWithPickedPartNumber:pn];

 //this is actually calling a method in the parent.  remember you don't have a view you can see yet, but its in memory and you can modify things so when the child pops back it will look how you want it.  Also note, you can name the protocol methods what ever you want, as long as they match up from the parent  to the protocol in the child.


 //then return to parent
 [self.navigationController popViewControllerAnimated:YES];

}

CocoaEv
  • 2,984
  • 20
  • 21
0

ok, another try at this.

  • lets use the hair color example. you have a table in controller a, with a static cell for hair color. lets say you display a text value for the color selected. cell.textLabel = self.hairColor. If it is blank, the user can touch the cell and open a new controller to select colors.

in the new view controller that you segue'd to, you set a property to tell it to look up "hairColor'. So it builds a list from a db called listOfHairChoices.

user selects the 3rd row which, so you set the property in the parent using your delegate reference like this. self.delegate.hairColor = [self.listOfHairChoices objectAtIndex:indexPath.row]; then pop dismiss the child vc and pop back to parent. [self.navigationController popViewCon..etc]

Then back in the parent, in -(void)viewDidAppear you just reload your tableview like this: [self.tableView reloadData]

now, your original tableview should display the value of the cell selected in the child view controller.

That is a straight forward way of doing it and requires logic at each step to determine which property you are working with and which needs to be set - but will work with your existing framework.

an alternative approach would be to create a model class of the data you are presenting, then pass the model to the child VC's - let them update the model and pass it back, then simply reload your table from the updated model after each transition. You may not be familiar with that approach, but it simplifies things and reduces all the conditional logic you have now for each row of the tableview.

hope this suggestion helps you with your project.

CocoaEv
  • 2,984
  • 20
  • 21
  • I already have a model class, so I think I'm doing something more than I should. Later tonight I'll try to make some changes according to what you said and let you know. For now thank you very much to both of you for your help. – Aleph72 Aug 05 '12 at 18:41
  • Ok I'm trying the model class approach. I'm already sending an instance of my model class (myPet) to child VC. Then in that VC I can simply update the model based on which cell has been selected. Now I have some doubts about how to send back to parent VC the updated model. – Aleph72 Aug 05 '12 at 19:35
  • Ok I did it. I didn't have to do anything strange to pass back the updated model. I simply updated it in the child VC and then popped it. Reloading the tableview in parent VC showed the updated data. The problems I had with the navigation going crazy seems to be solved. I'm marking this answer as the right one, even if both of you have helped me a lot. Thanks again. – Aleph72 Aug 05 '12 at 20:25