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 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.
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 UILabel
s, making them the foundation of nearly any user interface. Figure 3–1 is the simplest example of a 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 shadowColor
andshadowOffset
properties. 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);
Compare this to a UILabel
with no shadow, as in Figure 3–3.
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.
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
.
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 UIButton
s 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);
}
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.
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];
}
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
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.
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.
The UISlider
provides a very nice and simple UI element for allowing a user to smoothly adjust values, as shown in Figure 3–9.
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:
IBAction
) in your header file, and then connect the UISlider
in your XIB file to this method by holding and dragging from the slider to the method header.-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.
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.
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 on
property, and animate the changing of its value using the -setOn:animated:
method.
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 -stopAnimating
methods. 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.
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.
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.
Just like the UIActivityIndicatorView
, UIProgressView
s can be created with a style, accessed through the progressViewStyle
property, which has the following possible values:
UIProgressViewStyleDefault
:Standard style chosen for UIProgressView
sUIProgressViewStyleBar
:Style often used inside of a toolbarThe 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.
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.
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 defersCurrentPageDisplay
property 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.
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.
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 reachmaximumValue
: The maximum value
stepValue
: The amount by which value
is incremented upon the use of the stepping buttonswraps
: 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 value
property changes or only if the user has finished changing the value, as used with the autorepeat
propertyAs before, you can assign actions to be performed upon the changing of the value
property 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.
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.
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.
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 animationImages
property, 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 isAnimating
methods.
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.
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.
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 delegate
property 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 inputView
property, 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 UITextView
eitheris 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 f
or more explanation on how to receive notifications when the keyboard appears and disappears.
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 contentInset
andcontentOffset
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 showsVerticalScrollIndicator
properties. You can even manually flash the indicators using the -flashScrollIndicators
method.
Aside from scrolling and panning, UIScrollView
s also have a zooming ability naturally built into them. In order to implement this functionality, you must simply change the maximumZoomScale
and/or minimumZoomScale
properties 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.
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 timeoutInterval
or a cachePolicy
. Once your content is loading, you can also make use of the -stopLoading
or -reload
methods as you wish.
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.
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.
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.
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 componentpickerView: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.
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.
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.
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 timeUIDatePickerModeDate
: Selects a dateUIDatePickerModeDateAndTime
: Selects both a date and time in one pickerUIDatePickerModeCountDownTimer
: Selects a countdown timer to be setIn 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.
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 UIGestureRecognizer
onlyin 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 UILabel
s, will not respond to any UIGestureRecognizer
s 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:
UIGestureRecognizerStatePossible
: Indicates a gesture is possibly in the process of being performed, but its requirements have not yet been metUIGestureRecognizerStateBegan
: Indicates a continuous gesture has been recognized and is continuingUIGestureRecognizerStateChanged
: Signifies a change to an already begun continuous gestureUIGestureRecognizerStateEnded
: Indicates a gesture has finishedUIGestureRecognizerStateCancelled
: A gesture recognizer has received touches to cancel a continuous gesture.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.
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
.
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.
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.
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 allowableMovement
property, 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.
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:
scale
: The scaling factor created by the pinchvelocity:
The velocity of the given pinch gestureDepending on your use, you will want to look for different values of state
.
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 rotation
andvelocity
properties, similar to the UIPinchGestureRecognizer
.
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.
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.
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:
-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.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.
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.
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;
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 UIViewController
s, 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.
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.
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.
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. UIPopoverController
s 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.
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.
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 delegate
anddataSource
, 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 delegate
property deals more with the visual setup of the controller, with the UIPageViewControllerDelegate
protocol method -pageViewController:spineLocationForInterfaceOrientation:
.
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 UIModalTransitionStylePartialCurl
style 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 UIModalTransitionStylePartialCurl
transition 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 screenUIModalPresentationPageSheet
: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.
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.
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];
Once your UIAlertView
is fully configured, it can be presented quite simply by calling the -show
method.
[alert show];
The UIAlertView
also has a delegate
property, 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.
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.
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.
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.