One perk of Cocoa Touch is the collection of control classes in
UIKit. A user interface control pattern includes one or more user
interface elements that work together to solve an input problem. It’s
clear that Apple put a lot of thought into the design of its controls, and
developers can easily take advantage of that work. This section will
introduce you to the most common
types of controls.1. Buttons
The simplest controls are buttons. The UIButton class is the foundation
for most buttons, including custom button subclasses. Buttons are used
extensively in Cocoa Touch applications and can be customized through
display attributes or with specialized drawing code.
1.1. Creating buttons
You can use the buttonWithType: method of
UIButton to create buttons of several different
types. Table 1 describes the various
UIButtonType keys.
Table 1. UIButtonType keys
UIButtonType key | Description |
---|
UIButtonTypeCustom | No button style. Instead, a custom
drawRect: method should be defined. |
UIButtonTypeRoundedRect | A rectangular button with rounded corners and a
centered title. Used for all general-purpose buttons. |
UIButtonTypeDetailDisclosure | A circular button with a centered chevron (>). Used
to display details of an associated object or
record. |
UIButtonTypeInfoLight | A light-colored, circular button with an italicized
lowercase “i” character. Used to flip a view over to display
settings or additional information. |
UIButtonTypeInfoDark | A dark-colored, circular button with an italicized
lowercase “i” character. Used to flip a view over to display
settings or additional information. |
UIButtonTypeContactAdd | A rectangular button with rounded corners and a
centered title. Used to display either a list of contacts or a
form for adding a new contact. |
Creating and customizing buttons is easy with the
UIButton class. Custom subclasses of
UIButton allow developers to use more attractive
buttons in their applications. In this example, a special
UIButton subclass, PrettyButton,
assigns special images to be used for the two main button states:
UIControlStateNormal and
UIControlStateHighlighted. The supplied images are
used as stretchable graphics by invoking the stretchableImageWithLeftCapWidth:topCapHeight:
method of UIImage. Stretchable images use the
concept of caps to lock a portion of the left, right, top, and bottom
of a graphic as non-stretchable, while allowing the center area
outside of the caps to grow as needed:
// PrettyButton.h
#import <Foundation/Foundation.h>
@interface PrettyButton : UIButton {
UIImage *standardImg;
UIImage *hoverImg;
}
@end
// PrettyButton.m
#import "PrettyButton.h"
@implementation PrettyButton
- (id)init
{
if(self = [super init]){
if(!standardImg){
UIImage *image = [UIImage imageNamed:@"standard.png"];
standardImg = [image stretchableImageWithLeftCapWidth:12
topCapHeight:12];
}
if(!hoverImg){
UIImage *image = [UIImage imageNamed:@"hover.png"];
hoverImg = [image stretchableImageWithLeftCapWidth:12
topCapHeight:12];
}
[self setBackgroundImage:standardImg forState:UIControlStateNormal];
[self setBackgroundImage:hoverImg forState:UIControlStateHighlighted];
[self setTitleColor:[UIColor colorWithRed:.208
green:.318
blue:.525
alpha:1.0]
forState:UIControlStateNormal];
[self setTitleColor:[UIColor whiteColor]
forState:UIControlStateHighlighted];
self.titleLabel.font = [UIFont fontWithName:@"Helvetica-Bold"
size:15.0];
self.titleLabel.textAlignment = UITextAlignmentCenter;
}
return self;
}
@end
// ButtonsViewController.h
#import <UIKit/UIKit.h>
@interface ButtonsViewController : UIViewController {
}
- (void)createStandardButton;
- (void)createPrettyButton;
- (void)standardButtonPressed:(id)sender;
- (void)prettyButtonPressed:(id)sender;
@end
// ButtonsViewController.m
#import "ButtonsViewController.h"
#import "PrettyButton.h"
@implementation ButtonsViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self createStandardButton];
[self createPrettyButton];
}
#pragma mark Standard button and click handler
- (void)createStandardButton
{
// Create a button with a rounded-rectangle style
UIButton *standardButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
float x = 30.0;
float y = 30.0;
float width = 120.0;
float height = 40.0;
// Create the frame that determines the size of the button
CGRect frame = CGRectMake(x, y, width, height);
standardButton.frame = frame;
// Set the title of the button for the normal state
[standardButton setTitle:@"Standard" forState:UIControlStateNormal];
// Add self as a target
[standardButton addTarget:self action:@selector(standardButtonPressed:)
forControlEvents:UIControlEventTouchUpInside];
// Set the button as a subview of my view
[self.view addSubview:standardButton];
}
- (void)standardButtonPressed:(id)sender
{
NSLog(@"Standard button pressed.");
}
#pragma mark Pretty button and click handler
- (void)createPrettyButton
{
// Create an instance of a custom button subclass
PrettyButton *prettyButton = [[PrettyButton alloc] init];
float x = 170.0;
float y = 30.0;
float width = 120.0;
float height = 40.0;
// Create the frame that determines the size of the button
CGRect frame = CGRectMake(x, y, width, height);
prettyButton.frame = frame;
// Set the title of the button for the normal state
[prettyButton setTitle:@"Custom" forState:UIControlStateNormal];
// Add self as a target
[prettyButton addTarget:self action:@selector(prettyButtonPressed:)
forControlEvents:UIControlEventTouchUpInside];
// Set the button as a subview of my view
[self.view addSubview:prettyButton];
[prettyButton release];
}
- (void)prettyButtonPressed:(id)sender
{
NSLog(@"Pretty button pressed.");
}
@end
Figure 1 shows the results of this
application: a standard UIButton with rounded
corners, and a custom UIButton subclass with PNG
files as background images.
1.2. Info buttons
Many iPhone applications fit into the category of lightweight utilities. These apps
tend to be very simple in their presentation of information and
require very little interaction. The utility application Xcode template provided by the
iPhone SDK is typically the foundation of such apps. One common
feature of utilities is the presence of a small icon that displays the
letter “i” and acts as a button to display more information about the
application. Additional information is most often displayed after a
“flip” animation that reverses the view. This pattern is well known
because it is the default behavior of the utility application
template, and because Apple uses it in official iPhone applications
such as the Weather application, as shown in Figure 2.
You can add an info button to your views by passing UIButtonTypeInfoLight or UIButtonTypeInfoDark to the static
buttonWithType method of the
UIButton class:
UIButton *lightButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
UIButton *darkButton = [UIButton buttonWithType:UIButtonTypeInfoDark];
You can wire an information button to flip a view using Core
Animation. In this example, the class FlipperView
represents a main view on the screen. It in turn contains two
subviews. The frontView variable is a pointer to an
instance of FrontView, a UIView
subclass. The backView variable is a pointer to an
instance of BackView, also a
UIView subclass. When the toggle
method is called, an animation transaction is created and a transition
is assigned to the FlipperView instance:
// FrontView.h
#import <UIKit/UIKit.h>
@interface FrontView : UIImageView {
}
@end
#import "FrontView.h"
#import "FlipperView.h"
@implementation FrontView
#define PADDING 10.0
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
infoButton = [UIButton buttonWithType:UIButtonTypeInfoDark];
[infoButton addTarget:self action:@selector(showInfo:)
forControlEvents:UIControlEventTouchUpInside];
infoButton.center = CGPointMake(
self.frame.size.width - infoButton.frame.size.width/2 - PADDING,
self.frame.size.height - infoButton.frame.size.height/2 - PADDING
);
[self addSubview:infoButton];
self.backgroundColor = [UIColor clearColor];
self.image = [UIImage imageNamed:@"front.png"];
self.userInteractionEnabled = YES;
}
return self;
}
- (void)showInfo:(id)sender
{
[(FlipperView *)self.superview toggle];
}
@end
// FlipperView.h
#import <UIKit/UIKit.h>
@class FrontView;
@class BackView;
@interface FlipperView : UIView {
BOOL isFlipped;
FrontView *frontView;
BackView *backView;
}
@property (assign) BOOL isFlipped;
- (void)toggle;
@end
// FlipperView.m
#import "FlipperView.h"
#import "FrontView.h"
#import "BackView.h"
@implementation FlipperView
@synthesize isFlipped;
- (id)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame]){
frontView = [[FrontView alloc] initWithFrame:self.frame];
backView = [[BackView alloc] initWithFrame:self.frame];
// Add the front view as the main content to start off.
[self addSubview:frontView];
// Insert the back view under the front view, hidden.
[self insertSubview:backView belowSubview:frontView];
self.backgroundColor = [UIColor clearColor];
self.clipsToBounds = YES;
}
return self;
}
- (void)toggle
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1];
UIViewAnimationTransition direction;
if(isFlipped){
direction = UIViewAnimationTransitionFlipFromLeft;
isFlipped = NO;
}else{
direction = UIViewAnimationTransitionFlipFromRight;
isFlipped = YES;
}
// Mid-animation, swap the views.
[self exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
[UIView setAnimationTransition:direction forView:self cache:YES];
[UIView commitAnimations];
}
- (void)dealloc
{
[frontView release];
[backView release];
[super dealloc];
}
@end
You should be careful having an information button trigger views
outside of the flip, though you needn’t necessarily avoid it. As with
most user experience programming for Cocoa Touch, it’s best to stick
to the known paths and perfect the nuances, but at the same time you
shouldn’t hesitate to evaluate new patterns.