3

Using storyboard I currently have a static Uitableview. When a user selects a row from the Parent table view a new table view is displayed showing some options. Once the user selects a row from the new table view I want to dismiss the table and display the parent view controller back and have the user selection displayed in the cell. Pretty much a radio selection for a form.

Do I handle the dismissing of the view controller and passing of the data within the view will appear or did select row at index path? I been stuck on this for a while.

vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
GOAT
  • 601
  • 2
  • 13
  • 25

3 Answers3

9

Use delegation. The child view controller defines a delegate protocol that the parent view controller implements. Before the parent view controller displays the child view controller, it sets itself as the delegate of the child view controller. When the user has selected a row or what ever in the child view, the child view controller calls a method on its delegate and dismisses itself.


I wrote a sample code for you: https://github.com/vikingosegundo/StateSelection

The Parent View Controller is the MasterViewController.
The Child View Controller is the StateSelectionViewController.

  • The StateSelectionViewController defines a protocol in it's header: StateSelectionDelegate and has a delegate property id<StateSelectionDelegate> delegate.

    @protocol StateSelectionDelegate <NSObject>
    -(void) selectedState:(NSString *)state forNation:(NSString *)nation;
    @end  
    
  • MasterViewController conforms to this protocol, it implements the only delegate method selectedState:forNation:.

    -(void)selectedState:(NSString *)state forNation:(NSString *)nation
    {
        self.statesDictionray[nation] = state;
        [self.tableView reloadData];
    }
    
  • During MasterViewController's prepareForSegue:, it sets itself a delegate of the destination view controller, which happen to be StateSelectionViewController.

    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {
        if ([[segue identifier] isEqualToString:@"showDetail"]) {
            NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
            NSString *selectedNation =[[[self.tableView cellForRowAtIndexPath:indexPath] textLabel] text];
            [[segue destinationViewController] setSelectedNation:selectedNation];
            [[segue destinationViewController] setDelegate:self];
        }
    }
    
  • Now the segue is executed and the StateSelectionViewController's tableview gets displayed.

  • Once the user tabs on one of the rows, StateSelectionViewController will call

    [self.delegate selectedState: <theState> forNation: <theNation>];
    

    and dismisses or pops itself. Note the switch to determine the way the controller was presented.

    -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
           [self.delegate selectedState:stateDictionary[_selectedNation][indexPath.row] forNation:_selectedNation];
           if(self.presentingViewController)
               [self dismissViewControllerAnimated:NO completion:NULL];
           else
               [self.navigationController popViewControllerAnimated:YES];
    
    }
    
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
5

Next to my other answer I want to show another way that I wasnt aware of till just few minutes ago: Using an unwinding segue instead of delegation.

An unwinding segue will some how reverse the segue that brought us to a view controller. "Some how" means that you actually dont need to return to the previous controller, but a previous one. When creating a unwinding segue you connect it with a target of the signature

-(IBAction)actionName:(UIStoryboardSegue *)segue

in one of the former view controller. For details see here: Using Xcode Storyboarding
The segue the action is called with will contain the source controller, in our case the detail view controller. We can now access an property or method we defined on it.

@interface StateSelctionViewController : UITableViewController
@property (nonatomic, strong) NSString *selectedNation;
@property (nonatomic, strong, readonly) NSString *selectedState;
@end

I made the selectedState readonly so that it is clear that this cant be set but should be read after selection. I re-declare it readwrite in a class extension.

@interface StateSelctionViewController ()
@property (nonatomic, strong, readwrite) NSString *selectedState;
@end

in the storyboard i created a manually unwinding segue as shown in Technical Note TN2298: Figure 2
As soon as a row is selected, I will set selectedState and perform the unwinding segue

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    self.selectedState =  stateDictionary[_selectedNation][indexPath.row];
    [self performSegueWithIdentifier:@"unwind" sender:self];
}

This will execute the hooked up action in the Master View Controller

-(void)returned:(UIStoryboardSegue *)sender
{
    NSLog(@"%@", NSStringFromSelector(_cmd));
    [self.statesDictionray setObject:[sender.sourceViewController selectedState]
                              forKey:[sender.sourceViewController selectedNation]];
    [self.tableView reloadData];
}

I created a sample code: https://github.com/vikingosegundo/StateSelectionUnwind

Community
  • 1
  • 1
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
  • Thank you, both worked for me. I see you're using a Dictionary to store the states. If I add a search bar to the state view controller, I should be able to search through the options available right? – GOAT Sep 26 '13 at 15:21
  • what version are you using now: delegation or this? – vikingosegundo Sep 26 '13 at 15:32
  • I'm using the delegation @vikingosegundo – GOAT Sep 26 '13 at 18:01
  • @EricOboite: check out the new version of the delegate example. it contains a basic filtering. – vikingosegundo Sep 26 '13 at 18:46
  • thanks that worked! Can you take a look at my recent post http://stackoverflow.com/q/19042726/1568886 . Should I subclass UiTableViewCell for this? @vikingosegundo – GOAT Sep 27 '13 at 13:23
1

To expand on Vikingosegundo's suggestion:

When your first view controller segues to your second view controller, implement a prepareForSegue method.

Give your second view controller a delegate property, that conforms to a parentVC protocol.

Define methods in your parentVC protocol that let you notify the delegate about the currently selected table view cell.

In your first view controller's prepareForSegue method, set yourself up as the delegate of the second view controller.

When the user changes cells in the second view controller, use the delegate property to notify the parent view controller.

I have a sample app on github that illustrates this exact setup. Mine is set up to use parent/child view controllers and embed segues, but you could use the same approach for modal presentation, navigation controller based navigation, or various other app structures.

The repo has the totally useless name "test". You can get to it at https://github.com/DuncanMC/test

Duncan C
  • 128,072
  • 22
  • 173
  • 272