Chapter 3

Application Design Elements

Every occupation has its own specific set of materials, tools, and methods, and aperson’s skill and understanding of this equipment arealmost always what definehis or hersuccess in the given field. In iOS development, you are given a great many components and functionalities from which you can assemble your applications. Having a full and practical understanding of these tools allows any developer to considerably improve the quality of any work he or shecontributes to.

In this chapter, you will systematically go through the design elements that iOS developers are initially given, discussing their purpose, use, implementation, functionality, and general guidelines for acquiring the best possible result from each item. Through this approach, you can acquire a better understanding of how best to utilize the tools given to youto create higher-quality applications.

Cocoa Touch Controls

Cocoa Touch includes a nice variety of items known as “controls,” which are the main objects used by an application to interact with the user. All of these elements, some of which are entirely new to iOS 5.0, allow you to build a more advanced yet simpler user interface with which to operate your applications.

UILabel

The UILabel class is easily one of the most basic and fundamental controls with which you can interact with your user by displaying information. A variety of other elements make use of UILabels, making them the foundation of nearly any user interface. Figure 3–1 is the simplest example of a UILabel.

Image

Figure 3–1. A simple UILabel

The primary way that you deal with a UILabel is simply by setting its text using either the -setText: method or, more simply, the text property. However, the class also has a variety of other properties you can set to customize your display even more, including the following:

  • font
  • textColor
  • textAlignment
  • enabled: You can easily dim or undim labels with this property, as a disabled UILabel is displayed dimmed out.

For a more precise control of a text display, you can also create a drop shadow using the shadowColorandshadowOffsetproperties. The shadowOffset property takes a CGSize type, which you can create using the CGSizeMake() function. This function takes two parameters, a width and a height, which specify where your shadow is placed in reference to the label. For example, if you configure a shadow with the following code, then the resulting UILabel will resemble Figure 3–2.

myLabel.shadowColor = [UIColor blackColor];
myLabel.shadowOffset = CGSizeMake(2.0, 2.0);
Image

Figure 3–2. Heavily shadowed text

Compare this to a UILabel with no shadow, as in Figure 3–3.

Image

Figure 3–3. A label with no shadow

As you can see, by specifying shadowOffset width of 2.0 and a height of 2.0, you made your label’s shadow appear shifted 2 points to the right and 2 points down from the original text. As you can guess, negative values for these will cause a shadow to move left and up respectively.

It is often helpful to an application’s graphic design to make use of the UILabel’s shadow, but it is often difficult to determine the ideal specifications to use. In general, it is safe to say that less is more, and a very subtle change, such as a gray shadow with an offset of (1.0, 1.0), will help improve the visual quality of an application. Figure 3–4 features one UILabel without a shadow compared to one with a gray shadow and an offset of (1.0, 1.0), in order to demonstrate the difference in visual appeal.

Image

Figure 3–4. A label with no shadow compared with a one square point shadow

The UILabel also has properties highlightedTextColor, which you can specify, and highlighted, which allows you to specifically highlight a label. However, no highlighting color will be applied unless one is specified with the highlightedTextColor property.

The UILabel, like many other elements you deal with, has a property called userInteractionEnabled. This property must be set to YES in order for any kind of gestures, such as a tap, to have any effect with a UILabel.

UIButton

As the cornerstone of two-way user interaction, the UIButton allows you to actively give your users clear options in their abilities within an application.

There are a variety of pre-defined types of UIButtons that you can very easily use. You can set the type of a button by way of the class method +buttonWithType:, which takes the following possible pre-defined values.

  • UIButtonTypeCustom
  • UIButtonTypeRoundedRect
  • UIButtonTypeDetailDisclosure
  • UIButtonTypeInfoLight
  • UIButtonTypeInfoDark
  • UIButtonTypeContactAdd

If no value is specified for the property, the Custom type is assumed, giving you the most freedom in customizing the view of your button.

In general, you will probably use a UIButtonTypeRoundedRect button if you simply want to give a simple button with text, but if you have a more complex button, including one that is image-based, you will probably use the UIButtonTypeCustom option. This way, you can more easily control the background settings of the button to ensure your visual theme stays well maintained.

Whenever you are dealing with a UIButton, you must always consider the possible “state” of the button, whether it is currently being selected. For this reason, most of the UIButton methods include a parameter for a UIControlState. For example, in order to set a UIButton’s text, rather than accessing the titleLabel property, you should use the -setTitle:forState: property. For example, you may configure a UIButton like so:

UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:@"Test" forState:UIControlStateNormal];
[button setTitle:@"Selected" forState:UIControlStateHighlighted];

Remember that whenever you create a view element programmatically, you should set its frame to specify the location, and then add it as a subview of whichever view it belongs in.

[button setFrame:CGRectMake(10, 10, 100, 44)];
[self.view addSubview:button];

The button title on a UIButton is simply a UILabel, so you can customize fonts and shadows quite easily with such methods as -titleColorForState: and -titleShadowColorForState:.

A UIButton also allows quite easily for two different images to be placed inside of it: a background image and a foreground image. You can easily set these with the -setBackgroundImage:forState: and setImage:forState: methods.

In order to programmatically add actions for a UIButton to perform, use the -addTarget:action:forControlEvents:method.

[button addTarget:self action:@selector(buttonPressed:)
forControlEvents:UIControlEventTouchUpInside];

From here, you can define your -buttonPressed: method to do whatever you prefer. If you implement your action in this manner, a reference to the UIButton will be passed to the method as the first parameter. This allows you a great deal of power in reacting to a multitude of different events with a single method by simply checking the properties of the “sender” element and acting accordingly. As a simple example, the following implementation will display in the log the text of whichever button was pressed.

-(void)buttonPressed:(UIButton *)sender
{
NSLog(@"%@", sender.titleLabel.text);
}

UISegmentedControl

The UISegmentedControl class is essentially an extension of the UIButton. It allows you to not only make selections, but alsopreserve those selections indefinitely, until another selection is made. They are particularly designed for situations in which one of multiple options will always be selected, such as choosing the type of display in a Maps application, or simply for configuring settings in a game.

UISegmentedControl elements are made up of multiple “segments,” with each one having either a string or an image inside them, as in Figure 3–5.

Image

Figure 3–5. A simple UISegmentedControl

Each segment has an index referring to it, starting with the first segment having index 0.

Just like with the UIButton, you can add actions to a UISegmentedControl to be performed any time the selected segment is changed using the -addTarget:action:forControlEvents:method, like so:

[self.segConaddTarget:self action:@selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];

When building instances of UISegmentedControl, you can specify an initial set of items to display using the -initWithItems: method after allocating the object. From there, you can add segments using the -insertSegmentWithImage:atIndex:animated:and-insertSegmentWithTitle:atIndex:animated: methods, and remove them using -removeSegmentAtIndex:animated: or -removeAllSegments:.

At any point, you can always access the current number of items in the control using the numberOfSegments property, and access the currently selected index with selectedSegmentIndex.

Once you have a specific index you want to access, you can use -setImage:forSegmentAtIndex:, imageForSegmentAtIndex:, setTitle:forSegmentAtIndex:, and titleForSegmentAtIndex: to modify or utilize your UISegmentedControl as needed! The following is an example of an action for the UISegmentedControl, which replaces the text of a newly selected segment with the square of its previous value.

-(void)segmentChanged:(UISegmentedControl *)sender
{
int index = sender.selectedSegmentIndex;
NSString *title = [sender titleForSegmentAtIndex:index];
int x = [title intValue]*[title intValue];
NSString *newTitle = [[NSNumber numberWithInt:x] stringValue];
    [sender setTitle:newTitle forSegmentAtIndex:index];
}

UITextField

The UITextField is easily the most customizable form of user input, as well as the most heavily used, as it allows you to easily take input from a user to be processed. You even have the ability to apply auto-correct to the user input, though this should be used sparingly to avoid unwanted corrections.

If you want to add a UITextField to a view, the easiest way to do it is to place it in the view in your XIBfile, as in Figure 3–6. You can then connect it to your header file as a property, and configure it in your -viewDidLoad method.

One of the absolutely most important things to remember whenever you are dealing with a UITextField (or other text-based inputs that you will deal with later) is that at some point, a keyboard will likely end up covering half of your screen. You need to plan for this as a designer by making sure your UITextField is in the top half of the screen or moves up to the top half of the screen when the keyboard appears. You can set up actions to be performed when the keyboard appears or disappears by registering for the following notifications:

  • UIKeyboardWillShowNotification
  • UIKeyboardDidShowNotification
  • UIKeyboardWillHideNotification
  • UIKeyboardDidHideNotification
Image

Figure 3–6. Adding a UITextView to a XIB interface

While in Interface Builder, you can also do some easy configuration of your UITextField. You can make all these changes programmatically using UITextField properties, but Interface Builder makes them a great deal easier, especially the keyboard settings such as Capitalization and Auto-Correction. Figure 3–7 is a view of the Attribute inspector containing these settings. As shown, the “Correction” type defaults to “Default”, resulting in the general response you often see when typing messages.

Image

Figure 3–7. Configuring a UITextField in the utilities pane

It is also very easy to programmatically customize the view of a UITextField through the use of the leftView, rightView, inputView, and inputAccessoryView properties.

One of the most important properties of a UITextField is the delegate property, which receives a variety of method calls relating to the actions of a UITextField. Whichever object is set as the delegate (usually the view controller in whose view the UITextField is shown) must conform to the UITextFieldDelegate protocol.

The UITextFieldDelegate protocol specifies a variety of methods to manage the editing of a UITextField. You can use the -textFieldShouldBeginEditing:and-textFieldShouldEndEditing: methods to either enable or disable the beginning or ending of editing. (Return NO in either of these to disable the given action.)

You can also use -textFieldDidBeginEditing:and-textFieldDidEndEditing: to do any movement of elements around to make sure that the keyboard does not block your UITextField.

In terms of editing the UITextField’s text, the UITextFieldDelegate offers a few methods to help customize the actions of your text field. -textField:shouldChangeCharactersInRange:replacementString: is useful for actively parsing text as it is entered. The -textFieldShouldClear:method also allows you a voice in whether a UITextField clears.

Quite possibly the most useful UITextFieldDelegate protocol method is the -textFieldShouldReturn: method, as it often serves as the main method by which you can implement the dismissing of the keyboard. Most users are used to pressing the return key to finish editing a UITextField, so you can simply implement this method like so:

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
return YES;
}

By using the -resignFirstResponder method, your UITextField gives up its role as the current key element, dismissing the keyboard.Yourresulting app, as simulated in Figure 3–8, will allow you to dismiss a keyboard with the pressing of the return key.

Image

Figure 3–8. An app with enabled functionality for dismissing the keyboard

UISlider

The UISlider provides a very nice and simple UI element for allowing a user to smoothly adjust values, as shown in Figure 3–9.

Image

Figure 3–9. A simple UISlider

These sliders are fairly easy to set up and configure. You can use Interface Builder to do most of the value setup, or you can set values programmatically. The most important properties to configure are the minimumValue, maximumValue, and initial value. You can set the initial value when you set up your XIB file, or in code by simply setting the value of the slider in your -viewDidLoad.

Beyond these essential properties, you can also greatly customize the appearance of the slider, including specifying customized track images and thumb images. You can also specify images to place on either end of the UISlider to help represent your maximum and minimum values using the minimumValueImage and maximumValueImage properties. For example, if your UISlider deals with an audio player’s volume, your minimum value image may be an image of a speaker, and the maximum value image might be the same speaker but with sound waves emanating from it.

You can create actions to be performed upon the changing of a UISlider’s value through two methods:

  1. Declare a method of return type (IBAction) in your header file, and then connect the UISlider in your XIB file to this method by holding Image and dragging from the slider to the method header.
  2. Use the -addTarget:action:forControlEvents: method, as shown here:
    [self.mySlider addTarget:self action:@selector(valueChanged:)
    forControlEvents:UIControlEventValueChanged];

The UISlider also has a property called continuous, which determines whether the value changes are reported continuously. If not, whichever action you have associated with the slider will be called only when users finish adjusting the value, rather than repeatedly as they move the thumb.

If you wish to cause the value of your slider to change programmatically, rather than having the user adjust it (possibly due to some other event), you should use the -setValue:animated: method, rather than simply setting the value property, in order to provide a smoother transition to the user.

UISwitch

The UISwitch acts very similarly to the UISlider, but allows the user to choose only a Boolean value, either “On” (as in Figure 3–10) or “Off”. These are very often used in Settings areas of applications to allow users to easily customize their preferences.

Image

Figure 3–10. An enabled UISwitch

The UISwitch works the exact same way as the UISlider in terms of adding actions to be performed upon the changing of the value, though significantly simplified due to the Boolean nature of the switch. Just as before, -addTarget:action:forControlEvents: is used to connect a method to the switch. You can also access the value of the switch using the onproperty, and animate the changing of its value using the -setOn:animated: method.

UIActivityIndicatorView

Often an application may be currently working on a task that does not happen immediately. This could be a process of downloading files from a server or even simply a task dealing with a large amount of data requiring some significant amount of time to complete. As the developer, you should always strive to keep your user informed of such activities. For this, you have the UIActivityIndicatorView, shown in Figure 3–11.

A UIActivityIndicatorView is a simple element that is used to display whether an activity is going on in the background. However, it allows for only two states: in progress, and not in progress. To switch between these, you can use the –startAnimating and -stopAnimatingmethods. You can also access whether the indicator is currently animating through the –isAnimating method. If you do not have any particular use of the indicator once your task is finished, you can use the hidesWhenStopped property.

Image

Figure 3–11. A UIActivityIndicatorView element

For this element, you are fairly limited in your customization options in that you are given only properties to adjust the color and size. First, you have the activityIndicatorViewStyle property, which takes three possible values:

  • UIActivityIndicatorViewStyleWhiteLarge
  • UIActivityIndicatorViewStyleWhite
  • UIActivityIndicatorViewStyleGray

These three values differ only in size and color, and should be chosen from to maximize visual display quality.

If none of the foregoingstyles fit your application’s design very well, you can also specify a different color, through the UIColor class, using the color property.

UIProgressView

Following along the lines of the UIActivityIndicatorView, you have the UIProgressView. This acts very similarly to a UISlider in that it displays a value (although limited to scaling between 0 and 1), but does not allow for any user input. This element is most often used for displaying some amount of progress of a task completed, as in Figure 3–12, as an alternative to simply using a UIActivityIndicatorView to show that progress is occurring.

Image

Figure 3–12. A UIProgressView in use

Just like the UIActivityIndicatorView, UIProgressViews can be created with a style, accessed through the progressViewStyle property, which has the following possible values:

  • UIProgressViewStyleDefault:Standard style chosen for UIProgressViews
  • UIProgressViewStyleBar:Style often used inside of a toolbar

The most important property of the UIProgressView is, naturally, the amount of progress. This value, which ranges between 0.0 and 1.0, can be accessed through the progress property. You can also set this property using the -setProgress:animated: method to improve your application’s visual quality.

Aside from the progressViewStyle, the UIProgressView allows for decent appearance customization of the tint color and image for both the progress displayed, as well as the track upon which it rests. These are all respectively accessed through the progressTintColor, progressImage, trackTintColor, and trackImage properties.

UIPageControl

For dealing with applications with “paging,” a very useful little UI element is the UIPageControl, shown in Figure 3–13. This device acts mainly as an indicator to users as to which page they are currently on, though it can also be used to directly manipulate an application, usually by changing the current page. For an excellent example of this utility, look at the bottom of your device’s Weather app.

Image

Figure 3–13. A UIPageControl

When using a UIPageControl, you can easily access multiple values associated with it through such properties as currentPage and numberOfPages. If your application happens to have a possibility of havingonlyone page at some point, you may also make use of the hidesForSinglePage property.

Just as with the UISlider and UISwitch, you can add actions to a UIPageControl to be performed on the changing of the currentPage value using the -addTarget:selector:forControlEvents: method in conjunction with the UIControlEventValueChanged event. This method would ideally handle the actual changing of your application’s display to display the newly selected page.

If, upon the changing of the currentPage, you decide there might be some reason to not have the display of the UIPageControl update immediately, you can set the defersCurrentPageDisplayproperty to YES, causing it to wait until the -updateCurrentPageDisplay method is called before adjusting the display. This property defaults to NO otherwise.

Unfortunately, you are incredibly limited in terms of customizing the appearance of a UIPageControl. However, you are given a very useful method called -sizeForNumberOfPages:, which allows you to easily find the minimum size needed to display a UIPageControl with any given number of pages.

Anytime you make use of a UIPageControl element, you want to make sure the indicator does not at all interfere visually with the pages that it manages. The element should therefore, according to Apple’s Interface Guidelines, be centered between the bottom of the “pages” and the bottom of the screen.

UIStepper

The UIStepper is an element entirely new in iOS 5.0, intended to streamline an application’s use of incremental values. It is equipped with “+” and “-” buttons for the user, but does not actually display its associated value, as in Figure 3–14. This task is left up to the developer to implement as is appropriate for each application.

Image

Figure 3–14. The new UIStepper

The main property of the UIStepper is its value property, which you can access in order to update your display accordingly. This value can be easily configured with a variety of properties:

  • minimumValue: The minimum number that the value can reach
  • maximumValue: The maximum value
  • stepValue: The amount by which value is incremented upon the use of the stepping buttons
  • wraps: If this property is set to YES, your minimum and maximum values will wrap together. Thus, if your value is incremented to exceed the maximumValue, it will wrap around to the minimumValue, and vice versa.
  • autorepeat: This property allows the UIStepper buttons to be held in order to repeatedly increment the value without having to repeatedly tap the element.
  • continuous: Specifies whether value change events are sent every time the valueproperty changes or only if the user has finished changing the value, as used with the autorepeatproperty

As before, you can assign actions to be performed upon the changing of the valueproperty using the -addTarget:selector:forControlEvents: method with UIControlEventValueChanged.

Once your UIStepper is fully configured, the only concept to keep in mind when designing your application is to make sure that your user interface informs the user clearly as to which value a UIStepper changes.

Data Views

In iOS you have access to a variety of subclasses of UIView, known collectively as “data views,” that allow you to easily place content in your application in very specific ways depending on the type of your application.

UIImageView

One of the most intuitive and important data views in Cocoa Touch is the UIImageView class. This view element, along with its properties and methods, is optimized to help the developer display and manage images within an application.

The core properties of a UIImageView are its image and highlightedImage properties. Both of these will be instances of the UIImage class, with the highlightedImage being displayed only if the image is selected. You can create and specify these images programmatically by using the -initWithImage:or-initWithImage:highlightedImage: designated initializers, or by simply setting the properties individually. An example is as follows:

self.myImageView = [[UIImageView alloc] initWithImage:[UIImage
imageNamed:@"myImage.png"]];

If you make use of the +imageNamed:method to create your UIImage, you will need to make sure that your actual image file is imported into your project. You can do this by dragging the file from the Finder into Xcode. When you do this, a dialog will appear, and you need to make sure that the option marked “Copy items into destination group’s folder (if needed)” is checked, as in Figure 3–15.

Image

Figure 3–15. Pop-up dialog for adding files to a project

The UIImageView class also has built-in functionality to allow for animating multiple images by making use of the following properties:

  • animationImages: This property, an NSArray of UIImage objects, specifies the actual images to be animated.
  • highlightedAnimationImages: This acts just like the animationImagesproperty, but for when the UIImageView is highlighted.
  • animationDuration:This value, created as an NSTimeInterval, represents the total time for all the images in animationImages to cycle through. The value, if unspecified, defaults to the number of images multiplied by 1/30 of a second.
  • animationRepeatCount: Quite simply, this value specifies how many times the cycle of images will repeat. If set to 0, the default, the animation will repeat indefinitely.

Once your UIImageView’s animation properties are configured, you can manage the actual animation using the -startAnimating, -stopAnimating, and isAnimatingmethods.

One of the most important properties to keep in mind when dealing with a UIImageView is the contentMode property. While this is actually inherited from UIView, it tends to become very important when dealing with images. The contentMode property essentially specifies how the view will respond to dealing with content whose aspect ratio does not fit well with the view it is placed in, such as a rectangular image being shown by a square UIImageView. There are a variety of options for this property, all of which are fully documented in the Apple documentation, but most of the time you will probably opt for UIViewContentModeScaleAspectFill. This option may end up clipping part of an image out, but it will be certain to fill the entire space of the presenting view, improving design quality. If, for any reason, you require the image to not be clipped and would prefer the empty space, you can use the UIViewContentModeScaleAspectFit value.

UITextView

The UITextView class is incredibly similar to a UITextField in that it allows the user to input text. However, its visual design allows for significantly greater quantities of text, as shown in Figure 3–16.

Image

Figure 3–16. Using a UITextView in a XIB

Most of the UITextView properties are very similar to the UITextField properties, such as text, font, and textColor. Other useful properties to configure your text are editable, which specifies whether the text can be edited, as well as textAlignment. You can even specify what kind of data the text view detects, such as phone numbers or e-mail addresses, using the dataDetectorTypes value.

Just like the UITextField, the UITextView has a delegateproperty that receives multiple actions depending on what happens in the UITextView. This property must conform to the UITextViewDelegate protocol.

Most of the UITextViewDelegate methods are the exact same as those in the UITextFieldDelegate protocol discussed earlier. However, the UITextViewDelegate does not have any method to indicate when the return key has been pressed, making it harder to determine when the keyboard should be dismissed using the -resignFirstResponder method. Instead, you can implement the -textView:shouldChangeTextInRange:replacementText: method.

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range
 replacementText:(NSString *)text
{
if ([text isEqualToString:@" "])
    {
        [textView resignFirstResponder];
return FALSE;
    }
return TRUE;
}

With a UITextView, the pressing of the return key causes a text of “ ” to be added to the current text, so you can simply have this method wait for such an input, and dismiss the keyboard accordingly. In your application, you may wish to allow the user to end lines in your text view, so this implementation may not be ideal.

In terms of customization, you can easily customize the text attributes of the text field through the aforementioned properties, but you also have some level of customization over the view itself. You can access the actual input view using the inputViewproperty, as well as an inputAccessoryView. This accessory view, when non-nil, is displayed above the keyboard that appears once the UITextView is being edited, allowing you to attach a custom toolbar to your keyboard if desired.

It is also possible for any class to receive notifications about the state of a UITextView by becoming an observer to any of the following notifications:

  • UITextViewTextDidBeginEditingNotification
  • UITextViewTextDidChangeNotification
  • UITextViewTextDidEndEditingNotification

Just as with the UITextField, one of the most important design aspects of the UITextView is to remember that at any given moment, a massive keyboard could be taking up half of your screen. As a developer, you should make sure that your UITextVieweitheris in an area that will not be blocked by the keyboard, or moves to such an area once the keyboard appears. See the previous “UITextField” section for more explanation on how to receive notifications when the keyboard appears and disappears.

UIScrollView

The UIScrollView class is incredibly useful for dealing with large amounts of content that you cannot fit in a single view, but belongs all in the same page. This could be a list of pictures to be displayed or simply a very large image that you want to be able to zoom and scroll with.

The content of a UIScrollView is defined as any subviews inside of it, so you can simply use the -addSubview: method to add content to your UIScrollView.

Absolutely any time that you use a UIScrollView, it is necessary to set the contentSize property. This specifies to the UIScrollView exactly how much scrolling in any direction to allow. Generally, this will be whatever the size of your content is, so if your content is a UIImageView with a UIImage of size 800x640, you will want the content size to be the same. Alternatively, if you want your UIScrollView not to be able to scroll in one direction, you might make that direction’s aspect of your contentSize smaller.

You can also adjust the contentInsetandcontentOffset to further customize the displaying of your content. The latter of these can even be animated using the -setContentOffset:animated: method.

The scrolling properties of a UIScrollView are incredibly easy to configure both in the XIBfile and programmatically. The most important of these properties follow:

  • scrollEnabled: Specifies whether the UIScrollView can scroll;you can use this property to “lock” the view.
  • directionalLockEnabled: This property, if enabled, restricts the UIScrollView to scroll in only one direction at any given time, either vertical or horizontal.
  • scrollsToTop: This enables or disables the ability of the user to tap the status bar at the top of the screen in order to scroll the UIScrollView to the top of its content.
  • pagingEnabled: If this property is enabled, the scrolling gravitates to multiples of the view’s bounds, rather than simply allowing constant scrolling throughout the content. This, in conjunction with a UIPageController, is very useful if you are using a UIScrollView to display multiple pages of content.

One of the most useful methods of a UIScrollView is -scrollRectToVisible:animated:, as it allows you to scroll specifically to any given area of the content based on the needs of your application.

Among many other properties, there also exist ones to manage the “bouncing” of the UIScrollView when the user flicks the scroll view past its bounds, such as the bounces, alwaysBounceVertical, and alwaysBounceHorizontal properties.

The developer also has control over the “scroll indicator,” the thin bar(s) on the bottom or sides showing how far the view has scrolled. You can adjust these using the indicatorStyle, scrollIndicatorInsets, showsHorizontalScrollIndicator, and showsVerticalScrollIndicatorproperties. You can even manually flash the indicators using the -flashScrollIndicators method.

Aside from scrolling and panning, UIScrollViews also have a zooming ability naturally built into them. In order to implement this functionality, you must simply change the maximumZoomScaleand/or minimumZoomScaleproperties to values other than 1.0.

The UIScrollView also has a delegate property that responds to a variety of events that occur within the UIScrollView. This object, which must conform to the UIScrollViewDelegate protocol, has multiple methods for responding to both the beginnings and endings of any scrolling, flicking, or zooming event. Refer to the Apple documentation for full details on all these methods.

UIWebView

The UIWebView class is a useful data presentation class for an application that “wraps” some kind of web application.

You can create a UIWebView in your view controller’s XIBfile very easily, which also allows you to easily edit most of the properties of a UIWebView in the Attributes inspector. These properties, such as the dataDetectorTypes (similar to those used in a UITextView), are also accessible programmatically.

A UIWebView can load data from the Web in a variety of ways, depending on the type of content you need to manage. The loading of content is managed through multiple methods, including -loadData:MIMEType:textEncodingName:baseURL:, -loadHTMLString:baseURL:, and -loadRequest:. The simplest of these, -loadRequest:, takes a parameter of type NSURLRequest. This class is essentially a wrapped NSURL with extra properties specifically pertaining to accessing content online, such as a timeoutIntervalor a cachePolicy. Once your content is loading, you can also make use of the -stopLoading or -reload methods as you wish.

MKMapView

The MKMapView is a data presentation class specifically used in conjunction with the MapKit framework to present the user with a map. For more information on this framework, including the detailed use of this view, refer toChapter5, MapKit Recipes.

UITableView

The UITableView is an incredibly powerful class for data presentation, based on the idea of presenting large amounts of data that is all formatted similarly. We have devoted Chapter 9, UTTableViewRecipes,to covering the general use of this class and fully explaining the nuances of developing table-based applications.

UIPickerView

A UIPickerView is similar to a UITableView, though it is not quite as complex and customizable. It allows the user to be presented with a variety of similarly formatted options, and rotate through them in order to select a specific option, as in Figure 3–17.

Image

Figure 3–17. The default UIPickerView

The UIPickerView is set up similarly to the UITableView through the use of both a dataSource property and a delegate property. These properties must respectively conform to the UIPickerViewDataSource and UIPickerViewDelegate protocols, and are usually set to the UIViewController that will present the UIPickerView.

The UIPickerViewDataSource protocol requires only two methods, which define the physical configuration of the UIPickerView in terms of “rows” and “components.” Components are vertical sections into which your view is split that allow you to easily choose multiple values at once. Rows contain the individual options in each component that can be chosen by the user.

You can configure the number of components, as well as the number of rows per component, through the data source methods -numberOfComponentsInPickerView:and-pickerView:numberOfRowsInComponent:.

The visual setup of a UIPickerView is handled through multiple delegate methods:

  • pickerView:rowHeightForComponent:: Specifies the height of each row in a given component
  • pickerView:widthForComponent:: Specifies the width of each component; your components will not automatically be fit into the UIPickerView, so make sure that the total width of your components is not more than the width of the UIPickerView.
  • pickerView:titleForRow:forComponent:: Use this method to give a simple text to be displayed in each row.
  • pickerView:viewForRow:forComponent:reusingView:: You can use this if you wish to display more than a simple text in a row. Try to make use of the reusingView: parameter to improve performance.
  • pickerView:didSelectRow:inComponent:: This method is called every time a component stops rotating and lands on a specific row, allowing your application to update properly elsewhere.

Here is a sample configuration:

#pragma mark - Data Source methods
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 3;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component
{
return 3;
}
#pragma mark - Delegate methods
-(CGFloat)pickerView:(UIPickerView *)pickerView
rowHeightForComponent:(NSInteger)component
{
return 30;
}
-(CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component
{
return 100;
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
NSLog(@"Selected. Row:%i, Component:%i", row, component);
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [NSString stringWithFormat:@"R:%i, C:%i", row, component];
}

This will configure the UIPickerView shown in Figure 3–18, along with logging each selected row within each component.

Image

Figure 3–18. A UIPickerView configured by rows and columns

Once your UIPickerView is configured, you can access the selected row in each component using the -selectedRowInComponent:method.

You can also toggle the appearance of the center bar that signifies the chosen rows through the showsSelectionIndicator property.

UIDatePickerView

The UIDatePickerView is a specialized version of a UIPickerView configured to handle any selection of times, dates, or countdowns, shown in Figure 3–19. The class is not actually a subclass of UIPickerView, but instead has a customized UIPickerView as a subview.

Image

Figure 3–19. The specialized UIDatePickerView

This class has a property called datePickerMode, which allows you to select from multiple values, representing types of pickers, to fit your specific situation.

  • UIDatePickerModeTime: Selects a time
  • UIDatePickerModeDate: Selects a date
  • UIDatePickerModeDateAndTime: Selects both a date and time in one picker
  • UIDatePickerModeCountDownTimer: Selects a countdown timer to be set

In order to be informed of changes to selected rows, add a method as an action to your UIDatePickerView to be called upon UIControlEventValueChanged events, just as with several of your previously discussed elements.

You can configure the different modes of a UIDatePickerView through such properties as maximumDate, minimumDate, minuteInterval, and countDownDuration.

If you wish to programmatically change the date displayed by a UIDatePickerView, you can set the date property, but this will not animate the change. It is recommended that you use the -setDate:animated: method to make such changes if the UIPickerView is visible.

Gesture Recognizers

In iOS you are able to improve the functionality of your application by adding “gesture recognizers” to instances or subclasses of UIView. “Gesture” refers to a touch-based event driven by the user, such as a tap, swipe, or pinch. These elements can then perform actions in your application, extending your normal functionality. Though it is possible to create your own subclasses of UIGestureRecognizer, you will focus on those already incorporated into iOS 5.0.

Whenever using a UIGestureRecognizer, it is important to remember that a large percentage of users will not know to look for the existence of the gestures that you can implement. Therefore, you should implement a UIGestureRecognizeronlyin order to expedite a task that can be performed elsewhere. Essential functions should not be built into these without explicitly informing the user.

The UIGestureRecognizer class is itself an abstract class that defines the behavior of multiple subclasses, each representing different gestures. However, no matter the subclass, a gesture recognizer is added to any UIView by use of the -addGestureRecognizer: method. To add an instance of UITapGestureRecognizer called tapGesture to your entire view controller’s view, for example, you write the following:

[self.view addGestureRecognizer:tapGesture];

TIP: Any subclass of UIGestureRecognizer can be added to any instance or subclass of UIView, so you can easily build custom gesture functionality into nearly any element, resulting in very flexible applications.

Some elements, such as UILabels, will not respond to any UIGestureRecognizers unless their userInteractionEnabled property is set to YES.

The UIGestureRecognizer property state allows you to evaluate the current condition of any specific subclass as its gesture is recognized. It has multiple different possible values, each representing a possible step of the gesture recognition process:

  1. UIGestureRecognizerStatePossible: Indicates a gesture is possibly in the process of being performed, but its requirements have not yet been met
  2. UIGestureRecognizerStateBegan: Indicates a continuous gesture has been recognized and is continuing
  3. UIGestureRecognizerStateChanged: Signifies a change to an already begun continuous gesture
  4. UIGestureRecognizerStateEnded: Indicates a gesture has finished
  5. UIGestureRecognizerStateCancelled: A gesture recognizer has received touches to cancel a continuous gesture.
  6. UIGestureRecognizerStateRecognized: Equivalent to UIGestureRecognizerStateEnded

Especially when dealing with continuous gestures, you can run into an issue where several of these states cause a UIGestureRecognizer to perform its action, resulting in a method being called repeatedly unnecessarily. By checking the state property for specific states, you can help avoid this.

Ideally, you will set up a single method to be called by any and all instances of UIGestureRecognizer subclasses, and simply differentiate them inside the method. You can make use of the +isKindOfClass method, as well as the multiple properties of each subclass, and even the view the gesture was recognized in, in order to identify which of multiple UIGestureRecognizer objects performed your action. This action will have the following handler:

-(void)handleGesture:(UIGestureRecognizer *)gestureRecognizer;

Another useful functionality that all UIGestureRecognizer subclasses inherit is the ability to determine exactly where in a view the gesture was performed through the use of the -locationInView: and -locationOfTouch:inView: methods. By using these values, you can easily adjust your application’s behavior depending on the specific location of a touch within a single view. One example of this would be to draw at the point of a user’s touch, allowing users to essentially draw on their screen.

All subclasses of UIGestureRecognizer inherit the delegate property, which can be set to the view controller containing the recognizer’s view. This property conforms to theUIGestureRecognizerDelegate protocol, and allows the view controller extra control in responding to gestures.

Multiple subclasses of UIGestureRecognizer allow for gestures that require multiple touches to be recognized. When dealing with these, it is important to keep in mind the maximum number of touches each device can handle. As of the writing of this text, the iPhone can handle up to five touches, while the iPad can handle up to eleven.

UITapGestureRecognizer

A UITapGestureRecognizer recognizes, as you can guess, when the user taps on its assigned view.

You are able to create very specific gesture functionalities by adjusting the numberOfTapsRequired and numberOfTouchesRequired. By setting both properties to 2, for example, you can look specifically for events of when the user taps the screen twice with two fingers. Your configuration would look like so:

UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleGesture:)];

tapGesture.numberOfTapsRequired = 2;
tapGesture.numberOfTouchesRequired = 2;
[self.view addGestureRecognizer:tapGesture];

When you implement handleGesture:, you should make sure to look for the UIGestureRecognizerStateEnded state with any UITapGestureRecognizer.

UISwipeGestureRecognizer

The UISwipeGestureRecognizer recognizes when the user swipes across a screen with one or more fingers. The swipe is a discrete gesture, so its action is called only once.

The UISwipeGestureRecognizer class has the property direction, which specifies the direction in which the swipe must occur to be recognized. Possible values are UISwipeGestureRecognizerDirectionRight, UISwipeGestureRecognizerDirectionLeft, UISwipeGestureRecognizerDirectionUp, and UISwipeGestureRecognizerDirectionDown.

This class also has a numberOfTouchesRequired, allowing you to specify multi-touch swipe gestures in your application.

UIPanGestureRecognizer

The UIPanGestureRecognizer class recognizes “panning” gestures, which are continuous gestures. As such, you generally perform actions with them in reaction to either the UIGestureRecognizerStateChanged or UIGestureRecognizerStateEnded states.

Pan gestures can be configured with both minimum and maximum numbers of touches, allowing you to create a range of allowable touch numbers. Use the maximumNumberOfTouches and minimumNumberOfTouches properties to implement this.

With a UIPanGestureRecognizer, you can access the distance moved, as well as the velocity of a gesture, through the use of the -translationInView: and -velocityInView: methods.

In most cases of using a UIPanGestureRecognizer, it is important to reset the translation and/or velocity values of the pan gesture after acquiring and using them. If you don’t, your values will accumulate, resulting in abnormally large values very quickly.

The following is a sample implementation, extracted from your -handleGesture: method, to drag and drop the view that your pan was recognized in.

if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
    {
UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)gestureRecognizer;
if (pan.state == UIGestureRecognizerStateChanged || pan.state == UIGestureRecognizerStateEnded)
        {
CGPoint movement = [pan translationInView:pan.view];
            [pan.view setCenter:CGPointMake(pan.view.center.x + movement.x, pan.view.center.y + movement.y)];
            [pan setTranslation:CGPointZero inView:pan.view];
        }
    }

If you did not reset your translation to zero after moving your view, your translations would build up rapidly, violently throwing your view off of the screen.

If you experience a Linker error when using the CGPointZero value, add the CoreGraphics.framework library to your project.

UILongPressGestureRecognizer

The UILongPressGestureRecognizer looks for “long presses,” which are simply when the user holds one or more fingers in the same position for longer than a normal tap.

Just as with several of the previous gesture recognizers, you can specify a required number of taps, as well as a number of fingers/touches used using the numberOfTapsRequired and numberOfTouchesRequired properties.

This subclass of UIGestureRecognizer has extra properties to configure the nature of the “long press.” The minimumPressDuration specifies how long a touch must be held for before the recognizer’s state turns to UIGestureRecognizerStateBegan. The allowableMovementproperty, which takes a float, specifies how much “wiggle-room” the user has before the long-press gesture fails. This value should be made large enough that a normal person can easily hold the touch, but not so large as to allow for significant movement that could cross multiple elements.

A UILongPressGestureRecognizer will trigger its action both when it begins and when it ends, which can cause unwanted behavior. Generally, you will want to respond only to the UIGestureRecognizerStateBegan state, so that the user can easily be notified that the gesture has been recognized.

UIPinchGestureRecognizer

This subclass of UIGestureRecognizer looks for pinch gestures, which involve a user moving two touches toward or away from each other. This class is continuous, meaning it stays active as long as the pinch is held, but has constantly changing values.

You are able to access two properties from each pinch gesture:

  1. scale: The scaling factor created by the pinch
  2. velocity: The velocity of the given pinch gesture

Depending on your use, you will want to look for different values of state.

UIRotationGestureRecognizer

This class recognizes a fairly uncommon gesture: a rotation. This gesture is composedof a user moving two fingers in a circular motion.

Information about the gesture performed can be accessed through the rotationandvelocity properties, similar to the UIPinchGestureRecognizer.

View Controllers

When you design iOS applications, you tend to organize your view controllers in fairly consistent ways based on the flow of data in your application. Built into Cocoa Touch are multiple special subclasses of the UIViewController class that allow you to easily organize, customize, and present your data depending on your type of application. By combining these controllers, you can create complex organizational schemes for your applications to present information to the user in the most optimized manner.

UINavigationController

The UINavigationController is by far one of the most commonly used classes in terms of workflow design and view presentation. It not only helps to organize transitions from one view to another, but also provides a highly customizable toolbar across the top of each view in order to help present and manage different parts of an application. These classes can then be placed into other organizational controllers, allowing you to create a nested flow of information from one level of your application to another.

A UINavigationController starts off with a “root view controller,” which is the view controller that will be visible if no others are added to it. Then, other view controllers can be either “pushed” onto a stack of controllers, or “popped” off using various methods. Views in a UINavigationController’s stack are displayed from the top down, so pushing a new view controller instructs the UINavigationController to display it. Likewise, when you pop off the current view controller, it is removed from the view, revealing the next controller in the stack.

Creating a UINavigationController is as simple as creating any other class. You can use a handy designated initializer called -initWithRootViewController: to set your root view, or you can simply call the -init method (after allocating), and then use the -pushViewController:animated:method to push in your root view.

Figure 3–20 features a configured UINavigationController with multiple elements.

Image

Figure 3–20. A UINavigationController with left and right buttons and a toolbar

The top edge of this view, presented in a UINavigationController, features the navigation bar. This area is meant to direct users in their navigation throughout your application through three features:

  • Title: The center of the navigation bar features the title of the presented view controller. This can easily be set in each -viewDidLoad method by changing the title property inherited from the UIViewController class. (e.g.,self.title = @“More Info”). You can also create a custom title view.
  • Back Button: The left side of the navigation bar, if the root view controller is not currently shown, automatically contains an arrowed button, and by default contains the title of the previously shown view controller, so as to help the user navigate back.
  • Right Bar Item(s): The right side of the navigation bar is by default empty, but can be configured using either the rightBarButtonItem or rightBarButtonItems properties (the latter for adding multiple items). On an iPhone, you most likely will not have more than one or two small buttons here, but an iPad has space for a larger quantity. Usually buttons in this location provide options to manipulate or operate on information shown in the current view controller, such as a UITableView’s edit button or a button to open up a printing interface. The aforementioned properties are accessed through the navigation item, i.e.,self.navigationItem.rightBarButtonItem.

The bottom edge of the view in Figure 3–20containsthe navigation toolbar, which comes built into the UINavigationController. This toolbar is by default hidden, but can be easily shown by setting the toolbarHidden property on UINavigationController to NO, or by using the -setToolbarHidden:animated: method (to animate the change). Be careful when doing this, however, as the state of the toolbar, whether it is hidden or shown, remains the same across the pushing and popping of view controllers until the property is set again. This means you may have to make use of the -viewWillAppear:animated: and -viewWillDisappear:animated: methods in each view controller to show or hide the toolbar as appropriate.

The contents of the UIToolbar are easily set, not by the UINavigationController, but instead by the UIViewController that is currently on top of the stack. By calling -setToolbarItems: with each view controller in your -viewDidLoad methods, you can easily implement a differently configured toolbar in each view controller. The following example, taken from a view controller’s -viewDidLoad, shows how an individual controller’s toolbar can be easily built.

[self setToolbarItems:[NSArray arrayWithObject:[[UIBarButtonItem alloc]
initWithTitle:@"Toolbar Button "style:UIBarButtonItemStyleBordered target:nil
action:NULL]] animated:NO];

By simply changing the target and action to an actual value and selector, you will easily be able to implement functionality in your toolbar’s buttons.

The UINavigationController also has a delegate property, which conforms to the UINavigationControllerDelegate protocol. Utilize this to gain extra control over actions performed right before and after view controllers are pushed or popped from the stack.

UITabBarController

Another special view controller, almost as ubiquitously used as the UINavigationController, is the UITabBarController. This class is specially designed to handle applications that contain multiple sections or “tabs,” each with their own flow of information. The Twitter application has an excellent example of this implementation. Each tab can include other controllers, including UINavigationController objects, allowing for more complex networks of view controllers. The UITabBarController, however, should not be placed inside of any other controllers, as it is unsupported by the iOS API.

Each view controller in a UITabBarController has a tab, which by default is populated with the view controller’s title, and no image. Creating a new instance of the UITabBarItem and setting it as the view controller’s tabBarItem will override this, allowing you to add images and slightly customize your tabs.

The -viewDidLoad method for each view controller is not actually called until the specific tab containing that view controller is selected, which means that any configuration done in this method, such as setting a title, does not occur right away, leaving you with unlabeled tabs until they are selected. Setting the view controller’s title when it is declared can simply solve this, though adding the title configuration into the view controller’s designated initializer will work as well.

Figure 3–21 features a screenshot of a simple UITabBarController with three tabs, each with configured titles and system-based images, as well as the sample code used to create it.

Image

Figure 3–21. A UITabBarController configured with three tabs

This uses the following configuration code, taken from an -application:didFinishLaunchingWithOptions: method, assuming that two UIViewController subclasses, MainViewController and SecondViewController, have been created and imported to the application delegate file.

self.viewController = [[MainViewController alloc] initWithNibName:@"MainViewController"
bundle:nil];
self.viewController.title = @"First";
self.viewController.tabBarItem = [[UITabBarItem alloc]
initWithTabBarSystemItem:UITabBarSystemItemFeatured tag:0];

__strong SecondViewController *second = [[SecondViewController alloc] init];
second.title = @"Second";
second.tabBarItem = [[UITabBarItem alloc]
initWithTabBarSystemItem:UITabBarSystemItemDownloads tag:1];

__strong SecondViewController *third = [[SecondViewController alloc] init];
third.title = @"third";
third.tabBarItem = [[UITabBarItem alloc]
initWithTabBarSystemItem:UITabBarSystemItemRecents tag:2];

__strong UITabBarController *tabcon = [[UITabBarController alloc] init];
[tabcon setViewControllers:[NSArray arrayWithObjects:self.viewController, second, third,
nil]];

self.window.rootViewController = tabcon;

UISplitViewController

The UISplitViewController is another element built to help organize other view controllers. This class, however, is available only when developing for the iPad, as it requires a large amount of space to utilize. It features two UIViewControllers, a “master pane” and a “detail pane.” The master pane is displayed in a narrower view on the left side of the screen, with the remainder taken up by the detail pane, as in Figure 3–22. Generally this setup is used to select an item from the master pane and give details on that item in the detail pane.

Image

Figure 3–22. The iPad-specific UISplitViewController

Figure 3–22 shows a pre-configured UISplitViewController-based application created from the Master-Detail Application template. If you wish to build your application using this controller, this template can easily help you get started. This option can be found in the first menu presented upon creating a new project, shown in Figure 3–23. Like the Empty Application template, it also provides an option to include the Core Data framework and automatic setup.

Image

Figure 3–23. Using the Master-Detail Application template to create a pre-configured UISplitViewController

The UISplitViewController class has two properties, viewControllers and delegate. The former of these is an NSArray that must contain exactly two controllers: first the master pane controller, then the detail pane controller.

By default, if the iPad is portrait-oriented, the master pane is not shown. You can adjust this through the UISplitViewControllerDelegate protocol method -splitViewController:shouldHideViewController:inOrientation:.Generally the delegate property of a UISplitViewController is set to the view controller for your detail pane, as it contains the relevant information of your application.

When using a UISplitViewController, be careful to ensure that all parts of your view are clear in their related actions and controllers. Avoid placing toolbars in both panes, as they might appear connected. Make sure also that any selection made in your master pane visibly persists, so that the user can always tell which item was selected to display the current view.

UIPopoverController

The UIPopoverController class is an element specific only to the iPad. It is used to present information over top of your current view, usually to present options to users as to how they want to proceed. UIPopoverControllers are very convenient to implement as their location can easily be specified to any location, improving the visual flow of your application.

Figure 3–24 is from Chapter 13 (Data Transmission Recipes), Recipe 13–2,dealing with mailing and printing material, in which you presented a UIPopoverController from a UIBarButtonItem to select a saved image to display.

Image

Figure 3–24. Your example from Chapter 13 (Data TransmissionRecipes) of a UIPopoverController

A UIPopoverController is easily configured to contain a content view using the designated initializer -initWithContentViewController:, and the content size can be configured using -setPopoverContentSize:animated:.If you wish to change the content view of a UIPopoverController while it is currently visible, you can make use of the -setContentViewController:animated: method.

The UIPopoverController class has a delegate protocol with methods -popoverControllerShouldDismissPopover:and-popoverControllerDidDismissPopover:. These can easily be used to make sure any user data is saved before a pop-over is dismissed.

When presenting a pop-over, you can make use of either the -presentPopoverFromRect:inView:permittedArrowDirections:animated: method, or the -presentPopoverFromBarButtonItem:permittedArrowDirections:animated: method. The former is generally used for presenting from any element in your main view, while the latter is used when presenting a pop-over from any item in a toolbar. Using these methods in their correct situation will drastically improve the visual organization quality of your application.

When designing iPad applications making use of a UIPopoverController, always keep in mind the overall visual quality of the view. Make sure your pop-over does not cover the entire screen, and that its arrow points to the element that caused it to appear.

Usually you want to avoid placing any kind of “dismiss” button inside of a UIPopoverController’s content view, as the user can simply dismiss the pop-over by tapping outside of the pop-over.

If you have the option of displaying multiple pop-over controllers from a single view, try to write your application in such a way that opening one pop-over closes any others that are open, so as to avoid obscuring the entire view.

UIPageViewController

The UIPageViewController is a controller new to iOS 5.0, designed specifically to help organize applications that deal with multiple “pages” of content that are organized on the same level of information.

An instance of UIPageViewController has its content views set using -setViewControllers:direction:animated:completion:.

The spineLocation property is used to customize the visual animation method of your page turning. You can imagine your application as a book, with the spineLocation referring to the pivot point of your page turning.

This class also has two properties, a delegateanddataSource, thathelp to configure the page view controller. The dataSource, which conforms to the UIPageViewControllerDataSource protocol, allows you to specially configure your view controller’s order through the -pageViewController:viewControllerBeforeViewController:and-pageViewController:viewControllerAfterViewController: methods. The delegateproperty deals more with the visual setup of the controller, with the UIPageViewControllerDelegate protocol method -pageViewController:spineLocationForInterfaceOrientation:.

Modal Controllers

Amodalview controller is any view controller that is presented “modally,” meaning it is shown either on top of or in place of its presenting controller. These can be anything from a normal UIViewController subclass to a specific view element. The general use of any modal view controller is to either provide or request specific information from the user, so you generally will not want to modally present a controller that implements the main functionality of your application.

Modal view controllers are presented by other view controllers using the -presentModalViewController:animated: method. Changing the modalTransitionStyle property, inherited from UIViewController, can set the style of animation. This property takes the following possible values, whose names give excellent description as to their style:

  • UIModalTransitionStyleCoverVertical
  • UIModalTransitionStyleFlipHorizontal
  • UIModalTransitionStyleCrossDissolve
  • UIModalTransitionStylePartialCurl

The differences between these options tend to be fairly cosmetic, especially when implementing modal presentation on an iPhone, although the UIModalTransitionStylePartialCurlstyle will, instead of covering the entire view, reveal only the lower area in the modal view controller. This is good for providing small amounts of information, such as your developer’s information, without having to populate the entire view.

If you make use of the UIModalTransitionStylePartialCurltransition style, avoid placing any UITextField or UITextView elements in the modal controller, as the keyboard will end up covering the entire modal controller, keeping your users from seeing what they are entering.

When developing on the iPhone, modal view controllers will always require the entire screen. However, on the iPad, you are able to customize the appearance of the controller in your view. The modalPresentationStyle property defines how your view controller is presented on an iPad, taking the following possible values:

  • UIModalPresentationFullScreen: Presents the modal controller over the entire device screen
  • UIModalPresentationPageSheet:Displays the controller with the width set as the device’s portrait width, leaving the presenting controller visible on either side; the background is then dimmed to bring focus to the presented controller.
  • UIModalPresentationFormSheet: Centers the modal controller on the iPad screen, at a smaller display size than the presenting controller; if the keyboard is visible, the view is shifted up to remain visible, and all uncovered areas are dimmed.
  • UIModalPresentationCurrentContext: The view is presented exactly as its presenting controller is. This is particularly useful when dealing with a split-pane controller or other view that does not fill the entire screen.

Whenever implementing a modal view controller, you need to be able to dismiss it programmatically. Dismissal can be implemented by calling the -dismissModalViewControllerAnimated:method from either the modal controller or the presenting controller. If you wish to dismiss your view from the presenting controller, you will need to define a delegate method for your modal controller to call when it is ready to be dismissed. This method can also be used to pass information gathered from your modal controller back into its parent controller.

Temporary User Interface Elements

Many applications deal with instances where user input is required at various points in an application, but only at that certain point. For these situations, incorporating an element directly into a view as a permanent element becomes wasteful of the precious space that you have to use. Instead, you can make use of certain elements that are shown when input or output is required, and then are dismissed afterward.

UIAlertView

A UIAlertView is an incredibly simple yet effective class. Most of the time it is used to present information, though it can be configured to allow text input.

A UIAlertView has a property alertViewStyle, which specifies the type of alert presented, with possible values:

  • UIAlertViewStyleDefault
  • UIAlertViewStyleSecureTextInput
  • UIAlertViewStylePlainTextInput
  • UIAlertViewStyleLoginAndPasswordInput

The default style refers to a simple alert presenting information, while the remaining three all allow text input. Their names give a straightforward idea of their specific uses.

The simplest way to configure a Default-styled UIAlertView is through its designated initializer,-initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:. The next example demonstrates the purpose of the configuration properties.

The following code will configure the UIAlertView shown in Figure 3–25.

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Title" message:@"This is our
message" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:@"Other Button", nil];
Image

Figure 3–25. A UIAlertView presented

Once your UIAlertView is fully configured, it can be presented quite simply by calling the -show method.

[alert show];

The UIAlertView also has a delegateproperty, as you set in the initializer, which you have set to your view controller in this example. This property conforms to the UIAlertViewDelegate protocol. It contains multiple methods called upon the presenting, cancelling, or dismissing of a UIAlertView. More importantly, though, is its -alertView:clickedButtonAtIndex: method, which allows your application to specifically react to each different button pressed.

If you wish to manually dismiss a UIAlertView, possibly by some outside event or after a certain amount of time, you can call the -dismissWithClickedButtonIndex:animated: method.

In general, the UIAlertView is used to present or request information in response to specific events, such as changes in outside conditions that affect your application, or loss in availability of some service.

UIActionSheet

The UIActionSheet class is often used in cases of presenting a user with multiple options to choose from so that an application knows how to proceed. It consists of a variety of large labeled buttons, which can have certain colors depending on the nature of their effect on the application, as in Figure 3–26.

Image

Figure 3–26. A simple UIActionSheet configured with multiple buttons

The UIActionSheet has a property actionSheetStyle. Unlike the similar property in UIAlertView, this simply determines the cosmetic style of the action sheet, with possible values:

  • UIActionSheetStyleAutomatic
  • UIActionSheetStyleDefault
  • UIActionSheetStyleBlackTranslucent
  • UIActionSheetStyleBlackOpaque

The automatic style will simply mimic, if specified, the visual style of the bottom bar. Otherwise, it will revert to the UIActionSheetStyleDefault value.

The simplest way to set up a UIActionSheet is through its designated initializer, initWithTitle:delegate:cancelButtonTitle:destructiveButtonTitle:otherButtonTitles:. The following line, for example, will reproduce the UIActionSheet displayed in Figure 3–26.

UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Title" delegate:self
cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete" otherButtonTitles:@"Other
Button 1", @"Other Button 2", nil];

As you can see, the Cancel button tends to be a darker color to indicate its result, as it is most often used to implement cancellation of some behavior. The Destructive button, however, is a bright red color in order to indicate that it usually implements some kind of method that will permanently delete user data.

When presenting a UIActionSheet, you must take into account the device being used. Due to the smaller screen of the iPhone compared to the iPad, a UIActionSheet will only ever be presented from the bottom of the view. On the iPad, however, multiple methods can be used to present the action sheet from any specified point, bar button item, toolbar, tab bar, or view.

The device being used also brings in certain considerations aboutbutton use. On the iPhone, tapping outside of a UIActionSheet does nothing, so a Cancel button is absolutely necessary. On the iPad, however, a user can usually dismiss the action sheet by tapping outside of the actual sheet. Unless the action sheet is being presented inside of a UIPopoverController, then a Cancel button is fairly unnecessary, and can be confusing.

Just as with the UIAlertView, you can manually dismiss a UIActionSheet using -dismissWithClickedButtonIndex:animated:.

Finally, your UIActionSheet’sdelegate property, which conforms to the UIActionSheetDelegate protocol, allows you to react to the presenting, cancellation, and dismissing of your action sheet, as well as the selection of each button. By implementing the -actionSheet:clickedButtonAtIndex: method, you can implement specific functionality in accordance with the selection of each option.

Summary

By this point, you should have an excellent understanding of the various elements of iOSdesign and development. We have reviewed the most commonly used design objects, and their general guidelines, as well as their most common implementations and little nuances of their use. Once you have a key understanding of the many options a developer has in creating anapplication, designing and creating useful, well-designed, and visually appealing products become simply second nature.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset