UIPickerView

UIPickerView也是一个选择器控件,它比UIDatePicker更加通用,它可以生成单列的选择器,也可生成多列的选择器,而且开发者完全可以自定义选择项的外观,因此用法非常灵活。

UIPickerView直接继承了UIView,没有继承UIControl,因此,它不能像UIControl那样绑定事件处理方法,UIPickerView的事件处理由其委托对象完成。

UIPickerView控件常用的属性和方法如下。

Ø numberOfComponents:获取UIPickerView指定列中包含的列表项的数量。该属性是一个只读属性。

Ø showsSelectionIndicator:该属性控制是否显示UIPickerView中的选中标记(以高亮背景作为选中标记)。

Ø - numberOfRowsInComponent::获取UIPickerView包含的列数量。

Ø - rowSizeForComponent::获取UIPickerView包含的指定列中列表项的大小。该方法返回一个CGSize对象。

Ø - selectRow:inComponent:animated::该方法设置选中该UIPickerView中指定列的特定列表项。最后一个参数控制是否使用动画。

Ø - selectedRowInComponent::该方法返回该UIPickerView指定列中被选中的列表项。

Ø - viewForRow:forComponent::该方法返回该UIPickerView指定列的列表项所使用的UIView控件。

UIDatePicker控件只是负责该控件的通用行为,而该控件包含多少列,各列包含多少个列表项则由UIPickerViewDataSource对象负责。开发者必须为UIPickerView设置UIPickerViewDataSource对象,并实现如下两个方法。

Ø - numberOfComponentsInPickerView::该UIPickerView将通过该方法来判断应该包含多少列。

Ø - pickerView:numberOfRowsInComponent::该UIPickerView将通过该方法判断指定列应该包含多少个列表项。

如果程序需要控制UIPickerView中各列的宽度,以及各列中列表项的大小和外观,或程序需要为UIPickerView的选中事件提供响应,都需要为UIPickerView设置UIPickerViewDelegate委托对象,并根据需要实现该委托对象中的如下方法。

Ø - pickerView:rowHeightForComponent::该方法返回的CGFloat值将作为该UIPickerView控件中指定列中列表项的高度。

Ø - pickerView:widthForComponent::该方法返回的CGFloat值将作为该UIPickerView控件中指定列的宽度。

Ø - pickerView:titleForRow:forComponent::该方法返回的NSString值将作为该UIPickerView控件中指定列的列表项的文本标题。

Ø - pickerView:viewForRow:forComponent:reusingView::该方法返回的UIView控件将直接作为该UIPickerView控件中指定列的指定列表项。

Ø - pickerView:didSelectRow:inComponent::当用户单击选中该UIPickerView控件的指定列的指定列表项时将会激发该方法。

Interface Builder只支持为UIPickerView设置一个属性——Shows Selection Indicator,该属性用于控制是否显示UIPickerView中的选中标记(以高亮背景作为选中标记)。

下面通过程序来介绍UIPickerView 的功能和用法。

†† 10.12.1  单列选择器

对于单列选择器,只要控制UIPickerView的dataSource对象的numberOfComponentsInPickerView:方法返回1即可。

下面创建一个单列选择器,首先创建一个Single View Application,使用Interface Builder打开应用的界面设计文件,并将一个UIPickerView拖入界面设计文件中,为在程序中访问该控件,需要将该控件绑定到picker IBOutlet属性。

接下来开始修改控制器类,本例打算使用控制器类作为UIPickerView的dataSource和delegate,因此,程序需要让控制器类实现UIPickerViewDataSource、UIPickerViewDelegate两个协议。

修改控制器类的实现代码,主要就是实现UIPickerViewDataSource、UIPickerViewDelegate两个协议中的必要方法,其代码如下。

程序清单:codes/10/10.12/UIPickerViewTest/UIPickerViewTest /FKViewController.m

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@implementation FKViewController
NSArray* books;
- (void)viewDidLoad
{
    [super viewDidLoad];
    // 创建并初始化NSArray对象
    books = [NSArray arrayWithObjects:@"疯狂Android讲义",
        @"疯狂iOS讲义", @"疯狂Ajax讲义" , @"疯狂XML讲义", nil];
    // 为UIPickerView控件设置dataSource和delegate
    self.picker.dataSource = self;
    self.picker.delegate = self;
}
// UIPickerViewDataSource中定义的方法,该方法的返回值决定该控件包含多少列
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView
{
    return 1;  // 返回1表明该控件只包含1列
}
// UIPickerViewDataSource中定义的方法,该方法的返回值决定该控件指定列包含多少个列表项
- (NSInteger)pickerView:(UIPickerView *)pickerView
    numberOfRowsInComponent:(NSInteger)component
{
    // 由于该控件只包含一列,因此无须理会列序号参数component
    // 该方法返回books.count,表明books包含多少个元素,该控件就包含多少行
    return books.count;
}
// UIPickerViewDelegate中定义的方法,该方法返回的NSString将作为UIPickerView
// 中指定列和列表项的标题文本
- (NSString *)pickerView:(UIPickerView *)pickerView
    titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    // 由于该控件只包含一列,因此无须理会列序号参数component
    // 该方法根据row参数返回books中的元素,row参数代表列表项的编号,
    // 因此该方法表示第几个列表项,就使用books中的第几个元素
    return [books objectAtIndex:row];
}
// 当用户选中UIPickerViewDataSource中指定列和列表项时激发该方法
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:
    (NSInteger)row inComponent:(NSInteger)component
{
    // 使用一个UIAlertView来显示用户选中的列表项
    UIAlertView* alert = [[UIAlertView alloc]
        initWithTitle:@"提示"
        message:[NSString stringWithFormat:@"你选中的图书是:%@"
            , [books objectAtIndex:row]]
        delegate:nil
        cancelButtonTitle:@"确定"
        otherButtonTitles:nil];
    [alert show];
}
@end

上面的程序首先初始化了一个NSArray,接下来的关键就是实现了4个用粗体字表示的方法,其中有两个方法来自UIPickerViewDataSource协议,分别用于控制该UIPickerView控件包含多少列、各列包含多少个列表项;另两个方法则来自UIPickerViewDelegate,最后一个粗体字方法负责为UIPickerView控件的选中事件提供响应——当用户选中该UIPickerView的某个列表项时,系统将会自动激发该方法,该方法的实现逻辑就是使用UIAlertView显示用户选择的图书。

编译、运行该程序,可以看到如图10.46所示的效果。

图10.46  单列UIPickerView

 

 

如果用户选择某个列表项,程序将会弹出如图10.47所示的UIAlertView警告框。

 

 

                            

   图10.47  选中指定项

 

 10.12.2  多列选择器

对单列选择器而言,只要控制UIPickerView的dataSource对象的numberOfComponentsInPickerView:方法返回大于1的整数即可。

本节创建一个多列选择器,首先创建一个Single View Application,使用Interface Builder打开应用的界面设计文件,并将一个UIPickerView拖入界面设计文件中,为在程序中访问该控件,需要将该控件绑定到picker IBOutlet属性。

接下来修改控制器类,本例打算使用控制器类作为UIPickerView的dataSource和delegate,因此程序需要让控制器类实现UIPickerViewDataSource、UIPickerViewDelegate两个协议。

修改控制器类的实现代码,主要就是实现UIPickerViewDataSource、UIPickerViewDelegate两个协议中的必要方法,其代码如下。

程序清单:codes/10/10.12/MultiPickerTest/MultiPickerTest/FKViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
@implementation FKViewController
NSArray* books;
NSArray* authors;
- (void)viewDidLoad
{
    [super viewDidLoad];
    // 创建并初始化两个NSArray对象,分别作为两列的数据
    authors = [NSArray arrayWithObjects:@"泰戈尔",
             @"冯梦龙", @"李刚" , nil];
    books = [NSArray arrayWithObjects:@"飞鸟集" , @"吉檀迦利"
             , @"醒世恒言",@"喻世明言", @"警世通言", @"疯狂Android讲义",
             @"疯狂iOS讲义", @"疯狂Ajax讲义" , @"疯狂XML讲义", nil];
    self.picker.dataSource = self;
    self.picker.delegate = self;
}
// UIPickerViewDataSource中定义的方法,该方法返回值决定该控件包含多少列
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView
{
    return 2;  // 返回2表明该控件只包含2列
}
// UIPickerViewDataSource中定义的方法,该方法的返回值决定该控件指定列包含多少个列表项
- (NSInteger)pickerView:(UIPickerView *)pickerView
    numberOfRowsInComponent:(NSInteger)component
{
    // 如果是第一列,返回authors中元素的个数
    // 即authors包含多少个元素,第一列就包含多少个列表项
    if (component == 0) {
        return authors.count;
    }
    // 如果是其他列,返回books中元素的个数。
    // 即books包含多少个元素,其他列(只有第二列)包含多少个列表项
    return books.count;
}
// UIPickerViewDelegate中定义的方法,该方法返回的NSString将作为
// UIPickerView中指定列和列表项上显示的标题
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:
    (NSInteger)row forComponent:(NSInteger)component
{
    // 如果是第一列,返回authors中row索引处的元素
    // 即第一列的列表项标题由authors集合元素决定
    if (component == 0) {
        return [authors objectAtIndex:row];
    }
    // 如果是其他列(只有第二列),返回books中row索引处的元素
    // 即第二列的列表项标题由books集合元素决定
    return [books objectAtIndex:row];
}
// 当用户选中UIPickerViewDataSource中指定列和列表项时激发该方法
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:
    (NSInteger)row inComponent:(NSInteger)component
{
    NSArray* tmp  = component == 0 ? authors: books;
    NSString* tip = component == 0 ? @"作者": @"图书";
    // 使用一个UIAlertView来显示用户选中的列表项
    UIAlertView* alert = [[UIAlertView alloc]
        initWithTitle:@"提示"
        message:[NSString stringWithFormat:@"你选中的%@是:%@,"
             , tip, [tmp objectAtIndex:row]]
        delegate:nil
        cancelButtonTitle:@"确定"
        otherButtonTitles:nil];
    [alert show];
}
// UIPickerViewDelegate中定义的方法,该方法返回的NSString将作为
// UIPickerView中指定列的宽度
- (CGFloat)pickerView:(UIPickerView *)pickerView
    widthForComponent:(NSInteger)component
{
    // 如果是第一列,宽度为90
    if (component == 0) {
        return 90;
    }
    return 210;  // 如果是其他列(只有第二列),宽度为210
}
@end

 

 

该程序与前一个程序基本相似,同样需要实现上面4个方法,只是numberOfComponentsInPickerView:方法返回了2,因此,该UIPickerView包含两列,程序创建了两个NSArray,分别为两列提供数据项。除此之外,上面的程序还实现了一个pickerView:widthForComponent:方法,该方法的返回值控制UIPickerView中各列的宽度。

编译、运行该程序,可以看到如图10.48所示的两列选择器。

如果用户选中指定的列表项,该应用同样会弹出UIAlertView显示用户的选择,弹出的警告框与图10.47类似。

†† 10.12.3  相互依赖的多列选择器

10.12.2节的示例虽然是一个两列的UIPickerView控件,但该控件的两列基本没有任何关系,选择第一列的作者时,第二列的图书不会动态更新——在某些情况下,这是允许的。但在某些情况下,我们需要第二列的列表项依赖第一列的选择,即当第一列选择作者时,第二列只显示该作者的图书。

为了让第二列能根据第一列的选择动态加载,程序需要用户选择第一列的事件,并根据该事件动态加载第二列的数据,然后强制重新加载UIDatePicker的第二列列表项。

下面创建一个相互依赖的多列选择器,首先创建一个Single View Application,使用Interface Builder打开应用的界面设计文件,并将一个UIPickerView拖入界面设计文件中,为在程序中访问该控件,需要将该控件绑定到picker IBOutlet属性。

接下来开始修改控制器类,本例使用控制器类作为UIPickerView的dataSource和delegate,因此,程序需要让控制器类实现UIPickerViewDataSource、UIPickerViewDelegate两个协议。

修改控制器类的实现代码,主要就是实现UIPickerViewDataSource、UIPickerViewDelegate两个协议中的必要方法,其代码如下。

程序清单:codes/10/10.12/MultiPickerTest2/MultiPickerTest2/FKViewController.m

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
@implementation FKViewController
NSDictionary* books;
NSArray* authors;
// selectedAuthor保存当前选中的作者
NSString* selectedAuthor;
- (void)viewDidLoad
{
    [super viewDidLoad];
    // 创建并初始化NSDictionary对象
    books = [NSDictionary dictionaryWithObjectsAndKeys:
         [NSArray arrayWithObjects:@"飞鸟集" , @"吉檀迦利", nil]
         , @"泰戈尔",
         [NSArray arrayWithObjects:@"醒世恒言",@"喻世明言"
         , @"警世通言", nil] , @"冯梦龙",
         [NSArray arrayWithObjects:@"疯狂Android讲义",
         @"疯狂iOS讲义", @"疯狂Ajax讲义" , @"疯狂XML讲义", nil]
         , @"李刚" ,nil];
    // 使用authors保存books所有key组成的NSArray排序后的结果
    authors = [[books allKeys] sortedArrayUsingSelector:
        @selector(compare:)];
    // 设置默认选中的作者authors中的第一个元素
    selectedAuthor = [authors objectAtIndex:0];
    self.picker.dataSource = self;
    self.picker.delegate = self;
}
// UIPickerViewDataSource中定义的方法,该方法的返回值决定该控件包含多少列
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView
{
    return 2;  // 返回2表明该控件只包含2列
}
// UIPickerViewDataSource中定义的方法,该方法的返回值决定该控件指定列包含多少个列表项
- (NSInteger)pickerView:(UIPickerView *)pickerView
    numberOfRowsInComponent:(NSInteger)component
{
    // 如果是第一列,返回authors中元素的个数
    // 即authors包含多少个元素,第一列包含多少个列表项
    if (component == 0) {
        return authors.count;
    }
    // 如果是其他列(只有第二列),
    // 返回books中selectedAuthor对应的NSArray中元素的个数
    return [[books objectForKey:selectedAuthor] count];
}
// UIPickerViewDelegate中定义的方法,该方法返回的NSString将作为
// UIPickerView中指定列和列表项上显示的标题
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:
    (NSInteger)row forComponent:(NSInteger)component
{
    // 如果是第一列,返回authors中row索引处的元素
    // 即第一列的元素由authors集合元素决定
    if (component == 0) {
        return [authors objectAtIndex:row];
    }
    // 如果是其他列(只有第二列),
    // 返回books中selectedAuthor对应的NSArray中row索引处的元素
    return [[books objectForKey:selectedAuthor] objectAtIndex:row];
}
// 当用户选中UIPickerViewDataSource中指定列和列表项时激发该方法
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row
       inComponent:(NSInteger)component
{
    if(component == 0)
    {
        // 改变被选中的作者
        selectedAuthor = [authors objectAtIndex:row];
        // 控制重写加载第二个列表,根据选中的作者来加载第二个列表
        [self.picker reloadComponent:1];
    }
    NSArray* tmp  = component == 0 ? authors:
        [books objectForKey:selectedAuthor];
    NSString* tip = component == 0 ? @"作者": @"图书";
    // 使用一个UIAlertView来显示用户选中的列表项
    UIAlertView* alert = [[UIAlertView alloc]
        initWithTitle:@"提示"
        message:[NSString stringWithFormat:@"你选中的%@是:%@,"
                , tip , [tmp objectAtIndex:row]]
        delegate:nil
        cancelButtonTitle:@"确定"
        otherButtonTitles:nil];
    [alert show];
}
// UIPickerViewDelegate中定义的方法,该方法返回的NSString将作为
// UIPickerView中指定列的宽度
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:
    (NSInteger)component
{
    // 如果是第一列,宽度为90
    if (component == 0) {
        return 90;
    }
    return 210;  // 如果是其他列(只有第二列),宽度为210
}
@end

上面控制器类的实现部分与前一个示例中控制器类的实现部分大致相同,关键就是程序中的粗体字代码。本程序采用了NSDictionary分别保存NSPickerView控件中的两列数据,NSDictionary的所有key组成的集合作为第1列的数据,当用户选中第一列的某个作者后,程序取出NSDictionary中该作者对应的图书集合作为第二列的数据。这就可以让第二列数据随第一列的选择动态改变。

编译、运行该程序,在第一列中选中某个作者,可以看到如图10.49所示的效果。

如果用户选中指定的列表项,该应用同样会弹出UIAlertView显示用户的选择,弹出的警告框与图10.47类似。

†† 10.12.4  自定义选择器视图

前面示例看到的所有列表项都是文字形式,实际上,UIPickerView允许开发者对列表项进行任意定制。开发者只要实现UIPickerViewDelegate协议中的-pickerView:viewForRow:forComponent: reusingView:方法即可,该方法返回的UIView将作为UIPickerView指定列和列表项的视图控件。

实例:“lao hu ji ”游戏

下面以一个简单的“lao hu ji ”游戏实例来示范自定义选择器视图的用法。

首先创建一个Single View Application,使用Interface Builder打开应用的界面设计文件,并将一个UIPickerView拖入界面设计文件中,再将一个UIImageView和一个UIButton拖入应用界面中,然后将这三个控件摆放整齐。为了在程序中访问这三个控件,分别将它们绑定到picker、image、startBn这三个IBAction属性。除此之外,程序还需要响应按钮的点击事件,因此为该按钮控件的Touch Up Inside事件绑定clicked:事件处理方法。

提示:

“lao hu ji ”游戏的列表项都是图标,则需要为该实例准备一些图标——读者既可用本实例提供的图标,也可用自己选择的图标,但图标不要太大,以小于60像素×60像素为宜,将这些图标拖入应用中即可。

 

接下来开始修改控制器类,本例使用控制器类作为UIPickerView的dataSource和delegate,因此程序需要让控制器类实现UIPickerViewDataSource、UIPickerViewDelegate两个协议。除此之外,程序需要分别为UIPickerView的5列准备数据,因此程序在控制器类的接口部分定义了5个NSArray属性,其代码如下。

程序清单:codes/10/10.12/CustomPicker/CustomPicker/FKViewController.h

 

1
2
3
4
5
6
7
8
@interface FKViewController : UIViewController
    <UIPickerViewDataSource, UIPickerViewDelegate>
// 分别绑定到应用界面的三个UI控件
@property (strong, nonatomic) IBOutlet UIPickerView *picker;
@property (strong, nonatomic) IBOutlet UIImageView *image;
@property (strong, nonatomic) IBOutlet UIButton *startBn;
- (IBAction)clicked:(id)sender;
@end

然后修改该控制器类的实现部分,主要就是实现UIPickerViewDataSource、UIPickerViewDelegate两个协议中的必要方法,其代码如下。

程序清单:codes/10/10.12/CustomPicker/CustomPicker/FKViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
@implementation FKViewController
UIImage* loseImage;
UIImage* winImage;
// 保存系统中所有图片的集合
NSArray* images;
- (void)viewDidLoad
{
    [super viewDidLoad];
    loseImage = [UIImage imageNamed:@"lose.jpg"];
    winImage = [UIImage imageNamed:@"win.gif"];
    // 依次加载6张图片,生成对应的UIImage对象
    UIImage* dog = [UIImage imageNamed:@"dog.png"];
    UIImage* duck = [UIImage imageNamed:@"duck.png"];
    UIImage* elephant = [UIImage imageNamed:@"elephant.png"];
    UIImage* frog = [UIImage imageNamed:@"frog.png"];
    UIImage* mouse = [UIImage imageNamed:@"mouse.png"];
    UIImage* rabbit = [UIImage imageNamed:@"rabbit.png"];
    // 初始化images集合,将前面的6张图片封装成images集合
    images = [NSArray arrayWithObjects: dog, duck, elephant,
        frog, mouse, rabbit,nil];
    self.picker.dataSource = self;
    self.picker.delegate = self;
}
// UIPickerViewDataSource中定义的方法,该方法的返回值决定该控件包含多少列
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView
{
    // 返回5表明该控件只包含5列
    return 5;
}
// UIPickerViewDataSource中定义的方法,该方法的返回值决定该控件指定列包含多少个列表项
- (NSInteger)pickerView:(UIPickerView *)pickerView
    numberOfRowsInComponent:(NSInteger)component
{
    // images集合包含多少个元素,该控件的各列就包含多少个列表项
    return images.count;
}
#define kImageTag 1
// UIPickerViewDelegate中定义的方法,该方法返回的UIView将作为
// UIPickerView中指定列和列表项的UI控件
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:
    (NSInteger)row forComponent:(NSInteger)component
    reusingView:(UIView *)view
{
    // 如果可重用的view的tag不等于kImageTag,表明该view已经不存在,需要重新创建
    if(view.tag != kImageTag)
    {
        view = [[UIImageView alloc] initWithImage:[images objectAtIndex:row]];
        // 为该UIView设置tag属性
        view.tag = kImageTag;
        // 设置不允许用户交互
        view.userInteractionEnabled = NO;
    }
    return view;
}
// UIPickerViewDelegate中定义的方法,该方法的返回值决定列表项的高度
- (CGFloat)pickerView:(UIPickerView *)pickerView
    rowHeightForComponent:(NSInteger)component
{
    return 40;
}
// UIPickerViewDelegate中定义的方法,该方法的返回值决定列表项的宽度
- (CGFloat)pickerView:(UIPickerView *)pickerView
    widthForComponent:(NSInteger)component
{
    return 40;
}
- (IBAction)clicked:(id)sender {
    self.startBn.enabled = NO;  // 禁用该按钮
    self.image.image = nil;  // 清空界面上image控件中的图片
    // 定义一个NSMutableDictionary来记录每个随机数的出现次数
    NSMutableDictionary* result = [[NSMutableDictionary alloc]
        initWithCapacity:6];
    NSURL *winSoundUrl = [[NSBundle mainBundle]
        URLForResource:@"crunch" withExtension:@"wav"];
    SystemSoundID soundId;
    // 装载声音文件
    AudioServicesCreateSystemSoundID((__bridge CFURLRef)
        (winSoundUrl), &soundId);
    // 播放声音
    AudioServicesPlaySystemSound(soundId);
    for(int i = 0 ; i < 5 ; i++)
    {
        // 生成0~images.count之间的一个随机数
        NSUInteger selectedVal = arc4random() % images.count;
        [self.picker selectRow:selectedVal inComponent:i animated:YES];
        // 在result中已经为该随机数记录了出现次数
        if([result objectForKey:[NSNumber numberWithInt:selectedVal]])
        {
            // 获取result中该随机数的出现次数
            NSUInteger newCount = [[result objectForKey:
                [NSNumber numberWithInt:selectedVal]] integerValue];
            // 将result中该随机数的出现次数+1
            [result setObject:[NSNumber numberWithInt:(newCount + 1)]
                forKey:[NSNumber numberWithInt:selectedVal]];
        }
        else
        {
            // 使用result记录该随机数的出现次数为1
            [result setObject:[NSNumber numberWithInt:1]
                forKey:[NSNumber numberWithInt:selectedVal]];
        }
        // 使用该变量记录随机数的最大出现次数
        NSUInteger maxOccurs = 1;
        for (NSNumber* num in [result allKeys])
        {
            // 只要任何随机数的出现次数大于maxOccurs
            if ([[result objectForKey:num] integerValue] > maxOccurs)
            {
                // 使用maxOccurs保存该随机数的出现次数
                maxOccurs = [[result objectForKey:num] integerValue];
            }
        }
        // 如果某个随机数的出现次数大于或等于3(即使界面出现了3个相同的图案)
        if(maxOccurs >= 3)
        {
            // 如果赢了,延迟0.5秒执行showWin方法,显示结果
            [self performSelector:@selector(showWin)
                withObject:nil afterDelay:0.5];
        }
        else
        {
            // 如果输了,延迟0.5秒执行showLose方法,显示结果
            [self performSelector:@selector(showLose)
                withObject:nil afterDelay:0.5];
        }
    }
}
- (void) showWin
{
    NSURL *winSoundUrl = [[NSBundle mainBundle]
        URLForResource:@"win" withExtension:@"wav"];
    SystemSoundID soundId;
    // 装载声音文件
    AudioServicesCreateSystemSoundID((__bridge CFURLRef)
        (winSoundUrl), &soundId);
    // 播放声音
    AudioServicesPlaySystemSound(soundId);
    self.image.image = winImage;
    self.startBn.enabled = YES;
}
- (void) showLose
{
    self.image.image = loseImage;
    self.startBn.enabled = YES;
}
@end

上面程序中为UIPickerView实现自定义列表项的关键就是第一段粗体字代码,该方法返回一个UIImageView作为各列的列表项控件。因此,该示例中UIPickerView所包含的各列的列表项都是UIImageView控件。

接下来的两行粗体字代码负责生成一个随机数,并根据生成的随机数来选中UIPickerView的指定列表项,这样就可让UIPickerView的5列随机选中指定的列表项——程序接下来的代码判断5个随机数中是否有相同的数出现过3次以上(意味着UIPickerView的5列中出现了3个以上相同的图标)——如果符合该条件,程序就延迟0.5秒执行showWin方法(该方法就是播放胜利音乐并显示胜利图标),否则就延迟0.5秒执行showLose方法(该方法显示失败图标)。

由于该程序使用AudioToolbox来播放音乐,因此程序还需要将win.wav和crunch.wav两个音乐文件拖入项目中。除此之外,iOS项目默认是不带AudioToolbox库的,因此需要手动添加AudioToolbox库,为iOS项目添加指定库的步骤如下。

① 在项目导航面板中单击指定项目对应的图标。

② 选中中间编辑区域左侧的TARGETS下面的应用,如图10.50所示。

图10.50  添加库

③ 选择编辑窗口上方的Build Phases标签,单击Link Binary With Libraries旁边的三角符号,即可展开该项目当前包含的所有库,如图10.51所示。

 图10.51  查看项目已包含的库

 

 

④ 单击图10.51所示窗口中“添加库”的“+”图标,系统将会显示如图10.52所示的库列表。

⑤ 在图10.52所示的对话框中选中需要添加的库(也可通过上面的搜索框进行搜索),单击“Add”按钮即可完成添加。

 图10.52  选择需要添加的库  

按上面所示步骤为该项目添加AudioToolbox库,然后编译、运行该程序,即可通过单击程序界面上的按钮开始游戏,游戏界面如图10.53所示。

 

图10.53  自定义UIPickerView实现lao hu ji 

 

 

 

 

 

————本文节选自《疯狂ios讲义》

 

posted @ 2016-04-12 16:54  w_only  阅读(258)  评论(0编辑  收藏  举报