Pass data from modal view back to parent in the iOS SDK

I recently came across a problem using the newest version of XCode (4.4), in which we can use the fantastic Storyboards feature. The question was simple - how do I present a modal view, ask the user for some data, and then return that to the parent view? In the past it was a pretty simple matter, but for me passing it back along via a navigation controller turned out to be slightly more complex that I predicted. Luckily the code to fix this issue is pretty concise and very easy to understand. Let's take a look.

Here's what we're dealing with in terms of the view setup:

First thing's first, give your modal segue an identifier like so:

Make this descriptive, for my app the modal view is used to add an order. And now we need to get our hands dirty and start coding. Jump to the modal view's header file and add this property:

@property (nonatomic, assign) id delegate;

This will allow us to assign a delegate to our view, in our case will allow the parent view to tell the modal view that it is the delegate, and therefore all data should be passed back to our parent. Note: Don't forget to @synthesize the above property in your .m file.

Now let's imagine you want to pass back the contents of an input box when a button is tapped, I'll assume you've setup a method called didFinishEnteringData:sender - a fairly common looking method name as generated by XCode. Here's the code you would use inside of this method:

- (IBAction)didFinishEnteringData:(id)sender {
    [self.delegate setInput:myInput.text];
    [self dismissModalViewControllerAnimated:YES];
}

And voilà! We are now talking to our delegate. But hang on, we need to have our parent assign itself as the delegate before the modal view is presented. And for that we need to jump to our parent view - an excellent point here is that the method applies to the storyboard configured method of presenting a modal view, rather than a programmatic approach. Here's the code:

-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {    
    if([segue.identifier isEqualToString:@"NewOrder"]){

        AddOrdersViewController *vc = (AddOrdersViewController *)[[[segue destinationViewController] viewControllers] objectAtIndex:0];
        
        [vc setDelegate:self];

    }
}

Notice the use of the identifier we configured before. Also note the modal view's class name, and the need to #import the header file of the modal view's class.

And that is how to simply access the parent to send data back from a modal view!

Search a UITableView

Note: This article has been marked for a quality review and will soon be updated.

As with most things there are a bunch of tutorials on the internet about how to search a UITableView, but when I was attempting to implement my search I hit a brick wall with it and ended back where I started - Apple's developer docs - the code from which will be used in this here tutorial. I trawled through many tutorials and cannibalised a load of code, but finally I tamed the beast and got myself a lovely search bar. So this post is as much a form of catharsis for me, as it is a tutorial for you 🙂 Let's get started.

To begin we need to think about what we need for this - we want a search bar, we need a search button, we may even need some scope titles to allow us to filter our results. Something which might not be too obvious to begin with is the fact that we also need to think about the UITableView in which to load our results. When we've finished loading this UITableView, it will display it to the user to scroll through. In terms of variables we also need to consider the fact that we will need a duplicate of our main data - be it an array or dictionary, so that we can store our search results. Lastly we need to store the search term and selected scope so that if our users switch to another view we can retain that information. All of this code is available from Apple, but I'll show you it here in a real world implementation - they use their own "Product" class, but I'll show you how to accomplish search with an NSDictionary and NSArray. Now at this point you may be wondering what you've let yourself in for, after all I've just gone through a rather long list of UI elements that are required to search our table, but luckily for us Apple provides a UISearchDisplayController class, which handles the UI, and expects only the back-end implementation to be written by us. So I'm going to create a new view-based project and get started.

My data structure will be as follows: I will have an NSArray populated with NSDictionary objects holding all my data, I will then populate my table-view with that data, and implement the search functionality based on that structure. If you want to use some other type of object, just replace all mention of NSArray etc. You should also note that Apple's implementation places code inside a view controller based on the UITableView class, not the UIView class - this will affect what variables we need, and what we need to hook up in IB. In this tutorial we will put a UITableView within a UIView.

Go ahead and add a UITableView, and then add a "Search Bar and Search Display Controller" to your UITableView, and link up the dataSource and delegate parts, making sure to implement their relevant protocols - you know how to set up a UITableView right? That handy object, as you might be able to tell, handles our search bar, and the controller for our search. We don't need to link to these expressly in our code by creating variables and properties, but we will need to implement a couple of protocols - namely the UISearchDisplayDelegate, UISearchBarDelegate ones. The search controller that we added in IB automatically sets it's delegate, so there's no need to hook that up manually. So now let's set up our variables. I'm putting these in my .h file:

NSArray*listItems;
NSMutableArray *filteredListItems;
NSString *savedSearchTerm;
NSInteger savedScopeButtonIndex;
BOOL SearchWasActive;
IBOutlet UITableView tableView; // Be sure to hook this up in IB

The role of some of these variables will soon become clear. Don't forget to link the tableView variable up in IB before continuing. Then to finish up in the .h file we need to add our properties like so:

@property (nonatomic, retain) NSArray *listItems;
@property (nonatomic, retain) NSMutableArray *filteredListItems;

@property (nonatomic, copy) NSString *savedSearchTerm;
@property (nonatomic) NSInteger savedScopeButtonIndex;
@property (nonatomic) BOOL searchWasActive;

Notice the mutability of our filtered content array - this is very important as we will be changing the content of this data store each time the user performs a search. If you want to use a dictionary at this point, you know what to do. And with that we can wave goodbye to our header, and welcome our implementation. Once you've synthesised your variables, we need to look at the methods required by the protocols we promised to implement.

The first two we will write are fairly simple, they will be called when a search is performed:

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
    [self filterContentForSearchText:searchString scope:
			[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];

    return YES;
}


- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
    [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:
			[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
    return YES;
}

Those two bad-boys each call another function, and then exit while returning "YES". When a user types something in the search field the first function is called, it then passes what it knows to the function filterContentForSearchText:scope:. The second is called whenever the user selects a different scope, and does the same as the first. Great stuff, we only have 1 more function to implement our search and a few lines to put elsewhere a little later on. And this is the part that caught me out to begin with. For some reason Apple neglected to provided simple code concerned with searching something like an array, and instead they provide a separate class - maybe that's more common where they come from, but round these parts I'm rather partial to the ol' NSArray and NSDictionary. So I changed certain parts of the following code to allow the use of said objects in our table. Just so you know my dictionaries that are stored in my listItems array have the keys "title" and "type". This is what I got:

- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{	
	[filteredListItems removeAllObjects]; // Clear the filtered array.

	for (NSDictionary *item in listItems)
	{
		if ([scope isEqualToString:@"All"] || [[item objectForKey:@"type"]  isEqualToString:scope]  || scope == nil)
		{
			NSComparisonResult result = [[item objectForKey:@"title"] compare:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchText length])];
            if (result == NSOrderedSame)
			{
				[filteredListItems addObject:item];
            }
		}
	}
}

This function first clears the filtered array, and then uses fast enumeration to search through our main array for matches. If you don't know what fast enumeration is, in short it is a more efficient way of iterating of collections of data. Inside our loop we compare the scope to "All", and if we have a match we use some nifty functions to see if our search term matches what we have in our array. Notice the if statement also features two other parts - the second compares our "type" key - so maybe dinner or snack, to the scope buttons, and the last checks to see if the scope is nil - meaning we haven't set any buttons for our scope. So if we select the "Snack" scope button it will only compare items with a type set to "Snack".

This next bit of code is for your viewDidLoad method.

// create a filtered list that will contain products for the search results table.
//filteredListItems = [NSMutableArray arrayWithCapacity:[listItems count]];
filteredListItems = [[NSMutableArray alloc] initWithCapacity:[listItems count]];

// restore search settings if they were saved in didReceiveMemoryWarning.
if (self.savedSearchTerm){
    [self.searchDisplayController setActive:self.searchWasActive];
    [self.searchDisplayController.searchBar setSelectedScopeButtonIndex:self.savedScopeButtonIndex];
    [self.searchDisplayController.searchBar setText:savedSearchTerm];
                
    self.savedSearchTerm = nil;
}

[self.tableView reloadData];

Here we simply restore any data that we have retained if our user dismissed the current view, and then reload our table to reflect any changes. Now we need to update our viewDidDisappear to make sure we actually save the data we are using in the above function, so go ahead and add the following lines to your viewDidDisappear method:

self.searchWasActive = [self.searchDisplayController isActive];
self.savedSearchTerm = [self.searchDisplayController.searchBar text];
self.savedScopeButtonIndex = [self.searchDisplayController.searchBar selectedScopeButtonIndex];

And add this to your viewDidUnload method:

 self.filteredListItems = nil;

Now we need to think about our already-implemented UITableView methods - no you're not going mad, I've not written them here, but we need to change them up a bit. At the moment they all return data based on our main list called "listItems", this is okay if we need to display our table to users, but when in search mode, our functions will be returning the unfiltered data - let's get a fixin'.

The easy way to do this is to set up an if statement to detect if we are in search mode, and then return different data based on that information, that if statement looks like:

if (tableView == self.searchDisplayController.searchResultsTableView){
    // Search mode
}else {
    // Normal mode
}

So go ahead and add this to all your protocol methods; as an example your method for returning the number of rows in a section would look like:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (tableView == self.searchDisplayController.searchResultsTableView){
        return [self.filteredListItems count];
    }
    else{
        return [self.listItems count];
    }
}

Now we are ready to run it! Go ahead and hit run, and type something in the search bar, you should see a filtered list appear as you type. Now you may be getting a warning or two if you used the variable name "tableView", about the method parameters hiding your instance variables, in this case it's not a big deal, but it might be a good idea to change the name to something a little more descriptive in the future.

So what if we want to implement some scope buttons? Well now we've done all the work behind the scenes, we can jump into IB, and add some titles. So go to IB and select your search-bar. In the attributes inspector you should see a box that says "Scope titles", just above that is a check box that says "Shows Scope Bar" - check that, and use the +/- controls to add titles. Then if you want your scope bar to be visible all the time leave the check box checked, but if you want it to only appear when the search becomes active, uncheck the box. Now when you run the program you should see that a scope bar has appeared!

And with that, we have successfully added search to our app. If you have any issues with this code do let me know, and if you want the completed project use the link below.

Completed XCode 4 Project

Will you make it big?

It seems today that many people across the internet are infatuated with the idea of making it big - fair enough, surely that's what we all want right? But what is worrying is that may of these people are operating under the mis-conception that doing so will be easy. They read the stories of some of the biggest names in the web design industry and set about trying to become one of them. Now that's all well and good, but I find that so many of these hopefuls are attempting to make a large amount of money by usually replicating what others have created. It is important that such people understand that creating a new social networking site won't work - unless that is they have a new and really good idea. To make it big you have to have a totally original idea, and be able to implement it well. Many people have blogs (I am one of those people!), but few make it to the big time (I ain't one of them :(). Many people think they will be able to create the next best selling iPhone app, but few recognize the many difficulties they will face if they are poorly equipped.

The worst kind of person like this is a young student who thinks they know it all about website design. Unfortunately their websites are usually painfully recognizable because they were made from a tutorial. What many of these people fail to understand, is that without a proper education and experience in the field, they won't really be able to design great websites.

There are of course those who do make it big with little work. But usually they are one in a million. What I am trying to say is this: don't go about life thinking you will be able to pick up the skills you will need to create the next killer iPhone app; don't go around in life expecting ideas to simply flow into your mind. If you want to make it to the big time you are going to have to work hard and come up with something that solves a problem and that people like. For instance CSS Tricks, a website by Chris Coyier is very successful because the guy behind it is incredibly skillful at what he does, and people care about his opinion because they can see that he is as skilled as he is.

I don't mean to dampen your dreams or anything - just to make you have a more realistic outlook on life. Keep smilin'! 😉