Tuesday, 6 May 2014

Pickers [Got Views?]

In iOS a “picker” is a user interface that lets the user choose something from a predetermined set. You used the image picker in your MyStuff to choose a picture from the photo library, and a media picker to choose a songfrom the iTunes library in DrumDub. These are both big interfaces that take over the entire user experience.

iOS also supplies a couple of smaller picker view objects. Theres the specialty UIDatePicker, for choosing dates and times, and the customizable UIPickerView, for anything else. Both present a view containing a number ofvertical “wheels” that the user spins to choose the value or item they want, as shown in Figure 10-11.


Figure 10-11. Picker views

Date Picker

Use the date picker when you want the user to choose a date, time, or duration. The date picker has four different interfaces, controlled by its datePickerMode property. This can be set to one of the four values listed in Table 10-4.The four different modes are shown in Figure 10-12.


Table 10-4. Date picker modes

Mode                                                              Description
UIDatePickerModeTime                                Choose a time of day UIDatePickerModeDat
                                                                       Choose a calendar date 
UIDatePickerModeDateAndTiz                   Choose a date and time
UIDatePickerModeCountDownTimer          Choose a duration (hours and minutes)           

Figure 10-12. Date picker modes

The pickers date property reports the value the user has selected. Setting it changes the date/time in the view. If you want to set the date and have the “wheels” spin to their new positions, send
-setDate:animated:. The time portion of the date property is 0:00 when using the date-only interface. Similarly, the calendar day of the date property is meaningless when using the time-only or duration interface.

If you want to limit the range of values the user can choose from, set the minimumDate and/or maximumDate properties. For example, to force the user to choose a day in the future, set the minimumDate to tomorrow.

You can also reduce the granularity of time choices with the minuteInterval property. When set
to 1, the user can choose any time or duration in one-minute increments (2:30, 2:31, 2:32, anso on). Setting minuteInterval to 5 narrows the users choices to 5-minute intervals (2:30, 2:35, 2:40, 2:45, and so on).

If you plan on using date picker, and your interface leaves the picker visible while time progresses, Apple recommends updating the picker in real time. For example, if your interface uses a duration picker and a start button,pressing the start button will probably cause some timer in your app to begin counting down. During that time, your app should periodically update the picker so it slowly (once a minute) changes as the time counts down tozero.


Anything Picker

What if you dont need to pick a date or a time? What if you need to pick an ice cream flavor, a model of car, or an arch nemesis? The UIPicker object is the catchall picker view. It looks and functions just like the datepicker, except that you define the wheels and the content of each (see Figure 10-11).

A UIPicker uses a delegate and data source arrangement thats eerily similar to a table view (Chapter 5). A UIPicker needs a delegate object (UIPickerDelegate) and a data source object (UIPickerDataSource). The pickers datasource determines the number of wheels (called components) and the number of choices (called rows) on each wheel. The delegate object provides the label for each choice. At a minimum, you must implement theseUIPickerDataSource  methods:

-  (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
-  (NSInteger)pickerView:(UIPickerView  *)pickerView numberOfRowsInComponent:(NSInteger)component

And one of these UIPickerDelegate  methods:

-  (NSString *)pickerView:(UIPickerView *)pickerView  titleForRow:(NSInteger)row forComponent:(NSInteger)component
-  (NSAttributedString *)pickerView:(UIPickerView *)pickerView  attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component
-  (UIView *)pickerView:(UIPickerView *)pickerView  viewForRow:(NSInteger)row forComponent:(NSInteger)component  reusingView:(UIView *)view

The first data source method tells your picker how many wheels it has. The second method is then received once for each wheel; it returns the number of rows in that wheel.

Finally (much like the table view data source) a delegate method returns the label for each row in each wheel. You have three choices for which method you implement, depending on how sophisticated you want to be withthe content of each row:

n     Implement -pickerView:titleForRow:forComponent: to show plain text labels.
Your method returns a simple string value for each row. This is the most common. See the middle of Figure 10-11.

n     Implement -pickerView:attributedTitleForRow:forComponent: to display labels containing special fonts or styles. Your method returns an attributed string for each row. UICatalog doesnt include an attributedstring example, but it just means that label could have a mixture of fonts, sizes, and styles.

n     Implement -pickerView:viewForRow:forComponent:reusingView: to display anything you want in a row. Your method returns a UIView object, which is then used to draw that row. See the right of Figure 10-11.

The last method is the most like the table views use of cell objects. For a picker, you can supply a different UIView objects for each row or reuse a single UIView object over and over again. Theres no row cell object cache, asin the table view. Instead, the last UIView returned is passed back to your delegate the next time -pickerView:viewForRow:forComponent:reusingView: is sent. If you’re
reusing a single UIView object, alter that view and return it again. If not (or the view parameter is nil), return a new view object.

If you want to control the width of each wheel or the height of the rows in a wheel, implement the optional - pickerView:widthForComponent: or -pickerView:rowHeightForComponent: methods, respectively.

Look at the code that implements the simple picker view in the UICatalog app (in the middle of Figure 10-11).

You’ll find it in the PickerViewController.m file. The code that implements the picker using custom view objects(on the right in Figure 10-11) can be found in the
CustomPickerDataSource.m file. The view object used as the rubber stamp for each row is defined in
CustomView.m.

UIPickerView objects are not control objects; they are not subclasses of UIControl and they dont send action messages. Instead, the pickers delegate receives a -pickerView:didSelectRow:inComponent: message when the userchanges one of the wheels.

Image Views
You’ve already used enough image views to know your way around them. There are, however, a couple of properties that I’d like to mention.
The first is the contentMode. This property controls how the image
(which may not bethe same size as the view) gets arranged. The choices are listed in Table 10-5. 

Table 10-5. View content mode

Mode                                                                 Description

UIViewContentModeScaleToFill                     Stretches or squeezes the image to exactly fill the view. It may distort the image if the aspect ratio of the view is not the same as the image.
UIViewContentModeScaleAspectFit                Scales the image, without distorting it, so it just fits inside the view. Some parts of the view many not contain any image (think letterboxing).
UIViewContentModeScaleAspectFill               Scales the image, without distorting it, so it completely fills the view.
Some parts of the image may get clipped.

UIViewContentModeCenter                             Centers the image without scaling it.



UIViewContentModeTopUIViewContentModeBottomUIViewContentModeLeft, or UIViewContentModeRight

UIViewContentModeTopLeftUIViewContentModeTopRightUIViewContentModeBottomLeft, or UIViewContentModeBottomRight

The middle of one edge of the image is aligned with the corresponding edge of the view. The image is not scaled. The image may not fill, or be clipped, in the other three directions. 

One corner of the image is aligned with the same corner of the view. The image is not scaled. The image may not fill the entire view, or will be clipped if it overfills it.

UIImageView also has a quirky talent: it can show a sequence of images either quickly (like a flipbook or a really short movie), or slowly (like a slideshow). Put the images you want to display into an array (NSArray) and use thatarray to set the animationImages property. Set the animationDuration and, optionally, the animationRepeatCount to control the speed of each frame and how many times the entire sequence plays. (Set animationRepeatCount to 0 toplay forever.)

Once set up, send the view -startAnimation to begin the show and -stopAnimation to stop it again.
Code that demonstrates this is in the ImagesViewController.m file of the UICatalog project. 

Grouped Tables

Chapter 5 mentioned that you can create grouped table views, like thosused in the Settings app.
I didnt, however, actually show you how to do that. You already have all of the basics, but if you want a concrete example, look no furthethan the UICatalog project. Most of the sample views (buttons, controls, text fieldsandsegments) are presented in a group table view. Each group is a single example.

Start with the sample buttons. Its view controller is the ButtonsViewController class, which is subclass of UITableViewController. A table view controller is a UIViewController  designed specifically to manage table view. AUITableViewController is both UITableViewDelegate
and a UITableViewDataSource Find these delegate methods, which define the table contents, and see how they work:


-  (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
-  (NSString *)tableView:(UITableView *)tableView  titleForHeaderInSection:(NSInteger)section
-  (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
-  (UITableViewCell *)tableView:(UITableView *)tableView  cellForRowAtIndexPath:(NSIndexPath
*)indexPath

The View You Never See

That wraps up most of the important view objects in iOS. I’ll talk about toolbars a little in Chapter 12 and a lot more about UIView in Chapter 11. But I want to mention a very special view—one thats used a lot, but you neversee.

It's the UIScollView class. A scroll view adds the dynamics of scrolling to your interface. You never see the scroll view; you see its effects. A scroll view works by presenting a larger view inside a smaller view. The effect is like having a window into that larger view.

When you drag inside the window, you are “sliding” the view behind it around so you can see different 
portions of it, as illustrated in Figure 10-13.


Figure 10-13. Conceptual arrangement a scroll view

Its easiest to think about a scroll view as being two views in one. For most view objects, the size of its content (called its bounds) and the size it occupies in your interface (called its frame) are the same. So a view, say abutton, thats 30 by 100 pixels will occupy a region of 30 by 100 pixels in your interface.

A scroll view breaks this relationship. A scroll view has a special contentSize property thats divorced from its frame size. Its frame becomes the “window”  that appears in your interface.
The contentSize defines the logical size of the view, only a portion of which is visible through the window.

The contentOffset property determines exactly what portion is visible. This property is the point in the content area that appears at the upper-left corner of the frame—the portion visible to the usercontentOffset is initially 0,0. Thisplaces the upper-left corner of the content at the upper-left corner of the frame. As the contentOffset moves down, the content appears to scroll up, keeping the contentOffset point at the upper-left corner of the frame.

Table views, web views, and text views all provide scrolling and are all subclasses of UIScrollViewYou can subclass UIScrollView yourself to create a custom view that supports scrolling, or you can use a UIScrollView object onits own by simply populating its content view with whatever subviews you like. You can even have a scroll view inside another scroll view; its weird but there are notes in the Scroll View Programming Guide for iOS on how to doit.

A great place to get started with scroll views is the PhotoScroller example project. Search Xcodes Documentation and API Reference for the PhotoScroller sample code project and click on the Open Project button. ThePhotoScroller project defines a subclass of UIScrollView used to display, pan, and zoom an image. This project demonstrates two of scroll views three major talents:

n     Scrolling a larger content view around inside a smaller view

n     Pinching and zooming the content view

n     Scrolling by “page”

The first is its basic function. Its for this ability that scroll views are most often used, which includes table views, web views, and text views. To use a scroll view in this fashion, you dont have to subclass it or use a delegate. Simply populate and size its content view with the views you want to display, and the scroll view will let the user drag it around.

The scroll views second talent is pinching and zooming its content view, so it not only scrollit, but magnifies and shrinks it as well, as shown in Figure 10-14. 

This feature requires the usof a scroll view delegate(UIScrollViewDelegate) object. In the PhotoScroll project, the custom ImageScrollView is a UIScrollView subclass thats also its own delegate—an arrangement thatperfectly legitimate, if a little unusual. UIScrollView processes the touch events and handles the most of the panning and zooming details for you.


Figure 10-14. PhotoScroller app

You can also cause the view to scroll programmatically by setting its contentOffset property to any point in the content view you want. If you want to make the view animate its journey to the new position, send it the -setContentOffset:animate: message.

SCROLL VIEWS AND THE KEYBOARD
Scroll views can contain text fields—usually indirectly, by placing a text field in a table view, which you now know is a scroll view. When the keyboard appears, it can cover up the very text field the user wants to edit. Thesolution is to cause the scroll view to scroll up so the text field is visible above the keyboard.

To do that, your controller will need to observe keyboard notifications (such as UIKeyboardDidShowNotification).
These notifications  contain the coordinates  of the virtual keyboard on the screen. You use this information todetermine if the keyboard is covering your text field. If it is, send the scroll view a -setContentOffset:animate: message that will cause the text field to scroll to a position above the virtual
keyboard.

The mechanics  of this is described  in the Text, Web, and Editing Programming  Guide for iOS, which  you’ll find in Xcodes documentation. Look for the aptly named section “Moving Content That Is Located Under theKeyboard” in the “Managing the Keyboard” chapter.


The PhotoScroller project also demonstrates an advanced technique called tiling. In the beginning of 
Chapter 5, I explained that an iOS device doesnt have enough memory or CPU power to create thousands of individual rowobjects for a table. Instead, it draws just the portion of the table that is visible to the user, as the user scrolls through the list.

The contents of an exceptionally large content view may fall into the same category. The PhotoScroller project demonstrates how to dynamically prepare only those view objects that are currently visible through the scrollviews “window.” The table view—which, as you remember, is based on UIScrollView—already does this for you, only preparing the view objects for those rows that are visible in the table.

A much less common use of scroll views is to view content in “pages.” This is enabled by setting the pagingEnabled property to YES. When you do that, the scroll view forces the content view (technically, its contentOffsetproperty) to move in discrete distances, exact multiples of its frame size. Conceptually, it divides your content view into a grid (the exact size of the window) and any scrolling eventually settles on one segment. Theres aPageControl sample project that demonstrates this feature.

Advanced use of scroll views is not for the faint of heart. This can be really complex stuff, but its the stuff of really cool apps.
The now famous “drag to update” gesture that has become the mainstay of iOS apps is all done with scroll views and scroll view delegates. If you need this feature in a table view, most of the work is already done for you: create a UIRefreshControl object and connect it
to the table view controllers refreshControl property. Now the user can drag down to update the
table. To dive into the power of scroll views, start with the Scroll View Programming Guide for iOS.


Summary

Your command of the “language” of iOS is growing. You started out with the syntax and grammar of iOS, learning to create objects, connect them, and send messages. In this chapter you’ve expanded your vocabulary,acquiring an impressive number of view and control objects you can add and customize. You also saw how grouped tables are made, and got a glimpse of the magic behind scrolling. In the process, you learned how to downloadsample code and unlock its secrets.

You can go a long way using pre-made view and control objects. But there are limits, and at some point you’re going to want a view that no one has created yet. Creating your own views is the next step of your journey, andthe next chapter in this book.

No comments:

Post a Comment