Tải bản đầy đủ - 0 (trang)
Chapter 25. Controls and Other Views

Chapter 25. Controls and Other Views

Tải bản đầy đủ - 0trang

Figure 25-1. A large activity indicator

v.color = [UIColor yellowColor];

v.backgroundColor = [UIColor colorWithWhite:0.2 alpha:0.4];



An activity indicator has a standard size, which depends on its style. Changing its size

in code changes the size of the region painted by its backgroundColor, but not the size

of the spinning spokes.

Here’s some code from a UITableViewCell subclass in one of my apps. In this app, it

takes some time, after the user taps a cell to select it, for me to construct the next view

and navigate to it, so to cover the delay, I show a spinning activity indicator in the center

of a cell while it’s selected:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {

if (selected) {

UIActivityIndicatorView* v =

[[UIActivityIndicatorView alloc]

initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];

v.center =

CGPointMake(self.bounds.size.width/2.0, self.bounds.size.height/2.0);

v.frame = CGRectIntegral(v.frame);

v.tag = 1001;

[self.contentView addSubview:v];

[v startAnimating];

} else {

[[self.contentView viewWithTag:1001] removeFromSuperview];

// no harm if nonexistent

}

[super setSelected:selected animated:animated];

}



If activity involves the network, you might want to set UIApplication’s networkActivityIndicatorVisible to YES. This displays a small spinning activity indicator in the status

bar. The indicator is not reflecting actual network activity; if it’s visible, it’s spinning.

Be sure to set it back to NO when the activity is over.

An activity indicator is simple and standard, but you can’t change the way it’s drawn.

If you want your own custom activity indicator, though, it’s easy to make one. One

obvious way is to use a UIImageView with a sequence of custom images forming an

animation (animationImages), as described in Chapter 17.



UIProgressView

A progress view (UIProgressView) is a “thermometer,” graphically displaying a percentage. It is often used to represent a time-consuming process whose percentage of

completion is known (if the percentage of completion is unknown, you’re more likely



708 | Chapter 25: Controls and Other Views



www.it-ebooks.info



Figure 25-2. A progress view



to use an activity indicator), but it might also be used to represent a fairly static percentage. For example, in one of my apps, I use a progress view to show the current

position within the song being played by the built-in music player; in another app,

which is a card game, I use a progress view to show how many cards are left in the deck.

A progress view comes in a style, its progressViewStyle; if the progress view is created

in code, you’ll set its style with initWithProgressViewStyle:. Your choices are:

• UIProgressViewStyleDefault

• UIProgressViewStyleBar

The latter is intended for use in a UIBarButtonItem, as the title view of a navigation

item, and so on.

The height (the narrow dimension) of a progress view is generally not up to you; it’s

determined by the progress view’s style. Changing a progress view’s height has no

visible effect on how the thermometer is drawn and is not a good idea.

The fullness of the thermometer is the progress view’s progress property. This is a value

between 0 and 1, inclusive; obviously, you’ll need to do some elementary arithmetic in

order to convert from the actual value you’re reflecting to a value within that range.

For example, to reflect the number of cards remaining in a deck of 52 cards:

prog.progress = [[deck cards] count] / 52.0;



Starting in iOS 5, you can animate the change from one progress value to another by

calling setProgress:animated:.

Also new in iOS 5, you can customize the colors or images of the parts of the progress

view. To customize the colors, set the progress view’s progressTintColor and trackTintColor (the track is the unfilled part of the progress view). To customize the images, set

the progress view’s progressImage and trackImage; these, if set, override the tint colors.

The images will be squashed and stretched to fill the bounds of the track and the progress “thermometer,” so in all probability you’ll use a resizable image whose height is

the progress view’s standard height (9 points). The “thermometer” image is not clipped

to the track image, so seeing that it fits inside is up to you. In this simple example, the

track image and progress image are squares rotated 45 degress, so that they both have

pointed ends:

UIGraphicsBeginImageContextWithOptions(CGSizeMake(9,9), NO, 0);

CGContextRef con = UIGraphicsGetCurrentContext();

CGContextSetFillColorWithColor(con, [UIColor blackColor].CGColor);

CGContextMoveToPoint(con, 0, 4.5);

CGContextAddLineToPoint(con, 4.5, 9);

CGContextAddLineToPoint(con, 9, 4.5);

CGContextAddLineToPoint(con, 4.5, 0);



UIProgressView | 709



www.it-ebooks.info



Figure 25-3. A custom progress view

CGContextClosePath(con);

CGPathRef p = CGContextCopyPath(con);

CGContextFillPath(con);

UIImage* im = UIGraphicsGetImageFromCurrentImageContext();

CGContextSetFillColorWithColor(con, [UIColor whiteColor].CGColor);

CGContextAddPath(con, p);

CGContextFillPath(con);

UIImage* im2 = UIGraphicsGetImageFromCurrentImageContext();

CGPathRelease(p);

UIGraphicsEndImageContext();

im = [im resizableImageWithCapInsets:UIEdgeInsetsMake(4, 4, 4, 4)];

im2 = [im2 resizableImageWithCapInsets:UIEdgeInsetsMake(4, 4, 4, 4)];

prog.trackImage = im;

prog.progressImage = im2;



Despite its new customizability, you can’t really change the way a progress view is

drawn; for example, you can’t make it taller. If you want your own custom progress

view, it’s easy to make one; all you need is a custom UIView subclass that draws something similar to a thermometer. Figure 25-3 shows a simple custom thermometer view;

it has a value property, and you set this to something between 0 and 1 and then call

setNeedsDisplay to get the view to redraw itself. Here’s its drawRect: code:

- (void)drawRect:(CGRect)rect {

CGContextRef c = UIGraphicsGetCurrentContext();

[[UIColor whiteColor] set];

CGFloat ins = 2.0;

CGRect r = CGRectInset(self.bounds, ins, ins);

CGFloat radius = r.size.height / 2.0;

CGMutablePathRef path = CGPathCreateMutable();

CGPathMoveToPoint(path, NULL, CGRectGetMaxX(r) - radius, ins);

CGPathAddArc(path, NULL,

radius+ins, radius+ins, radius, -M_PI/2.0, M_PI/2.0, true);

CGPathAddArc(path, NULL,

CGRectGetMaxX(r) - radius, radius+ins, radius, M_PI/2.0, -M_PI/2.0, true);

CGPathCloseSubpath(path);

CGContextAddPath(c, path);

CGContextSetLineWidth(c, 2);

CGContextStrokePath(c);

CGContextAddPath(c, path);

CGContextClip(c);

CGContextFillRect(c, CGRectMake(

r.origin.x, r.origin.y, r.size.width * self.value, r.size.height));

}



710 | Chapter 25: Controls and Other Views



www.it-ebooks.info



UIPickerView

A UIPickerView displays selectable choices using a rotating drum metaphor. It has a

standard legal range of possible heights, which is undocumented and must be discovered by trial and error (attempting to set the height outside this range will fail with a

warning in the console); its width is largely up to you. Each drum, or column, is called

a component.

Your code configures the UIPickerView’s content through its data source (UIPickerViewDataSource) and delegate (UIPickerViewDelegate), which are usually the same

object (see also Chapter 11). Your data source and delegate must answer questions

similar to those posed by a UITableView (Chapter 21):

numberOfComponentsInPickerView: (data source)



How many components (drums) does this picker view have?

pickerView:numberOfRowsInComponent: (data source)



How many rows does this component have? The first component is numbered 0.

pickerView:titleForRow:forComponent:

pickerView:viewForRow:forComponent:reusingView: (delegate)



What should this row of this component display? The first row is numbered 0. You

can supply either a simple title string or an entire view such as a UILabel, giving

you more control over formatting, but you must supply every row of every component the same way, because if viewForRow is implemented, titleForRow isn’t

called. The reusingView parameter, if not nil, is a view that you supplied for a row

now no longer visible, giving you a chance to reuse it, much as cells are reused in

a table view.

Here’s the code for a UIPickerView (Figure 25-4) that displays the names of the 50 U.S.

states, obtained from a text file. We implement pickerView:viewForRow:forComponent:

reusingView: just because it’s the more interesting case; as our views, we supply UILabel

instances. The state names, drawn from an NSArray property states, are drawn centered because the labels are themselves centered within the picker view:

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {

return 1;

}

- (NSInteger)pickerView:(UIPickerView *)pickerView

numberOfRowsInComponent:(NSInteger)component {

return 50;

}

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row

forComponent:(NSInteger)component reusingView:(UIView *)view {

UILabel* lab;

if (view)

lab = (UILabel*)view; // reuse it

else



UIPickerView | 711



www.it-ebooks.info



Figure 25-4. A picker view



}



lab = [[UILabel alloc] init];

lab.text = [self.states objectAtIndex:row];

lab.backgroundColor = [UIColor clearColor];

[lab sizeToFit];

return lab;



The delegate may further configure the UIPickerView’s physical appearance by means

of these methods:

• pickerView:rowHeightForComponent:

• pickerView:widthForComponent:

The delegate may implement pickerView:didSelectRow:inComponent: to be notified

each time the user spins a drum to a new position. You can also query the picker view

directly by sending it selectedRowInComponent:.

You can set the value to which any drum is turned using selectRow:inComponent:

animated:. Other handy picker view methods allow you to request that the data be

reloaded, and there are properties and methods to query the picker view’s contents

(though of course they do not relieve you of responsibility for knowing the data model

from which the picker view’s contents are supplied):













reloadComponent:

reloadAllComponents

numberOfComponents

numberOfRowsInComponent:

viewForRow:forComponent:



By implementing pickerView:didSelectRow:inComponent: and using reloadComponent:

you can make a picker view where the values displayed by one drum depend dynamically on what is selected in another. For example, one can imagine expanding our U.S.

states example to include a second drum listing major cities in each state; when the

user switches to a different state in the first drum, a different set of major cities appears

in the second drum.



712 | Chapter 25: Controls and Other Views



www.it-ebooks.info



Figure 25-5. A search bar with a search results button



UISearchBar

A search bar (UISearchBar) is essentially a variety of text field, though it is not in fact

a UITextField subclass. It is displayed by default as a rounded rectangle containing a

magnifying glass icon, where the user can enter text (Figure 25-5). It does not, of itself,

do any searching or display the results of a search; a common interface involves displaying the results of a search as a table, and the UISearchDisplayController class makes

this easy to do (see Chapter 21).

A search bar’s current text is its text property. It can have a placeholder, which appears

when there is no text. A prompt can be displayed above the search bar to explain its

purpose. Delegate methods (UISearchBarDelegate) notify you of editing events; for

their use, compare the text field and text view delegate methods discussed in Chapter 23:















searchBarShouldBeginEditing:

searchBarTextDidBeginEditing:

searchBar:textDidChange:

searchBar:shouldChangeTextInRange:replacementText:

searchBarShouldEndEditing:

searchBarTextDidEndEditing:



A search bar has a barStyle, for which your choices are UIBarStyleDefault or UIBarStyleBlack, and either translucent or not. Alternatively, the search bar may have a

tintColor. The bar style and tint color are the same as for a navigation bar or toolbar,

and are drawn the same way a navigation bar or toolbar would draw them (see later in

this chapter); because of this, a search bar looks good at the top of the screen, where a

navigation bar or toolbar might go — in effect, it is drawn as if it were a navigation bar

or toolbar.

Starting in iOS 5, a search bar can have a custom backgroundImage; this will be treated

as a resizable image (that is, it will be either stretched or tiled; see Chapter 15), and

overrides the bar style or tint color.

The search field area where the user enters text can be offset with respect to its background with the searchFieldBackgroundPositionAdjustment property; you might use

this, for example, if you had enlarged the search bar’s height and wanted to position

the field area within that height. The text can be offset within the field area with the

searchTextPositionAdjustment property.



UISearchBar | 713



www.it-ebooks.info



You can also replace the image of the search field itself; this is the image that is normally

a rounded rectangle. The image you supply will be drawn in front of the background

and behind the contents of the search field (such as the text); its width will be adjusted

for you, but its height will not be — instead, the image is placed vertically centered

where the search field needs to go, and choosing an appropriate height, as well as

ensuring a light-colored area in the middle so the user can read the text, is up to you.

A search bar displays an internal cancel button automatically (normally an “x” in a

circle) if there is text in the search field. Internally, at its right end, a search bar may

display a search results button (showsSearchResultsButton), which may be selected or

not (searchResultsButtonSelected), or a bookmark button (showsBookmarkButton); if

you ask to display both, you’ll get the search results button. These buttons vanish if

text is entered in the search bar so that the cancel button can be displayed. There is

also an option to display a Cancel button externally (showsCancelButton, or call setShowsCancelButton:animated:). The internal cancel button works automatically to remove whatever text is in the field; the other buttons do nothing, but delegate methods

notify you when they are tapped:

• searchBarResultsListButtonClicked:

• searchBarBookmarkButtonClicked:

• searchBarCancelButtonClicked:

Starting in iOS 5, you can customize the images used for the internal left icon (the

magnifying glass, by default) and any of the internal right icons (the cancel button, the

search results button, and the bookmark button) with setImage:forSearchBarIcon:

state:. The image you supply may be sized down as required, but sometimes it isn’t,

so perhaps it is best to size the image first; about 20×20 seems to be a good size. The

state: value can be UIControlStateNormal or UIControlStateDisabled; if you don’t

supply an image for a state, the default image for that state is used. The icons are

specified with constants:











UISearchBarIconSearch

UISearchBarIconClear

UISearchBarIconBookmark

UISearchBarIconResultsList



The position of an icon can be adjusted with setPositionAdjustment:forSearchBarIcon:.

A search bar may also display scope buttons (see the example in Chapter 21). These

are intended to let the user alter the meaning of the search; precisely how you use them

is up to you. To make the scope buttons appear, use the showsScopeBar property; the

button titles are the scopeButtonTitles property, and the currently selected scope button is the selectedScopeButtonIndex property. The delegate is notified when the user

taps a different scope button:



714 | Chapter 25: Controls and Other Views



www.it-ebooks.info



• searchBar:selectedScopeButtonIndexDidChange:

Starting in iOS 5, the look of the scope bar can be heavily customized. Its background

is the scopeBarBackgroundImage, which will be stretched or tiled as needed. To set the

background of the smaller area constituting the actual buttons, call setScopeBarButtonBackgroundImage:forState:; the states are UIControlStateNormal and UIControlStateSelected. If you don’t supply a separate selected image, a darkened version of the normal image is used. If you don’t supply a resizable image, the image will be made resizable

for you; the runtime decides what region of the image will be stretched behind each

button.

The dividers between the buttons are normally vertical lines, but you can customize

them as well: call setScopeBarButtonDividerImage:forLeftSegmentState:rightSegmentState:. A full complement of dividers consists of three images, one when the buttons

on both sides of the divider are normal (unselected) and one each when a button on

one side or the other is selected; if you supply an image for just one state combination,

it is used for the other two state combinations. The height of the divider image is adjusted for you, but the width is not; you’ll normally use an image just a few pixels wide.

The font attributes of the titles of the scope buttons can customized with respect to

their font, color, shadow color, and shadow offset; this is done by calling setScopeBarButtonTitleTextAttributes:forState:. The attributes: argument is a dictionary

whose possible keys will be used for several interface objects in this chapter, so I’ll call

it a text attributes dictionary:











UITextAttributeFont, a UIFont; a zero size means “the default size”

UITextAttributeTextColor, a UIColor

UITextAttributeTextShadowColor, a UIColor

UITextAttributeTextShadowOffset, a UIOffset wrapped up as an NSValue



(All the customizing set... methods I’ve mentioned have a corresponding getter,

whose name is the same without the “set” prefix.)

It may appear that there is no way to customize the external Cancel button, but in fact,

although you’ve no direct access to it through the search bar, the Cancel button is a

UIBarButtonItem and you can customize it using the UIBarButtonItem appearance

proxy, discussed later in this chapter.

By combining the various customization possibilities, a completely unrecognizable

search bar of inconceivable ugliness can easily be achieved (Figure 25-6). Let’s be careful

out there.

The problem of allowing the keyboard to appear without hiding the search bar is exactly

as for a text field (Chapter 23). Text input properties of the search bar configure its

keyboard and typing behavior like a text field as well: keyboardType, autocapitalizationType, and autocorrectionType (and, new in iOS, its spellCheckingType, which lets you

turn spell checking on and off). When the user taps the Search key in the keyboard, the



UISearchBar | 715



www.it-ebooks.info



Figure 25-6. A horrible search bar



delegate is notified, and it is then up to you to dismiss the keyboard (resignFirstResponder) and perform the search:

• searchBarSearchButtonClicked:

A common interface on the iPad is to embed a search bar as a bar button item’s view

in a toolbar at the top of the screen. This approach has its pitfalls; for example, there

is no room for a prompt, and scope buttons or an external Cancel button may not

appear either. One rather slimy workaround is to layer the search bar over the toolbar

rather than having it genuinely live in the toolbar. Another is to have the search bar

itself occupy the position of the toolbar at the top of the screen. On the other hand, a

search bar in a toolbar that is managed by a UISearchDisplayController will automatically display search results in a popover, which can be a considerable savings of time

and effort (though the popover controller is unfortunately out of your hands); see

Chapter 22 for an example. An interesting thing about that example, which I didn’t

mention before, is that the search bar contains a results list button that summons the

popover when tapped, and in that case the popover’s top bar contains a Clear button

that empties the search bar and dismisses the popover; that behavior is apparently

entirely automatic and due to the search display controller.



UIControl

UIControl is a subclass of UIView whose chief purpose is to be the superclass of several

further built-in classes and to endow them with common behavior. These are classes

representing views with which the user can interact (controls).

The most important thing that controls have in common is that they automatically

track and analyze touch events (Chapter 18) and report them to your code as significant

control events by way of action messages. Each control implements some subset of the

possible control events; see Chapter 11 for a list of which control events are implemented by which controls. For each control event that you want to hear about automatically, you attach to the control one or more target–action pairs. You can do this

in the nib (Chapter 7) or in code (Chapter 11).

For any given control, each control event and its target–action pairs form a dispatch

table. The following methods permit you to manipulate and query the dispatch table:

• addTarget:action:forControlEvents:



716 | Chapter 25: Controls and Other Views



www.it-ebooks.info













removeTarget:action:forControlEvents:

actionsForTarget:forControlEvent:

allTargets

allControlEvents (a bitmask of control events to which a target–action pair is at-



tached)

An action selector may adopt any of three signatures, whose parameters are (see Chapter 11):

• The control and the UIEvent

• The control only

• No parameters

To make a control emit its action message to a particular control event right now, in

code, call its sendActionsForControlEvents: method (which is never called automatically by the framework). For example, suppose you tell a UISwitch programmatically

to change its setting from Off to On. This doesn’t cause the switch to report a control

event, as it would if the user had slid the switch from off to on; if you wanted it to do

so, you could use sendActionsForControlEvents:, like this:

[switch setOn: YES animated: YES];

[switch sendActionsForControlEvents:UIControlEventValueChanged];



You might also use sendActionsForControlEvents: in a subclass to customize the circumstances under which a control reports control events.

A control has enabled, selected, and highlighted properties; any of these can be YES

or NO independently of the others. These correspond to its state, which is reported

as a bitmask of three possible values:

• UIControlStateHighlighted

• UIControlStateDisabled

• UIControlStateSelected

A fourth state, UIControlStateNormal, corresponding to a zero state bitmask, means

that enabled, selected, and highlighted are all NO.

A control that is not enabled does not respond to user interaction; whether the control

also portrays itself differently, to cue the user to this fact, depends upon the control.

For example, a disabled UISwitch is faded. But a round rect text field, unless you explicitly configure it to display a different background image when disabled (Chapter 23), gives the user no cue that it is disabled. The visual nature of control selection

and highlighting, too, depends on the control. Neither highlighting nor selection make

any difference to the appearance of a UISwitch, but a highlighted UIButton usually

looks quite different from a nonhighlighted UIButton.



UIControl | 717



www.it-ebooks.info



Figure 25-7. A switch in iOS 5



Figure 25-8. A switch in iOS 4



A control has contentHorizontalAlignment and contentVerticalAlignment properties.

Again, these matter only if the control has content that can be aligned. You are most

likely to use these properties in connection with a UIButton to position its title and

internal image.

A text field (UITextField) is a control; see Chapter 23. The remaining controls are

covered here, and then I’ll give a simple example of writing your own custom control.



UISwitch

A UISwitch portrays a BOOL value: it looks like a sliding switch whose positions are

labeled ON and OFF, and its on property is either YES or NO. The user can slide or

tap to toggle the switch’s position. When the user changes the switch’s position, the

switch reports a Value Changed control event. To change the on property’s value with

accompanying animation, call setOn:animated:.

A switch has only one size (apparently 79×27); any attempt to set its size will be ignored.

The default size is not as wide as it was in earlier system versions, and the way the

switch is drawn has changed; in iOS 5 it appears as in Figure 25-7, but previously it

appeared as in Figure 25-8. In iOS 5 you can set a switch’s onTintColor, but that’s the

only customization you can perform. You can’t customize the ON and OFF labels; the

only solution is to roll your own switch-like interface widget (several third-party implementations are available).

Don’t name a UISwitch instance variable or property switch, as this is

a reserved word in C.



UIStepper

A UIStepper (new in iOS 5, Figure 25-9) lets the user increase or decrease a numeric

value: it looks like two buttons side by side, one labeled with a minus sign, the other

with a plus sign. The user can slide a finger from one button to the other as part of the

same interaction with the stepper. It has only one size (apparently 94×27). It maintains

a numeric value, which is its value. Each time the user increments or decrements the

718 | Chapter 25: Controls and Other Views



www.it-ebooks.info



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Chapter 25. Controls and Other Views

Tải bản đầy đủ ngay(0 tr)

×