1. Trang chủ >
  2. Công Nghệ Thông Tin >
  3. Kỹ thuật lập trình >

Chapter 12. Controls and Other Views

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (12.71 MB, 929 trang )


Figure 12-1. A large activity indicator

You can assign an activity indicator a color; this overrides the color assigned through

the style. An activity indicator is a UIView, so you can set its backgroundColor; a nice

effect is to give an activity indicator a contrasting background color and to round its

corners by way of the view’s layer (Figure 12-1).

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; to cover the delay, I show a spinning activity indicator in the center

of the cell while it’s selected:

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

[super setSelected:selected animated:animated];

if (selected) {

UIActivityIndicatorView* v =

[[UIActivityIndicatorView alloc]

initWithActivityIndicatorStyle:

UIActivityIndicatorViewStyleWhiteLarge];

v.color = [UIColor yellowColor];

dispatch_async(dispatch_get_main_queue(), ^{

// cell tries to change background color match selection color

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

});

v.layer.cornerRadius = 10;

CGRect f = v.frame;

f = CGRectInset(f, -10, -10);

v.frame = f;

CGRect cf = self.frame;

cf = [self.contentView convertRect:cf fromView:self];

v.center = CGPointMake(CGRectGetMidX(cf), CGRectGetMidY(cf));

v.tag = 1001;

[self.contentView addSubview:v];

[v startAnimating];

} else {

UIView* v = [self viewWithTag:1001];

if (v) {

[v removeFromSuperview];

}

}

}



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



564



| Chapter 12: Controls and Other Views



www.it-ebooks.info



Figure 12-2. A progress view

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.

One obvious alternative would be a UIImageView with an animated image, as described

in Chapter 4.



UIProgressView

A progress view (UIProgressView) is a “thermometer,” graphically displaying a per‐

centage. 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

to use an activity indicator). But it’s good for static percentages too. 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

UIProgressViewStyleBar is intended for use in a UIBarButtonItem, as the title view of

a navigation item, and so on. In iOS 7, both styles by default draw the thermometer

extremely thin — just 2 pixels and 3 pixels, respectively. (Figure 12-2 shows a

UIProgressViewStyleDefault progress view.) Changing a progress view’s frame height

directly has no visible effect on how the thermometer is drawn. Under autolayout, to

make a thicker thermometer, supply a height constraint with a larger value (thus over‐

riding the intrinsic content height).



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

between 0 and 1, inclusive; you’ll usually 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;



A change in progress value can be animated by calling setProgress:animated:.



UIProgressView



www.it-ebooks.info



|



565



Figure 12-3. A thicker progress view using a custom progress image

In iOS 7, the default color of the filled portion of a progress view is the tintColor (which

may be inherited from higher up the view hierarchy). The default color for the unfilled

portion is gray for a UIProgressViewStyleDefault progress view and transparent for

a UIProgressViewStyleBar progress view. You can customize the colors; set the pro‐

gress view’s progressTintColor and trackTintColor, respectively. This can also be

done in the nib.

Alternatively, you can customize the image used to draw the filled portion of the progress

view, the progress view’s progressImage. If you do that, you can optionally customize

also the image used to draw the unfilled portion, the trackImage. This can also be done

in the nib. Each image must be stretched to the length of the filled or unfilled area, so

you’ll want to use a resizable image.

Here’s a simple example from one of my apps (Figure 12-3):

self.prog.backgroundColor = [UIColor blackColor];

self.prog.trackTintColor = [UIColor blackColor];

UIGraphicsBeginImageContextWithOptions(CGSizeMake(10,10), YES, 0);

CGContextRef con = UIGraphicsGetCurrentContext();

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

CGContextFillRect(con, CGRectMake(0, 0, 10, 10));

CGRect r = CGRectInset(CGContextGetClipBoundingBox(con),1,1);

CGContextSetLineWidth(con, 2);

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

CGContextStrokeRect(con, r);

CGContextStrokeEllipseInRect(con, r);

self.prog.progressImage =

[UIGraphicsGetImageFromCurrentImageContext()

resizableImageWithCapInsets:UIEdgeInsetsMake(0, 4, 0, 4)];

UIGraphicsEndImageContext();



For maximum flexibility, you can design your own UIView subclass that draws some‐

thing similar to a thermometer. Figure 12-4 shows a simple custom thermometer view;

it has a value property, and you set this to something between 0 and 1 and call setNeedsDisplay to make the view 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();



566



|



Chapter 12: Controls and Other Views



www.it-ebooks.info



Figure 12-4. A custom progress view

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

CGPathAddArc(path, nil,

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

CGPathAddArc(path, nil,

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));

}



UIPickerView

A picker view (UIPickerView) displays selectable choices using a rotating drum meta‐

phor. It has a standard legal range of possible heights, which is undocumented but seems

to be between 162 and 180; 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 (UIPicker‐

ViewDataSource) and delegate (UIPickerViewDelegate), which are usually the same

object. Your data source and delegate must answer questions similar to those posed by

a UITableView (Chapter 8):

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:attributedTitleForRow:forComponent:

pickerView:viewForRow:forComponent:reusingView: (delegate)



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

can supply a simple string, an attributed string (Chapter 10), or an entire view such

as a UILabel; but you should supply every row of every component the same way.

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



UIPickerView



www.it-ebooks.info



|



567



Figure 12-5. A picker view

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 12-5) that displays the names of the

50 U.S. states, stored in an array. We implement pickerView:viewForRow:forComponent:reusingView: just because it’s the most interesting case; as our views, we

supply UILabel instances. The state names appear centered because the labels are cen‐

tered 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

lab = [UILabel new];

lab.text = self.states[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:



568



|



Chapter 12: Controls and Other Views



www.it-ebooks.info



Figure 12-6. A search bar with a search results button

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 con‐

tents (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.



UISearchBar

A search bar (UISearchBar) is essentially a wrapper for a text field; it has a text field as

one of its subviews, though there is no official access to it. It is displayed by default as a

rounded rectangle containing a magnifying glass icon, where the user can enter text

(Figure 12-6). 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 8).

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



UISearchBar



www.it-ebooks.info



|



569



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

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

• searchBarShouldBeginEditing:

• searchBarTextDidBeginEditing:

• searchBar:textDidChange:

• searchBar:shouldChangeTextInRange:replacementText:

• searchBarShouldEndEditing:

• searchBarTextDidEndEditing:

A search bar has a barStyle, for which your choices and their default appearances are:

• UIBarStyleDefault, a flat light gray background and a white search field

• UIBarStyleBlack, a black background and a black search field

In iOS 7, both styles are translucent. In addition, iOS 7 introduces a searchBarStyle

property:

• UISearchBarStyleDefault, as already described

• UISearchBarStyleProminent, identical to UISearchBarStyleDefault

• UISearchBarStyleMinimal, transparent background and dark transparent search

field

Alternatively, you can set a UISearchBarStyleDefault search bar’s barTintColor to

change its background color; if the bar style is UIBarStyleBlack, the barTintColor will

also tint the search field itself. An opaque barTintColor is a way to make a search bar

opaque. This property is new in iOS 7; the old tintColor property, whose value may

be inherited from higher up the view hierarchy, now governs the color of search bar

components such as the Cancel button title and the flashing insertion cursor.

A search bar can also have a custom backgroundImage; this will be treated as a resizable

image. The full setter method in iOS 7 is setBackgroundImage:forBarPosition:barMetrics:; I’ll talk later about what bar position and bar metrics are. The backgroundImage overrides all other ways of determining the background, and the search bar’s

backgroundColor, if any, appears behind it — though under some circumstances, if the

search bar’s translucent is NO, the barTintColor may appear behind it instead.

The search field area where the user enters text can be offset with respect to its back‐

ground, using the searchFieldBackgroundPositionAdjustment property; you might

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



570



|



Chapter 12: Controls and Other Views



www.it-ebooks.info



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

searchTextPositionAdjustment property.

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

a rounded rectangle. To do so, call setSearchFieldBackgroundImage:forState:. Ac‐

cording to the documentation, the possible state: values are UIControlStateNormal

and UIControlStateDisabled; but the API provides no way to disable a search field,

so what does Apple have in mind here? The only way I’ve found is to cycle through the

search bar’s subviews, find the search field, and disable it:

for (UIView* v in [self.searchbar.subviews[0] subviews]) {

if ([v isKindOfClass: [UITextField class]]) {

UITextField* tf = (UITextField*)v;

tf.enabled = NO;

break;

}

}



The search field image 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. It’s up to you choose an appropriate height, and to ensure an appropriate color in

the middle so the user can read the text.

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:

You can customize the images used for the search icon (a magnifying glass, by default)

and any of the internal right icons (the internal cancel button, the search results button,

and the bookmark button) with setImage:forSearchBarIcon:state:. The images will

be resized for you, except for the internal cancel button, for which about 20×20 seems

to be a good size. The icons are specified with constants:



UISearchBar



www.it-ebooks.info



|



571



• UISearchBarIconSearch

• UISearchBarIconClear (the internal cancel button)

• UISearchBarIconBookmark

• UISearchBarIconResultsList

The documentation says that the possible state: values are UIControlStateNormal

and UIControlStateDisabled, but this is wrong; the choices are UIControlStateNormal and UIControlStateHighlighted. The highlighted image appears while the

user taps on the icon (except for the search icon, which isn’t a button). If you don’t supply

a normal image, the default image is used; if you supply a normal image but no high‐

lighted image, the normal image is used for both. Setting searchResultsButtonSelected to YES reverses this button’s behavior: it displays the highlighted image, but

when the user taps it, it displays the normal image.

The position of an icon can be adjusted with setPositionAdjustment:forSearchBar-



Icon:.



A search bar may also display scope buttons (see the example in Chapter 8). 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:

• searchBar:selectedScopeButtonIndexDidChange:



A background image applied with setBackgroundImage:forBarPosition:barMetrics: using UIBarMetricsDefault will not appear

when the scope bar is showing. In iOS 7, when the scope bar is show‐

ing, the bar metrics value becomes UIBarMetricsDefaultPrompt (or

UIBarMetricsLandscapePhonePrompt).



The overall 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.

572



|



Chapter 12: Controls and Other Views



www.it-ebooks.info



Figure 12-7. A horrible search bar

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 by calling setScopeBarButtonTitleTextAttributes:forState:. In iOS 7, the attributes: argument is

an NSAttributedString attributes dictionary; in earlier systems, you could set only the

font, text color, and text shadow, but in iOS 7 it is legal to provide other attributes, such

as an underline.

It may appear that there is no way to customize the external Can‐

cel button, but in fact, although you’ve no official 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 12-7). 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 10). Text input properties of the search bar configure its

keyboard and typing behavior like a text field as well:

• keyboardType

• autocapitalizationType

• autocorrectionType



UISearchBar



www.it-ebooks.info



|



573



• spellCheckingType

• inputAccessoryView

When the user taps the Search key in the keyboard, the delegate is notified, and it is

then up to you to dismiss the keyboard (resignFirstResponder) and perform the

search:

• searchBarSearchButtonClicked:

A common interface is a search bar at the top of the screen. On the iPad, a search bar

can be embedded as a bar button item’s view in a toolbar at the top of the screen. On

the iPhone, a search bar can be a navigation item’s titleView. In Chapter 8, I discussed

UISearchDisplayController’s displaysSearchBarInNavigationBar property. A search

bar used in this way, however, has some limitations: for example, there may be no room

for a prompt, scope buttons, or an external Cancel button, and you might not be able

to assign it a background image or change its barTintColor.

On the other hand, in iOS 7, a UISearchBar can itself function as a top bar, like a navi‐

gation bar without being in a navigation bar. If you use a search bar in this way, you’ll

want its height to be extended automatically under the status bar; I’ll explain later in

this chapter how to arrange that.



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 5) 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. The full set of control events is listed under UIControlEvents in the

Constants section of the UIControl class documentation:

• UIControlEventTouchDown

• UIControlEventTouchDownRepeat

• UIControlEventTouchDragInside

• UIControlEventTouchDragOutside

• UIControlEventTouchDragEnter

• UIControlEventTouchDragExit

• UIControlEventTouchUpInside

574



|



Chapter 12: Controls and Other Views



www.it-ebooks.info



Xem Thêm
Tải bản đầy đủ (.pdf) (929 trang)

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

Tải bản đầy đủ ngay
×