This article is the final part of a 3 part series on how to create a simple iPhone app for beginners. And this app happens to be about rating scary bugs!
In the first part of the series, we created an app that contained a list of bugs in a table view.
In the second part of the series, we covered how to create a detail view for the bugs.
In this article, we’ll cover how to add new bugs, how to add an icon and default image to our project, and how to handle long-running operations.
So let’s wrap this app up!
Adding and Deleting Bugs
Everything’s working great so far, but so far this isn’t a very user-friendly app! I mean the first thing anyone would want to do is add their own bug, and so far the only way to do that is by editing code!
Luckily, since we wrote our DetailViewController to allow editing of bugs, and are using a UITableViewController for the RootViewController, most of the infrastructure is already in place! There are just four changes we have to make, but I’m going to explain them bit-by-bit to keep things easy to understand:
1) Set up navigation bar buttons
Inside MasterViewController.m, add the following lines of code to viewDidLoad:
self.navigationItem.leftBarButtonItem = self.editButtonItem; |
Here we set up some buttons in the navigation bar. Just like “title” is a special property in view controllers used by the navigation controller, “navigationItem” is another of those. Whatever you set for the leftBarButtonItem and the rightBarButtonItem will show up in the navigation bar when the navigation controller shows your view controller.
For the leftBarButtonItem, we use a special built-in button called “editButtonItem.” This button says “Edit” and toggles the UITableView between edit mode (where you can delete rows for example) and normal mode.
For the rightBarButtonItem, we create a button that the user can tap to create a new bug entry. It turns out there’s already a built-in system item for adding (that looks like a + symbol), so we go ahead and use that, and register the “addTapped:” method to be called when it’s tapped.
Note that you can also set these up in the Storyboard editor, but I set them up here in code to show you that you can do things this way too.
2) Implement tableView:commitEditingStyle:forRowAtIndexPath
Still in MasterViewController.m, uncomment tableView:commitEditingStyle:forRowAtIndexPath and replace the contents with the following:
if (editingStyle == UITableViewCellEditingStyleDelete) { |
This method is called when the user chooses to modify a row in some way. We check to see that the user is trying to delete the row, and if so we delete it. Note we have to remove it both from our data model (_bugs) AND notify the table view that one of the rows has been deleted, via deleteRowsAtIndexPaths.
3) Handle adding a new bug
Next add this new method to MasterViewController.m:
- (void)addTapped:(id)sender { |
We set things up in step 1 so that when the user taps the add button, this method gets called.
Here we create a ScaryBugDoc with some default values, and add it to the bugs array. Note we have to also update the table view so it knows there’s a new row.
Then we call some code to make the table view act as-if the user selected the new row, so we immediately go into the edit view for the new bug. We first select the row in the table view, then we manually perform the segue that we set up in the Storyboard editor.
Note that we never actually named the segue “MySegue” in the Storyboard editor earlier, so let’s do that as our final step.
4) Name the segue
Open MainStoryboard.storyboard and click on the arrow with an icon between the master and detail view controller – this is your segue.
Then in the fourth tab on the sidebar (the Attributes Inspector), set the Identifier to MySegue. This way we can manually run it when we want from code.
That’s it! If you compile and run the code, you should now be able to add your own bugs, such as this one:
Adding An Icon and Default Image
Ok, our app is looking pretty fun and amusing, let’s ship it and get rich!
Except it would be pretty embarassing if we did right now, I mean we don’t even have an icon!
Luckily, that is quite easy to fix. Earlier, we added an icon to our project file (logo1.png), from ExtraStuffForScaryBugs2.zip. Let’s set that as the icon for our project!
To do that, the easiest way is to select your ScaryBugs project in the Project navigator, and select your ScaryBugs target. Make sure the Summary tab is selected, and scroll down to App Icons. Control-click on the first icon and choose Select File:
Choose logo1.png from your project directory, accept any warnings, and you should see it show up. If you get a warning that the image size doesn’t match, resize logo1.png to 57×57 (an earlier download had the file larger than that).
Note that this is a shortcut for editing ScaryBugs-Info.plist, where your app settings like this are stored. You can verify this by opening ScaryBugs-Info.plist, and you should see the following:
You could have manually added these settings to this file, but I find using the GUI easier when it’s available.
Go ahead and delete and re-install the app on your simulator or phone, and you should see the new icon show up!
There’s one other thing we should fix as well. If you try running ScaryBugs, you might notice that after you tap the icon, there’s a pause before it shows up where just a black screen displays. That is kind of embarassing behavior – it looks like the app isn’t very responsive.
According to Apple’s documentation, the best thing to do is to display a screen that looks just like your app would, but without any data in it yet. That’s pretty easy to do. Just open up MasterController.m, and make the following change:
// Replace tableView:numberOfRowsInSection's return statement to the following: |
Then run the project on your device, and you’ll see an empty table view after it loads. In XCode, click on the Organizer button in the toolbar (the far right), click on your device, go to the screenshots tab, and click “New Screenshot” (the bottom right) to get a screenshot:
Then click Save as Launch Image. Make sure the ScaryBugs workspace is selected, keep the name as Default, and click Next. It will save the image to your project and set it as the launch image for you – handy, eh?
You can see that it’s set the image as the launch image by going to your target’s summary tab:
Then restore tableView:numberOfRowsInSection to the way it was and run the app again. You should see a default screen as it loads instead of a blank view, and the app should feel a lot more responsive!
Bonus: Handling Long-Running Operations
If you run the app on the Simulator, everything probably appears fine, but if you run it on your iPhone and go to tap a picture to change it, there is a LONG delay as the UIImagePicker initializes. After picking a picture, there is another long delay as the image is resized (especially if it’s large). This is a very bad thing, as it makes your application seem unresponsive to users.
The main rule to keep in mind is that you should never perform long-running operations on the main thread. We’re currently violating this rule in two places, which is why our app appears unresponsive.
What you should do instead is run long-running operations on a background thread. Ideally, the operation would be done in the background as the user continues to do other things. But if the work is required to occur before the user can continue (such as loading the image picker), at the very least you should display a loading indicator of some sort so the user understands that the app is working and not jsut broken.
So that’s what we’ll do here – run the long-running code on a background thread, and display a “loading” view on the foreground thread while we wait for the operation to complete.
The desire to display a loading view is a common problem for app developers, so a lot of people have created some activity indicator libraries that we can use to save ourselves some time doing it ourselves. I’ve tried a bunch of these, my current favorite is SVProgressHUD by Sam Vermette, so let’s try that. You can download a copy off the SVProgressHUD Github page.
Once you’ve downloaded SVProgressHUD, add SVProgressHUD.h and SVProgressHUD.m to your project under the “Views” group. You also have to perform two configuration steps:
- Add required library. Click your project in the Project Navigator and select your ScaryBugs target. Select the Build Phases tab and expand the Link Binary with Libraries section. Click the + button and add QuartzCore.framework.
- Compile without ARC support. SVProgressHUD is not currently ARC compatible, so we need to compile it without ARC support. To do this, expand the Compile Sources tab and double click the entry for SVProgressHUD.m. Enter -fno-objc-arc into the dialog box to compile it without ARC support.Update 7/15/12: SVProgressHUD now has ARC support so this step is no longer necessary. Thanks Juan for pointing this out!
At this point you should be able to build your project without errors.
Then make the following changes to DetailViewController.m:
// At top of file |
There’s a lot of new ideas here, so let’s go over this section by section.
- Here we use the SVProgressHUD helper class we just added to show a “Loading” GUI with a spinner on the screen. This way the user knows some work is going on and that the app hasn’t just locked up.
- We want to load the image picker in the background. You can do this on iOS with a technology called Grand Central Dispatch. We won’t go over it in huge detail here (but if you are curious, read this tutorial). For now, all you need to know is this line gives you a queue that you can use to run blocks of code in the background.
- This line executes a block of code to load the image picker in the background. If you are confused about the syntax of blocks, check out this tutorial.
- Finally, we present the picker on the main queue. Note you must always update the GUI on the main queue – you can’t do it on a background thread.
Similarly, we can perform the image resizing in the background as well. Replace imagePickerController:didFinishPickingMediaWithInfo with this:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { |
And that’s it! Give it a run on your device, and you’ll see a new animation while the long-running work takes place, which makes for a much nicer user experience.
Where To Go From Here?
Here is a sample project with all of the code we’ve developed in this tutorial series.
Congratulations – you have finished creating a simple Master/Detail app for iOS! You have dived through a crash course of putting an app together, and can dig into further areas of interest.
Here’s what I’d recommend you read next:
- The iOS Apprentice: iOS Tutorial Team member Matthijs Hollemans has written a detailed tutorial series on iOS development for beginners that covers everything you need to know to get started with iOS, from the ground up. You can get part one for free by signing up for our monthly iOS newsletter, or you can purchase the whole series in the raywenderlich.com store.
- Beginning Storyboards in iOS 5 Tutorial: To layout your user interface in iOS 5, you will typically use the Storyboard editor, so it is very important to learn how it works.
- Beginning ARC in iOS 5 Tutorial: It’s also important to understand how memory management is handled in iOS 5 (and how easy it is with ARC!)
- How To Use Blocks in iOS 5 Tutorial: If you’re new to blocks, you should check this tutorial out. It also gives some good additional practice with Storyboards.
- Multithreading and Grand Central Dispatch on iOS: It’s also important to learn how to run tasks in the background to keep your app responsive. This tutorial touched on it, but read this for more details.
- How To Submit Your App to the App Store: And of course when you finish your app, you’ll want to know how to get it onto the App Store! Read this tutorial for a step-by-step guide.
- And much more! We have tons of tutorials on iOS on this site. Click this link to see our entire list!
I wish you best of luck in your iOS adventures, and I hope you enjoyed making this ScaryBugs app! If you have any questions or comments, please join the form discussion below!
No comments:
Post a Comment