This article is the second part of a three-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 this second article, we’ll cover how to create a detail view so that we can view a larger picture of the bugs, rate them, and and change their pictures!
In the third and final part of the series, we’ll cover how to add new bugs, add an icon and default image to our project, and handle long-running operations.
So let’s get to making some bugs! After all, isn’t that what programming’s all about? :]
View Controllers, Oh My!
Now that we have a list of bugs, it would be nice to be able to tap on the bug to bring up a screen where we can edit the bug’s name or picture, and rate the bug.
Most of the time in iPhone apps, for every “screen” of the app you have a class that is the “View Controller” for that screen. Right now our MasterViewController appears on startup, which contains a table view. We want to make it so that when you tap a bug, it brings up the DetailViewController, and shows some info about the bug.
When we first ran the template, this was actually working, but when we changed the cells to dynamic rows tapping rows no longer brings up the detail view. We’ll fix that soon.
Each “View Controller” can contain multiple views. In our table view controller, we just had a single view – the table view. However in our details view controller, we’re going to need a bunch of views – we’re going to need a view for the name of the bug, a view for the image, a view for the rating, and several others.
Download Some Stuff!
Speaking of which – we’re going to need a 5-star rating view in this details screen – but the iPhone doesn’t come with one by default. However, I recently wrote a tutorial on How To Make a Custom UIView in iOS 5: A 5-Star Rating View, so we’ll just use the view from that tutorial again here.
Don’t worry about going throught that tutorial now (unless you feel like it) – instead you can just download the Extra Stuff for Scary Bugs package that I put together for this project.
Go ahead and download the file then:
- Create a new group named “Views” in XCode, and drag RateView.h/RateView.m to that group, making sure “Copy items into destination group’s folder (if needed)” is checked. This is the 5-star rating view code from the tutorial.
- Repeat for UIImageExtras, except drag them to a new group named “Helpers”. This is some helper code we’ll need to resize images a bit later on.
- Repeat for the three shocked face images made by my lovely wife, except drag them to a new group named “Art”. These are the images we’ll be using for the “stars” in our rating view for some humorous flair :]
- Repeat for the logo1.png, also drag that to the Art group. We’ll be setting that as the app icon later.
Laying Out Our Detail View Controller With the Storyboard Editor
Ok – now we’re finally ready to go! Open MainStoryboard.storyboard, and if you scroll to the far right you’ll see the Detail View Controller that the template made for us by default, with a “Detail view content goes here” label inside:
The Storyboard Editor provides a visual way to construct your user interface in XCode. You drag and drop UI elements onto your view, set up their properties the way you want, and you can even connect the elements to properties in your View Controller classes.
The easiest way to understand it is to try it out! First, click on the view controller and go to Editor\Canvas\Show Bounds Rectangles – this will make it easier to see how we’re laying out the controls on the screen.
Delete the label that says “Detail view content goes here” – we won’t be needing that!
Then in the panel to the right, on the bottom part, make sure the third tab is selected for the Object Library. Drag a UITextField, UIImageView, and a UIView onto the text screen and arrange them like the following (the text field is on top):
Then select the UITextField and in the sidebar top section, make sure the fourth tab (the Attributes Inspector) is selected, so we can change some properties.
Set the font to Custom\Helvetica\Bold\Size 18.0, the text alignment to center, the clear button behavior to “Appears While Editing”, the capitalization to Words like the following:
Then, switch over to the Size Inspector by clicking on the fourth tab, and set up the autosizing attributes like the following:
This will make it so that when our view gets rotated to landscape, the text field stretches across the screen to become wider.
Next let’s set up our UIImageView. In the fourth tab (Attributes Inspector) set the mode to “Aspect Fit”, and in the third tab (Size Inspector) and in the fifth tab (Size Inspector) set the autosizing attributes to the following:
This makes the Image View grow or shrink to fill the available space, while keeping its edges the same distance from the edge of the screen no matter what, and scale the image to fit the best it can within the space while maintaining the image’s aspect ratio.
For the UIView, go to the third tab (Identity Inspector) and set the set the Class Identity to “RateView” so our 5-Star Rating view shows up there. Then in the fifth tab (Size Inspector) set the autosizing attributes to the following:
This makes it so it stretches left to right, but always stays the same height.
So far, so good! Next we need to add a few more controls to the screen so that the user can tap the UIImageView area to change the picture.
There are a couple ways we can do this, but one easy way it to create an invisible button on top of the UIImageView, and set it up so that we get a callback when the button is tapped. We can also add a UILabel underneath the picture to say “Tap to Change Picture” if there is no picture set.
So drag a Round Rect Button from the library, and resize it to be the same exact size and postion as the UIImageView. To make it invisible, in the fourth tab (Attributes Inspector) change the type to Custom. Then in the third tab (Size Inspector) set the autosizing attributes to the following:
Finally, drag a UILabel from the library, place it in the middle of the UIImageView, and double click to edit the text, changing it to “Tap To Change Image.” Then change the text alignment to center. Also, drag the UILabel up a few positions in the XIB so it’s behind the UIImageView (the list goes from bottom to top):
Then in the fifth tab (Size Inspector) set the autosizing attributes to the following:
Before you move on, you can double check that you’ve gotten all of the autosizing attributes right by selecting the Detail View Controller, and changing the orientation from Portrait to Landscape:
If something isn’t right, don’t worry – just change it back to Portrait and double check the settings.
Phew! We’ve added all of the controls we need, so all we need to do now is hook everything up to their outlets in our class.
To do this, first bring up the Assistant Editor (second button under the “Editor” section in the top toolbar), and make sure it’s set to Automatic\DetailViewController.h:
Then control-drag from the UITextField down into DetailViewController.h, right before the @end. A popup will appear allowing you to hook the UITextField up to a property in your class. Name it titleField, and click Connect.
Repeat this for the image view (but connect it to an outlet named imageView) and the Rate View (but connect it to an outlet named rateView).
We also want to make it so when the button is tapped, a method gets called on our class. To do this, control-drag from the button right before the @end, like you did when connecting the other views. However, this time select Action as the connection type, name it addPictureTapped, and click Connect.
Notice how it by default selects the “Touch Up Inside” event for us. This is good because it means when the user’s finger moves up from the button (i.e. they tapped it), our method will be called.
You can connect other actions to callback methods too. For example, there’s an action on the text field when the text changes, and we want to get a callback when this happens.
To do this, control-drag from the UITextField right before the @end and also set it to Action. By default it sets the event to Editing Did End – change this to Editing Changed. Name the method titleFieldTextChanged, and click Connect.
The last thing we have to do is set our class as the delegate of the text field. Sometimes receiving callbacks on actions of a view isn’t enough – they might have other information to tell us about, and the text field is an example of this.
To do this, control-click on the Text Field, and drag a line from the little circle to the right of the “delegate” entry up to the Detail View Controller, and release.
At this point, your DetailViewController.h should look like this:
#import <UIKit/UIKit.h> |
You may notice some funky types above – IBOutlet and IBAction. These are “magic keywords” that the Storyboard Editor looks for, in order to allow us to associate controls that we add in interface builder to properties on our class. Basically, if we put an IBOutlet or IBAction next to a property/method, Interface Builder will detect it so we can hook it up later.
By creating these with the Storyboard editor, it already connected the properties to the controls for us automatically, but you can see the connections by control-clicking on one of the Detail View Controller. These things are called “outlets” btw.
We need to make some small tweaks to this to mark our view controller as implementing some delegates, adding a property for an image picker, and modifying our detailItem to mark it as specifically being a ScaryBugDoc, because that’s the detail item we are going to be displaying. So modify it to the following:
#import <UIKit/UIKit.h> |
OK finally done setting up the layout and header file – onto the implementation!
Implementing Our Detail View
We’re going to make a bunch of changes to DetailViewController.m. There’s a lot of code here, so let’s go over it party by part.
1) Import headers
// At top of file |
You should be a pro at this by this point!
2) Set up Rate View
// Replace configureView with the following |
In configureView (which is called from viewDidLoad), we set up the properties of our RateView. For more details, check out the How To Make a Custom UIView in iOS 5: A 5-Star Rating View tutorial.
3) Enable autorotation
// Replace the return statement in shouldAutorotateToInterfaceOrientation with: |
In shouldAutorotateToInterfaceOrientation, we return YES since we went to all the work of setting up the autosizing attributes in Interface Builder! This will allow the user to rotate this view between orientations, and our controls will re-layout according to the autosizing attributes we set up.
4) Set up initial UI state
// Add to the end of configureView |
Here we simply set up our GUI based on the bug that was selected.
5) Handle text view and rating view
- (IBAction)titleFieldTextChanged:(id)sender { |
We set up titleFieldValueChanged to be called whenever the user changes the value of the text field, so we update the model as well whenever it changes.
textFieldShouldReturn is called when the user hits the return key on the keyboard. We call resignFirstResponder to get the keyboard to disappear off the screen when that happens.
rateView:ratingIsChanged is called when the user chooses a new rating since we set ourselves as the RateView’s delegate, so when that happens we update our model.
In case you were wondering, the #pragma marks are just special lines that XCode can read to set up separators in the editor’s function list for organization’s sake:
6) Display image picker and process results
- (IBAction)addPictureTapped:(id)sender { |
We set up addPictureTapped to be called whenever the user taps the invisible button above the UIImage, so here we create the UIImagePicker (if it doesn’t exist already), and set the photo source to photo library (you can choose other things such as camera as well). We set ourselves as the delegate so we can get callbacks when the user finished picking the picture. Finally, we present the image picker as a modal view controller, which means it takes up the whole screen.
Finally, we implement the image picker callbacks for when the user picks an image or cancels. Either way, we dismiss the modal view controller. If the user did pick the image, we get the full image and also a thumbnail version (which we resize with the UIImageExtras class that we added earlier), and update both the model and the view.
OMG – you’re probably sick of writing code now eh? Don’t worry – we’re almost done, just gotta hook this baby in!
Integrating Our Detail View
This should be pretty quick. First, open MainStoryboard.storyboard, and select the table view cell in the Master View Controller. Control-drag from that cell over to the Detail View Controller, and a popup will appear asking if you want to connect them as a Push, Modal, or Custom. Select Push, and you should see an arrow connecting the view controllers:
Now we just need to make it pass the correct bug onto the detail view controller when a row is selected. To do this, open MasterViewController.m and make the following changes:
// Add to top of file |
First, note that in viewWillAppear, we reload the data in the table. This is because when the user is in the detail view, they might change the name of the bug or the picture, and we want the updated name/picture to show up when they come back to the table view. One easy way to do that is to reload the entire table, so we do that here.
Next, remember that we set things up in the Storyboard editor that whenever a row is tapped, it will push the Detail View Controller onto the stack. When this happens, the prepareForSegue will be called, so we have a chance to give the detail view controller any information it needs. In this case, we simply pass the selected bug on to display.
Finally, we’re done! Go ahead and compile and run your project, and if all goes well you should now be able to bring up the bug detail view, change the names of the bugs, change their pictures, rate them, and even rotate to any orientation!
Where To Go From Here?
Here is a sample project with all of the code we’ve developed so far in this tutorial series.
Please let me know if anything in the above is confusing or if you’d like me to go into more detail about anything.
In the final part of the series, we’ll cover how to add and delete bugs, add an icon and default image to our project, and correctly handle long-running operations!
No comments:
Post a Comment