Tuesday, September 22, 2009

Using NSNotificationCenter with UINavigation Controllers

When presenting an app to user with a large list of information, most developers will add in the ability to search the list inside of the table view.  To further increase usability it is a good practice to include different filters.  For example, a scope button that allows the user to filter by name, address, phone number, etc.  Implementing these features is pretty straight forward.  However, one of the filter options that was needed for a particular app that I am working on was to filter by department.

The complexity to this problem came in the fact that the search term couldn't be the department name, but the code.  Obviously, most users don't know the corresponding codes for a given department.  So I needed a way to present a list of "friendly" department names to the user to pick from, and once a given department was chosen then the corresponding department code would be populate the search bar field.

The first attempt was to use a UIPickerView.  Unfortunately, this wasn't the best solution mainly because you can't use key/value pairs with the UIPickerView.  Well...not in a very straight forward manner.  My second solution attempt was to use a modalViewController and then pass the selected object back to the parent controller.  Everything worked out just fine, but the parent object property wasn't being updated.

To solve this problem there are a couple of schools of thought.

  1. Set the parent controller as the delegate and setup a new protocol that the child controller implements.
  2. Use the NSNotificationCenter which uses the Observer Pattern to update another object based upon some action/method.
Most of the time I would say that the second option is overkill since it is designed for a one-to-many object broadcasts, but in this use case it is the preferred approach because "you would otherwise have to pass instances around of delegates just to connect objects". - http://alexvollmer.com/index.php/2009/06/24/cocoas-ways-of-talking/

FINAL SOLUTION:
// Parent UIViewControlller

- (void)viewWillAppear:(BOOL)animated {

  [[NSNotificationCenter defaultCenter] addObserver:self
                                        selector:@selector(refreshSearchBarControllerWithDepartmentCode:)
                                        name:@"DepartmentCodeNotification"
                                        object:nil];
 
  [super viewWillAppear:animated];
}


- (void) refreshSearchBarControllerWithDepartmentCode:(NSNotification *)notification {

  NSString *key;
  NSString *trimmedSearchString;

  for (key in [notification userInfo]) {
    trimmedSearchString = [[[notification userInfo] valueForKey:key] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  }
 
  self.searchDisplayController.searchBar.text = trimmedSearchString;

  [self.searchDisplayController.searchBar becomeFirstResponder];
  [self.searchDisplayController.searchResultsTableView reloadData];
}

- (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope {

  if (selectedScope == DEPARTMENT_SCOPE_TITLE_INDEX && [[self.searchDisplayController.searchBar text] length] <= 2) {
   
    DepartmentsViewController *dvController = [[DepartmentsViewController alloc] initWithNibName:@"DepartmentsView" bundle:[NSBundle mainBundle]];

    [self presentModalViewController:dvController animated:YES];

    [dvController release];
   
  } else if ([[self.searchDisplayController.searchBar text] length] >= 2) {
   
    NSString *trimmedSearchString = [[self.searchDisplayController.searchBar text] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

    [self getEmployees:trimmedSearchString method:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:selectedScope]];

    [self.searchDisplayController.searchResultsTableView reloadData];
  }
}

//Child UIViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

  NSString *code        = [self.tableView cellForRowAtIndexPath:indexPath].detailTextLabel.text;
  NSString *description = [self.tableView cellForRowAtIndexPath:indexPath].textLabel.text;
 
  NSDictionary *searchDepartmentCode = [NSDictionary dictionaryWithObjectsAndKeys: code, description, nil];

  [[NSNotificationCenter defaultCenter] postNotificationName:@"DepartmentCodeNotification"
                                                      object:nil
                                                    userInfo:searchDepartmentCode];

  [self dismissModalViewControllerAnimated:YES];
}

END RESULT:
After a user selects the appropriate department, the child controller is dismissed and the search bar text is updated with appropriate department code.

Posted via email from Cory Wiles Blog

No comments: