学习目标
1.【理解】应用的需求
2.【理解】设置图标和启动图片
3.【理解】创建模型类加载数据
4.【理解】搭建基本界面并初始化
5.【理解】实现图片放大缩小功能
6.【理解】动态创建答案区
7.【理解】动态创建选项区
8.【理解】答案区和选项区的交互
9.【理解】实现提示按钮功能
一、应用的需求
应用需求:
选项区:点击选项区中的按钮,可将文字显示到答案区的按钮上。
答案区:点击答案区的按钮,会将对应的文字返回到选项区。
下一题:进入下一题,重新加载各种数据。
正误判断:如果答案错误则文字变红,正确则变绿并进入下一题。
图片缩放:点击图片、大图按钮都能实现图片放大缩小功能。
大图后的阴影:图片放大后会有一层阴影,点击阴影可以缩小图片。
提示:能清空答案区先有文字并取出正确答案的第一个文字显示到第一个按钮中。
帮助:目前没有增加功能,可以随意增加。
积分:答对一题加100分,答错一题扣100分,点击提示扣100分。答题完毕或分数小于0则提示游戏结束,是否重新开始。
图片有点大,点击链接查看动态图:点击这里查看动态图
二、设置图标和启动图片
创建项目,先去掉Ceneral中的Launch Screen File选项,并选择Launch Images Source为Images。
将启动界面的所有素材图片拖到Images.xcassets中的LaunchImage目录,Xcode会自动适配各种屏幕和版本。
将app用到的各种图标素材拖到Images.xcassets中的AppIcon目录,这样启动图片和各种图片都设置好了。
将app用到的各种图片、图标素材和plist文件导入,注意有plist文件就先创建Model类加载数据。
三、创建模型类加载数据
创建的Model类命名应该与plist文件名保存一致,并且根据plist文件中的数据类型定义Model类的属性。
1
2
3
4
5
|
//根据plist文件中的字典key来定义属性
@property(copy,nonatomic)NSString*answer; //答案
@property(copy,nonatomic)NSString*icon; //图像
@property(copy,nonatomic)NSString*title; //标题
@property(strong,nonatomic)NSArray*options;//选项区数据
|
Model类一般会提供快速创建模型的对象方法和类方法,还有快速返回一个模型数组的类方法。
JFQuestions.h文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#import <Foundation/Foundation.h>
@interface JFQuestions : NSObject
//根据plist文件中的字典key来定义属性
@property(copy,nonatomic)NSString*answer; //答案
@property(copy,nonatomic)NSString*icon; //图像
@property(copy,nonatomic)NSString*title; //标题
@property(strong,nonatomic)NSArray*options;//选项区数据
//快速创建模型对象的对象方法
-(instancetype)initWithDictionary:(NSDictionary*)dict;
//快速创建模型对象的类方法
+(instancetype)questionsWithDictionary:(NSDictionary*)dict;
//返回模型数组
+(NSArray*)questions;
@end
|
JFQuestions.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
|
#import "JFQuestions.h"
@implementationJFQuestions
//快速创建模型对象的对象方法
-(instancetype)initWithDictionary:(NSDictionary*)dict{
if(self=[superinit]){
//根据字典初始化对象的属性
self.answer=dict[@"answer"];
self.icon=dict[@"icon"];
self.title=dict[@"title"];
self.options=dict[@"options"];
}
returnself;
}
//快速创建模型对象的类方法
+(instancetype)questionsWithDictionary:(NSDictionary*)dict{
return[[selfalloc] initWithDictionary:dict];
}
//返回模型数组
+(NSArray*)questions{
//加载plist文件
NSArray*array=[NSArray arrayWithContentsOfFile:[[NSBundlemainBundle] pathForResource:@"questions.plist" ofType:nil]];
//创建可变数组存储创建的模型对象
NSMutableArray*arrayM=[NSMutableArrayarray];
for(NSDictionary*dictinarray){
JFQuestions*question=[self questionsWithDictionary:dict];
[arrayM addObject:question];
}
//返回创建好的模型数组
returnarrayM;
}
@end
|
创建好Model类后,先在Contronller中定义一个数组属性用于存储模型对象,并重新这个属性的get方法加载模型数组。
ViewController.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
|
#import "ViewController.h"
#import "JFQuestions.h"
@interfaceViewController()
@property(strong,nonatomic)NSArray*questions;//模型数组
@end
@implementationViewController
-(void)viewDidLoad{
[superviewDidLoad];
}
-(void)didReceiveMemoryWarning{
[superdidReceiveMemoryWarning];
}
#pragma mark - 懒加载数据到数组
-(NSArray*)questions{
if(_questions==nil){
//调用Model类的questions方法快速返回一个模型数组
_questions=[JFQuestionsquestions];
}
return_questions;
}
@end
|
四、搭建基本界面并初始化
背景图:先拖拽一个UIImageView并全屏,用于设置应用的背景图。
积分:最好是Button,因为积分旁边有个小图标,可以通过设置按钮Image来实现。
索引:只显示文字数据,所以用Label。
标题:只显示文字数据,所以用Label。
图片:图片可点击,所以用Button。
其他按钮:用脚趾头都能想到使用按钮,并设置。
设置界面控件的各种属性,我们这里先不考虑答案区、选项区。
然后各种控件拖线,拖线的原则是要调用控件(修改控件属性等)就设置为属性,如果只是点击后触发一个事件就连线方法。
ViewController.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
|
#import "ViewController.h"
#import "JFQuestions.h"
@interfaceViewController()
@property(strong,nonatomic)NSArray*questions;//模型数组
@property(assign,nonatomic)intindex;//模型数组的索引(下标)
@property(weak,nonatomic)IBOutletUIButton*scoreView;//显示积分
@property(weak,nonatomic)IBOutletUILabel*indexView;//显示索引
@property(weak,nonatomic)IBOutletUILabel*titleView;//显示标题
@property(weak,nonatomic)IBOutletUIButton*iconView;//表示图片
@property(weak,nonatomic)IBOutletUIButton*nextButton;//下一题按钮
-(IBAction)tip;//提示按钮
-(IBAction)help;//帮助按钮
-(IBAction)bigImage;//大图按钮
-(IBAction)next;//下一题按钮
@end
@implementationViewController
-(void)viewDidLoad{
[superviewDidLoad];
//加载界面完界面让模型数组索引为-1
self.index=-1;
//调用next方法即可显示索引为0的数据
[selfnext];
}
-(void)didReceiveMemoryWarning{
[superdidReceiveMemoryWarning];
}
#pragma mark - 懒加载数据到数组
-(NSArray*)questions{
if(_questions==nil){
//调用Model类的questions方法快速返回一个模型数组
_questions=[JFQuestionsquestions];
}
return_questions;
}
#pragma mark - 提示
-(IBAction)tip{
}
#pragma mark - 帮助
-(IBAction)help{
}
#pragma mark - 图片变大
-(IBAction)bigImage{
}
#pragma mark - 下一题
-(IBAction)next{
//模型数组索引自增
self.index++;
//加载界面数据
[selfloadDataView];
//如果到了最后一题,就禁用下一题按钮
self.nextButton.enabled=self.index!=self.questions.count-1;
}
#pragma mark - 加载数据
-(void)loadDataView{
//取出当前索引的模型对象
JFQuestions*question=self.questions[self.index];
//加载索引数据
self.indexView.text=[NSString stringWithFormat:@"%d/%lu",self.index+1,self.questions.count];
//加载标题数据
self.titleView.text=question.title;
//加载图片数据,注意这里是设置按钮图片不是,按钮背景图
[self.iconView setImage:[UIImage imageNamed:question.icon] forState:UIControlStateNormal];
}
@end
|
加载界面并初始化索引为0的模型数组的各种数据到控件上显示,并对下一题按钮进行监控。如果到最后一题就禁用下一题按钮,防止下标越界。
实现效果如下图:
五、实现图片放大缩小功能
阴影控制:阴影不能直接加到界面,这样会覆盖掉包括图片的整个界面,所以放大实现中要将图片放到最外一层。阴影背景会不止在一个方法中调用,所以我们最好设置为属性,方便调用。
图片缩放:点击大图、图片都能实现图片放大,点击图片和阴影背景都能实现图片缩小。图片放大、缩小和阴影出现、消失都有渐变效果,所以使用动画来实现。图片实现缩放,必须要知道原先图片的frame,所以我们最好设置一个属性保存图片原先的frame。
注意:点击图片默认会有点击效果,去掉Highlighted Adjusts Image即可解决。
ViewController.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
#import "ViewController.h"
#import "JFQuestions.h"
@interfaceViewController()
#pragma mark - 属性
@property(strong,nonatomic)NSArray *questions; //模型数组
@property(assign,nonatomic)int index; //模型数组的索引(下标)
@property(weak,nonatomic)IBOutletUIButton*scoreView; //显示积分
@property(weak,nonatomic)IBOutletUILabel *indexView; //显示索引
@property(weak,nonatomic)IBOutletUILabel *titleView; //显示标题
@property(weak,nonatomic)IBOutletUIButton*iconView; //表示图片
@property(weak,nonatomic)IBOutletUIButton*nextButton; //下一题按钮
@property(assign,nonatomic)CGRect iconFrame; //图片原始frame
@property(weak,nonatomic)UIButton *shade; //阴影背景
#pragma mark - 方法
-(IBAction)tip; //提示按钮
-(IBAction)help; //帮助按钮
-(IBAction)bigImage; //大图按钮
-(IBAction)iconButton;//点击图片
-(IBAction)next; //下一题按钮
-(void)smallImage; //图片变小
@end
@implementationViewController
-(void)viewDidLoad{
[superviewDidLoad];
//加载界面完界面让模型数组索引为-1
self.index=-1;
//调用next方法即可显示索引为0的数据
[selfnext];
//保存图片元素frame值
self.iconFrame=self.iconView.frame;
}
-(void)didReceiveMemoryWarning{
[superdidReceiveMemoryWarning];
}
#pragma mark - 懒加载数据到数组
-(NSArray*)questions{
if(_questions==nil){
//调用Model类的questions方法快速返回一个模型数组
_questions=[JFQuestionsquestions];
}
return_questions;
}
#pragma mark - 提示
-(IBAction)tip{
}
#pragma mark - 帮助
-(IBAction)help{
}
#pragma mark - 图片变大
-(IBAction)bigImage{
//创建阴影背景,因为可以点击所以使用UIButton
UIButton*shade=[[UIButtonalloc] init];
//设置阴影背景的frame
shade.frame=CGRectMake(0,0,375,667);
//设置阴影背景的背景色
[shade setBackgroundColor:[UIColorblackColor]];
//设置阴影背景的初始透明度(为了在动画效果中渐变)
shade.alpha=0;
//将shade添加父视图
[self.view addSubview:shade];
//计算图片放大后的frame
CGFloaticonW=self.view.frame.size.width;
CGFloaticonH=iconW;
CGFloaticonX=0;
CGFloaticonY=(self.view.frame.size.height-iconH)/2;
//动画渐变设置图片放大、阴影背景出现的效果
[UIView animateWithDuration:1 animations:^{
self.iconView.frame=CGRectMake(iconX,iconY,iconW,iconH);
shade.alpha=0.6;
}];
//将图片控件调整到最外层
[self.view bringSubviewToFront:self.iconView];
//将背景赋值给属性(因为要在其他方法中让阴影消失,需要访问到阴影背景控件)
self.shade=shade;
//为阴影背景注册单击事件,点击后图片缩放,并让去掉阴影背景
[shade addTarget:self action:@selector(smallImage) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark - 图片变小
-(void)smallImage{
//动画渐变变小效果
[UIView animateWithDuration:1 animations:^{
//将图片frame还原,将阴影背景设为透明
self.iconView.frame=self.iconFrame;
self.shade.alpha=0;
} completion:^(BOOLfinished){
//去掉阴影背景
self.shade=nil;
}];
}
#pragma mark - 点击图片
-(IBAction)iconButton{
if(self.shade==nil){
//如果没有阴影背景就说明是缩小状态,点击后放大
[selfbigImage];
}else{
//如果有就说明是放大状态,点击后缩小
[selfsmallImage];
}
}
#pragma mark - 下一题
-(IBAction)next{
//模型数组索引自增
self.index++;
//加载界面数据
[selfloadDataView];
//如果到了最后一题,就禁用下一题按钮
self.nextButton.enabled=self.index!=self.questions.count-1;
}
#pragma mark - 加载数据
-(void)loadDataView{
//取出当前索引的模型对象
JFQuestions*question=self.questions[self.index];
//加载索引数据
self.indexView.text=[NSString stringWithFormat:@"%d/%lu",self.index+1,self.questions.count];
//加载标题数据
self.titleView.text=question.title;
//加载图片数据,注意这里是设置按钮图片不是,按钮背景图
[self.iconView setImage:[UIImage imageNamed:question.icon] forState:UIControlStateNormal];
}
@end
|
实现效果如下图:
六、动态创建答案区
答案区我们可以动态创建答案按钮在一个View中,然后把这个View添加到父视图(屏幕)上。View我们可以直接拖控件,再循环创建按钮控件并添加到这个View上。和我们前两天写的九宫格大同小异,非常easy,这里我就不分析计算过程了。
ViewController.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
#import "ViewController.h"
#import "JFQuestions.h"
@interfaceViewController()
#pragma mark - 属性
@property(strong,nonatomic)NSArray *questions; //模型数组
@property(assign,nonatomic)int index; //模型数组的索引(下标)
@property(weak,nonatomic)IBOutletUIButton*scoreView; //显示积分
@property(weak,nonatomic)IBOutletUILabel *indexView; //显示索引
@property(weak,nonatomic)IBOutletUILabel *titleView; //显示标题
@property(weak,nonatomic)IBOutletUIButton*iconView; //表示图片
@property(weak,nonatomic)IBOutletUIButton*nextButton; //下一题按钮
@property(assign,nonatomic)CGRect iconFrame; //图片原始frame
@property(weak,nonatomic)UIButton *shade; //阴影背景
@property(weak,nonatomic)IBOutletUIView *answerView; //答案区View
#pragma mark - 方法
-(IBAction)tip; //提示按钮
-(IBAction)help; //帮助按钮
-(IBAction)bigImage; //大图按钮
-(IBAction)iconButton;//点击图片
-(IBAction)next; //下一题按钮
-(void)smallImage; //图片变小
-(void)loadAnswerView;//加载答案区
@end
@implementationViewController
-(void)viewDidLoad{
[superviewDidLoad];
//加载界面完界面让模型数组索引为-1
self.index=-1;
//调用next方法即可显示索引为0的数据
[selfnext];
//保存图片元素frame值
self.iconFrame=self.iconView.frame;
}
-(void)didReceiveMemoryWarning{
[superdidReceiveMemoryWarning];
}
#pragma mark - 懒加载数据到数组
-(NSArray*)questions{
if(_questions==nil){
//调用Model类的questions方法快速返回一个模型数组
_questions=[JFQuestionsquestions];
}
return_questions;
}
#pragma mark - 提示
-(IBAction)tip{
}
#pragma mark - 帮助
-(IBAction)help{
}
#pragma mark - 图片变大
-(IBAction)bigImage{
//创建阴影背景,因为可以点击所以使用UIButton
UIButton*shade=[[UIButtonalloc] init];
//设置阴影背景的frame
shade.frame=CGRectMake(0,0,375,667);
//设置阴影背景的背景色
[shade setBackgroundColor:[UIColorblackColor]];
//设置阴影背景的初始透明度(为了在动画效果中渐变)
shade.alpha=0;
//将shade添加父视图
[self.view addSubview:shade];
//计算图片放大后的frame
CGFloaticonW=self.view.frame.size.width;
CGFloaticonH=iconW;
CGFloaticonX=0;
CGFloaticonY=(self.view.frame.size.height-iconH)/2;
//动画渐变设置图片放大、阴影背景出现的效果
[UIView animateWithDuration:1 animations:^{
self.iconView.frame=CGRectMake(iconX,iconY,iconW,iconH);
shade.alpha=0.6;
}];
//将图片控件调整到最外层
[self.view bringSubviewToFront:self.iconView];
//将背景赋值给属性(因为要在其他方法中让阴影消失,需要访问到阴影背景控件)
self.shade=shade;
//为阴影背景注册单击事件,点击后图片缩放,并让去掉阴影背景
[shade addTarget:self action:@selector(smallImage) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark - 图片变小
-(void)smallImage{
//动画渐变变小效果
[UIView animateWithDuration:1 animations:^{
//将图片frame还原,将阴影背景设为透明
self.iconView.frame=self.iconFrame;
self.shade.alpha=0;
} completion:^(BOOLfinished){
//去掉阴影背景
self.shade=nil;
}];
}
#pragma mark - 点击图片
-(IBAction)iconButton{
if(self.shade==nil){
//如果没有阴影背景就说明是缩小状态,点击后放大
[selfbigImage];
}else{
//如果有就说明是放大状态,点击后缩小
[selfsmallImage];
}
}
#pragma mark - 下一题
-(IBAction)next{
//模型数组索引自增
self.index++;
//加载界面数据
[selfloadDataView];
//如果到了最后一题,就禁用下一题按钮
self.nextButton.enabled=self.index!=self.questions.count-1;
}
#pragma mark - 加载数据
-(void)loadDataView{
//取出当前索引的模型对象
JFQuestions*question=self.questions[self.index];
//加载索引数据
self.indexView.text=[NSString stringWithFormat:@"%d/%lu",self.index+1,self.questions.count];
//加载标题数据
self.titleView.text=question.title;
//加载图片数据,注意这里是设置按钮图片不是,按钮背景图
[self.iconView setImage:[UIImage imageNamed:question.icon] forState:UIControlStateNormal];
//加载答案区
[selfloadAnswerView];
}
#pragma mark - 加载答案区
-(void)loadAnswerView{
//清除上一次加载的答案区所有按钮
for(UIButton*answerinself.answerView.subviews){
[answer removeFromSuperview];
}
//获取当前索引的模型对象
JFQuestions*question=self.questions[self.index];
//答案长度就是答案按钮个数
NSUIntegerlength=question.answer.length;
CGFloatmargin=10; //每个按钮之间的外边距
CGFloatanswerW=35; //按钮宽度
CGFloatanswerH=35; //按钮高度
CGFloatanswerY=0; //按钮靠着答案区View,所以y坐标为0
//左边距,也就是第一个按钮的x坐标
CGFloatmarginLeft=(self.answerView.frame.size.width-(answerW+margin)*length)/2;
//循环创建答案区按钮
for(inti=0;i<length;i++){
//创建按钮
UIButton*answer=[[UIButtonalloc] init];
//计算每个按钮的x坐标
CGFloatanswerX=marginLeft+(answerW+margin)*i;
//设置按钮的frame
answer.frame=CGRectMake(answerX,answerY,answerW,answerH);
//设置答案区按钮背景图
[answer setBackgroundImage:[UIImage imageNamed:@"btn_answer"] forState:UIControlStateNormal];
[answer setBackgroundImage:[UIImage imageNamed:@"btn_answer_highlighted"] forState:UIControlStateHighlighted];
//设置答案区按钮文字颜色为黑色
[answer setTitleColor:[UIColorblackColor] forState:UIControlStateNormal];
//将创建好的答案按钮添加到答案区View
[self.answerView addSubview:answer];
}
}
@end
|
实现效果如下图:
七、动态创建选项区
和创建答案区的方式类似,先拖一个UIView,动态创建选项区按钮并添加到选项区的UIView。
ViewController.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
|
#import "ViewController.h"
#import "JFQuestions.h"
@interfaceViewController()
#pragma mark - 属性
@property(strong,nonatomic)NSArray *questions; //模型数组
@property(assign,nonatomic)int index; //模型数组的索引(下标)
@property(weak,nonatomic)IBOutletUIButton*scoreView; //显示积分
@property(weak,nonatomic)IBOutletUILabel *indexView; //显示索引
@property(weak,nonatomic)IBOutletUILabel *titleView; //显示标题
@property(weak,nonatomic)IBOutletUIButton*iconView; //表示图片
@property(weak,nonatomic)IBOutletUIButton*nextButton; //下一题按钮
@property(assign,nonatomic)CGRect iconFrame; //图片原始frame
@property(weak,nonatomic)UIButton *shade; //阴影背景
@property(weak,nonatomic)IBOutletUIView *answerView; //答案区View
@property(weak,nonatomic)IBOutletUIView *optionsView;//选项区View
#pragma mark - 方法
-(IBAction)tip; //提示按钮
-(IBAction)help; //帮助按钮
-(IBAction)bigImage; //大图按钮
-(IBAction)iconButton;//点击图片
-(IBAction)next; //下一题按钮
-(void)smallImage; //图片变小
-(void)loadAnswerView;//加载答案区
@end
@implementationViewController
-(void)viewDidLoad{
[superviewDidLoad];
//加载界面完界面让模型数组索引为-1
self.index=-1;
//调用next方法即可显示索引为0的数据
[selfnext];
//保存图片元素frame值
self.iconFrame=self.iconView.frame;
}
-(void)didReceiveMemoryWarning{
[superdidReceiveMemoryWarning];
}
#pragma mark - 懒加载数据到数组
-(NSArray*)questions{
if(_questions==nil){
//调用Model类的questions方法快速返回一个模型数组
_questions=[JFQuestionsquestions];
}
return_questions;
}
#pragma mark - 提示
-(IBAction)tip{
}
#pragma mark - 帮助
-(IBAction)help{
}
#pragma mark - 图片变大
-(IBAction)bigImage{
//创建阴影背景,因为可以点击所以使用UIButton
UIButton*shade=[[UIButtonalloc] init];
//设置阴影背景的frame
shade.frame=CGRectMake(0,0,375,667);
//设置阴影背景的背景色
[shade setBackgroundColor:[UIColorblackColor]];
//设置阴影背景的初始透明度(为了在动画效果中渐变)
shade.alpha=0;
//将shade添加父视图
[self.view addSubview:shade];
//计算图片放大后的frame
CGFloaticonW=self.view.frame.size.width;
CGFloaticonH=iconW;
CGFloaticonX=0;
CGFloaticonY=(self.view.frame.size.height-iconH)/2;
//动画渐变设置图片放大、阴影背景出现的效果
[UIView animateWithDuration:1 animations:^{
self.iconView.frame=CGRectMake(iconX,iconY,iconW,iconH);
shade.alpha=0.6;
}];
//将图片控件调整到最外层
[self.view bringSubviewToFront:self.iconView];
//将背景赋值给属性(因为要在其他方法中让阴影消失,需要访问到阴影背景控件)
self.shade=shade;
//为阴影背景注册单击事件,点击后图片缩放,并让去掉阴影背景
[shade addTarget:self action:@selector(smallImage) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark - 图片变小
-(void)smallImage{
//动画渐变变小效果
[UIView animateWithDuration:1 animations:^{
//将图片frame还原,将阴影背景设为透明
self.iconView.frame=self.iconFrame;
self.shade.alpha=0;
} completion:^(BOOLfinished){
//去掉阴影背景
self.shade=nil;
}];
}
#pragma mark - 点击图片
-(IBAction)iconButton{
if(self.shade==nil){
//如果没有阴影背景就说明是缩小状态,点击后放大
[selfbigImage];
}else{
//如果有就说明是放大状态,点击后缩小
[selfsmallImage];
}
}
#pragma mark - 下一题
-(IBAction)next{
//模型数组索引自增
self.index++;
//加载界面数据
[selfloadDataView];
//如果到了最后一题,就禁用下一题按钮
self.nextButton.enabled=self.index!=self.questions.count-1;
}
#pragma mark - 加载数据
-(void)loadDataView{
//取出当前索引的模型对象
JFQuestions*question=self.questions[self.index];
//加载索引数据
self.indexView.text=[NSString stringWithFormat:@"%d/%lu",self.index+1,self.questions.count];
//加载标题数据
self.titleView.text=question.title;
//加载图片数据,注意这里是设置按钮图片不是,按钮背景图
[self.iconView setImage:[UIImage imageNamed:question.icon] forState:UIControlStateNormal];
//加载答案区数据
[selfloadAnswerView];
//加载选项区数据
[selfloadOptionsView];
}
#pragma mark - 加载答案区
-(void)loadAnswerView{
//清除上一次加载的答案区所有按钮
for(UIButton*answerinself.answerView.subviews){
[answer removeFromSuperview];
}
//获取当前索引的模型对象
JFQuestions*question=self.questions[self.index];
//答案长度就是答案按钮个数
NSUIntegerlength=question.answer.length;
CGFloatmargin=10; //每个按钮之间的外边距
CGFloatanswerW=35; //按钮宽度
CGFloatanswerH=35; //按钮高度
CGFloatanswerY=0; //按钮靠着答案区View,所以y坐标为0
//左边距,也就是第一个按钮的x坐标
CGFloatmarginLeft=(self.answerView.frame.size.width-(answerW+margin)*length)/2;
//循环创建答案区按钮
for(inti=0;i<length;i++){
//创建按钮
UIButton*answer=[[UIButtonalloc] init];
//计算每个按钮的x坐标
CGFloatanswerX=marginLeft+(answerW+margin)*i;
//设置按钮的frame
answer.frame=CGRectMake(answerX,answerY,answerW,answerH);
//设置答案区按钮背景图
[answer setBackgroundImage:[UIImage imageNamed:@"btn_answer"] forState:UIControlStateNormal];
[answer setBackgroundImage:[UIImage imageNamed:@"btn_answer_highlighted"] forState:UIControlStateHighlighted];
//设置答案区按钮文字颜色为黑色
[answer setTitleColor:[UIColorblackColor] forState:UIControlStateNormal];
//将创建好的答案按钮添加到答案区View
[self.answerView addSubview:answer];
}
}
#pragma mark - 加载选项区
-(void)loadOptionsView{
//清除上一次加载的选项区所有按钮
for(UIButton*optioninself.optionsView.subviews){
[option removeFromSuperview];
}
//获取当前索引的模型对象
JFQuestions*question=self.questions[self.index];
//选项长度就是选项按钮个数
NSUIntegerlength=question.options.count;
//每行的按钮个数
intcount=7;
CGFloatmargin=10; //每个按钮的外边距
CGFloatoptionW=35; //按钮宽
CGFloatoptionH=35; //按钮高
//左边距,也就是每行第一个按钮的x坐标
CGFloatmarginLeft=(self.optionsView.frame.size.width-(optionW+margin)*count)/2;
//循环创建选项区的按钮
for(inti=0;i<length;i++){
//创建按钮
UIButton*option=[[UIButtonalloc] init];
//计算行索引、列索引
introwIndex=i%count;
intcolIndex=i/count;
//计算x、y坐标
CGFloatoptionX=marginLeft+(optionW+margin)*rowIndex;
CGFloatoptionY=0+(optionH+margin)*colIndex;
//设置按钮的frame
option.frame=CGRectMake(optionX,optionY,optionW,optionH);
//设置答案区按钮背景图
[option setBackgroundImage:[UIImage imageNamed:@"btn_answer"] forState:UIControlStateNormal];
[option setBackgroundImage:[UIImage imageNamed:@"btn_answer_highlighted"] forState:UIControlStateHighlighted];
//设置答案区按钮文字颜色为黑色
[option setTitleColor:[UIColorblackColor] forState:UIControlStateNormal];
//设置按钮文字颜色
[option setTitle:question.options[i] forState:UIControlStateNormal];
//将创建好的答案按钮添加到答案区View
[self.optionsView addSubview:option];
}
}
@end
|
实现效果如下图:
八、答案区和选项区的交互
点击选项区会将按钮上的文字赋值给答案区的按钮,并隐藏被点击的选项区按钮。点击答案区会清楚被点击按钮的文字,并将选项区对应按钮显示出来。如果答案区按钮都全部赋值,就判断答案是否成功,如果成功则加分并判断是否是最后一题,不是则进入下一题,是则提示游戏结束。
ViewController.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
|
#import "ViewController.h"
#import "JFQuestions.h"
@interfaceViewController()<UIAlertViewDelegate>//监听对话框并遵守协议
#pragma mark - 属性
@property(strong,nonatomic)NSArray *questions; //模型数组
@property(assign,nonatomic)int index; //模型数组的索引(下标)
@property(weak,nonatomic)IBOutletUIButton*scoreView; //显示积分
@property(weak,nonatomic)IBOutletUILabel *indexView; //显示索引
@property(weak,nonatomic)IBOutletUILabel *titleView; //显示标题
@property(weak,nonatomic)IBOutletUIButton*iconView; //表示图片
@property(weak,nonatomic)IBOutletUIButton*nextButton; //下一题按钮
@property(assign,nonatomic)CGRect iconFrame; //图片原始frame
@property(weak,nonatomic)UIButton *shade; //阴影背景
@property(weak,nonatomic)IBOutletUIView *answerView; //答案区View
@property(weak,nonatomic)IBOutletUIView *optionsView;//选项区View
#pragma mark - 方法
-(IBAction)tip; //提示按钮
-(IBAction)help; //帮助按钮
-(IBAction)bigImage; //大图按钮
-(IBAction)iconButton; //点击图片
-(IBAction)next; //下一题按钮
-(void)smallImage; //图片变小
-(void)loadAnswerView; //加载答案区
-(void)loadOptionsView; //加载选项区
-(void)answerClick:(UIButton*)sender;//答案区按钮单击事件
-(void)optionClick:(UIButton*)sender;//选项区按钮单击事件
-(void)changeColor:(UIColor*)color; //改变按钮颜色
-(void)changeScore:(int)score; //改变积分
-(void)gameOver; //游戏结束对话框
-(void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;//实现协议方法
@end
@implementationViewController
-(void)viewDidLoad{
[superviewDidLoad];
//加载界面完界面让模型数组索引为-1
self.index=-1;
//调用next方法即可显示索引为0的数据
[selfnext];
//保存图片元素frame值
self.iconFrame=self.iconView.frame;
}
-(void)didReceiveMemoryWarning{
[superdidReceiveMemoryWarning];
}
#pragma mark - 懒加载数据到数组
-(NSArray*)questions{
if(_questions==nil){
//调用Model类的questions方法快速返回一个模型数组
_questions=[JFQuestionsquestions];
}
return_questions;
}
#pragma mark - 提示
-(IBAction)tip{
}
#pragma mark - 帮助
-(IBAction)help{
}
#pragma mark - 图片变大
-(IBAction)bigImage{
//创建阴影背景,因为可以点击所以使用UIButton
UIButton*shade=[[UIButtonalloc] init];
//设置阴影背景的frame
shade.frame=CGRectMake(0,0,375,667);
//设置阴影背景的背景色
[shade setBackgroundColor:[UIColorblackColor]];
//设置阴影背景的初始透明度(为了在动画效果中渐变)
shade.alpha=0;
//将shade添加父视图
[self.view addSubview:shade];
//计算图片放大后的frame
CGFloaticonW=self.view.frame.size.width;
CGFloaticonH=iconW;
CGFloaticonX=0;
CGFloaticonY=(self.view.frame.size.height-iconH)/2;
//动画渐变设置图片放大、阴影背景出现的效果
[UIView animateWithDuration:1 animations:^{
self.iconView.frame=CGRectMake(iconX,iconY,iconW,iconH);
shade.alpha=0.6;
}];
//将图片控件调整到最外层
[self.view bringSubviewToFront:self.iconView];
//将背景赋值给属性(因为要在其他方法中让阴影消失,需要访问到阴影背景控件)
self.shade=shade;
//为阴影背景注册单击事件,点击后图片缩放,并让去掉阴影背景
[shade addTarget:self action:@selector(smallImage) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark - 图片变小
-(void)smallImage{
//动画渐变变小效果
[UIView animateWithDuration:1 animations:^{
//将图片frame还原,将阴影背景设为透明
self.iconView.frame=self.iconFrame;
self.shade.alpha=0;
} completion:^(BOOLfinished){
//去掉阴影背景
self.shade=nil;
}];
}
#pragma mark - 点击图片
-(IBAction)iconButton{
if(self.shade==nil){
//如果没有阴影背景就说明是缩小状态,点击后放大
[selfbigImage];
}else{
//如果有就说明是放大状态,点击后缩小
[selfsmallImage];
}
}
#pragma mark - 下一题
-(IBAction)next{
//开启选项区按钮交互
self.optionsView.userInteractionEnabled=YES;
//模型数组索引自增
self.index++;
//加载界面数据
[selfloadDataView];
//如果到了最后一题,就禁用下一题按钮
self.nextButton.enabled=self.index!=self.questions.count-1;
}
#pragma mark - 加载数据
-(void)loadDataView{
//取出当前索引的模型对象
JFQuestions*question=self.questions[self.index];
//加载索引数据
self.indexView.text=[NSString stringWithFormat:@"%d/%lu",self.index+1,self.questions.count];
//加载标题数据
self.titleView.text=question.title;
//加载图片数据,注意这里是设置按钮图片不是,按钮背景图
[self.iconView setImage:[UIImage imageNamed:question.icon] forState:UIControlStateNormal];
//加载答案区数据
[selfloadAnswerView];
//加载选项区数据
[selfloadOptionsView];
}
#pragma mark - 加载答案区
-(void)loadAnswerView{
//清除上一次加载的答案区所有按钮
for(UIButton*answerinself.answerView.subviews){
[answer removeFromSuperview];
}
//获取当前索引的模型对象
JFQuestions*question=self.questions[self.index];
//答案长度就是答案按钮个数
NSUIntegerlength=question.answer.length;
CGFloatmargin=10; //每个按钮之间的外边距
CGFloatanswerW=35; //按钮宽度
CGFloatanswerH=35; //按钮高度
CGFloatanswerY=0; //按钮靠着答案区View,所以y坐标为0
//左边距,也就是第一个按钮的x坐标
CGFloatmarginLeft=(self.answerView.frame.size.width-(answerW+margin)*length)/2;
//循环创建答案区按钮
for(inti=0;i<length;i++){
//创建按钮
UIButton*answer=[[UIButtonalloc] init];
//计算每个按钮的x坐标
CGFloatanswerX=marginLeft+(answerW+margin)*i;
//设置按钮的frame
answer.frame=CGRectMake(answerX,answerY,answerW,answerH);
//设置答案区按钮背景图
[answer setBackgroundImage:[UIImage imageNamed:@"btn_answer"] forState:UIControlStateNormal];
[answer setBackgroundImage:[UIImage imageNamed:@"btn_answer_highlighted"] forState:UIControlStateHighlighted];
//设置答案区按钮文字颜色为黑色
[answer setTitleColor:[UIColorblackColor] forState:UIControlStateNormal];
//将创建好的答案按钮添加到答案区View
[self.answerView addSubview:answer];
//为答案区的按钮注册单击事件
[answer addTarget:self action:@selector(answerClick:) forControlEvents:UIControlEventTouchUpInside];
}
}
#pragma mark - 加载选项区
-(void)loadOptionsView{
//清除上一次加载的选项区所有按钮
for(UIButton*optioninself.optionsView.subviews){
[option removeFromSuperview];
}
//获取当前索引的模型对象
JFQuestions*question=self.questions[self.index];
//选项长度就是选项按钮个数
NSUIntegerlength=question.options.count;
//每行的按钮个数
intcount=7;
CGFloatmargin=10; //每个按钮的外边距
CGFloatoptionW=35; //按钮宽
CGFloatoptionH=35; //按钮高
//左边距,也就是每行第一个按钮的x坐标
CGFloatmarginLeft=(self.optionsView.frame.size.width-(optionW+margin)*count)/2;
//循环创建选项区的按钮
for(inti=0;i<length;i++){
//创建按钮
UIButton*option=[[UIButtonalloc] init];
//计算行索引、列索引
introwIndex=i%count;
intcolIndex=i/count;
//计算x、y坐标
CGFloatoptionX=marginLeft+(optionW+margin)*rowIndex;
CGFloatoptionY=0+(optionH+margin)*colIndex;
//设置按钮的frame
option.frame=CGRectMake(optionX,optionY,optionW,optionH);
//设置答案区按钮背景图
[option setBackgroundImage:[UIImage imageNamed:@"btn_answer"] forState:UIControlStateNormal];
[option setBackgroundImage:[UIImage imageNamed:@"btn_answer_highlighted"] forState:UIControlStateHighlighted];
//设置答案区按钮文字颜色为黑色
[option setTitleColor:[UIColorblackColor] forState:UIControlStateNormal];
//设置按钮文字颜色
[option setTitle:question.options[i] forState:UIControlStateNormal];
//给每个选项区按钮设置一个唯一tag值
option.tag=i;
//将创建好的答案按钮添加到答案区View
[self.optionsView addSubview:option];
//为选项区的按钮注册单击事件
[option addTarget:self action:@selector(optionClick:) forControlEvents:UIControlEventTouchUpInside];
}
}
#pragma mark - 答案区的按钮单击事件
-(void)answerClick:(UIButton*)sender{
//点击答案区,就开放选项区的交互功能
self.optionsView.userInteractionEnabled=YES;
//将被点击的按钮文字清空
[sender setTitle:nil forState:UIControlStateNormal];
//遍历找到与被点击按钮tag值相同的按钮让其取消隐藏
for(UIButton*optioninself.optionsView.subviews){
if(option.tag==sender.tag){
option.hidden=NO;
}
}
}
#pragma mark - 选项区的按钮单击事件
-(void)optionClick:(UIButton*)sender{
BOOLisPull=YES;
//遍历答案区按钮,遇到空的按钮就依次赋值
for(UIButton*answerinself.answerView.subviews){
if(answer.currentTitle==nil){
//将被点击按钮的文字赋值给答案按钮
[answer setTitle:sender.currentTitle forState:UIControlStateNormal];
//将被点击按钮的tag值也赋值给答案按钮
answer.tag=sender.tag;
//隐藏被点击按钮
sender.hidden=YES;
//每次点击只赋值一个按钮
break;
}
}
//定义一个可变字符串用于拼接用户点击
NSMutableString*userInput=[NSMutableStringstring];
//取出当前索引的模型对象
JFQuestions*questions=self.questions[self.index];
for(UIButton*answerinself.answerView.subviews){
if(answer.currentTitle==nil){
//只要进了循环就说明还没满
isPull=NO;
}else{
//不为空就拼接
[userInput appendString:answer.currentTitle];
}
}
if(isPull==YES){
//满了就关闭选项区交互功能
self.optionsView.userInteractionEnabled=NO;
//判断答案正误
if([userInput isEqualToString:questions.answer]){
//正确、改变按钮文字颜色为蓝色
[self changeColor:[UIColorblueColor]];
//增加100分
[self changeScore:100];
if(self.index!=self.questions.count-1){
//延迟一秒调到下一题
[self performSelector:@selector(next) withObject:nil afterDelay:1];
}else{
[selfgameOver];
}
}else{
//错误就改变按钮文字颜色为红色
[self changeColor:[UIColorredColor]];
//较少100分
[self changeScore:-100];
}
}
}
#pragma mark - 改变文字颜色
-(void)changeColor:(UIColor*)color{
for(UIButton*answerinself.answerView.subviews){
[answer setTitleColor:color forState:UIControlStateNormal];
}
}
#pragma mark - 改变积分
-(void)changeScore:(int)score{
//取出当前分数
intcurrentScore=self.scoreView.currentTitle.intValue;
//如果当前分数小于0就提示游戏结束,否则继续扣分
if(currentScore<=0){
[selfgameOver];
}else{
//修改分数
currentScore+=score;
}
//为控件赋值新分数
[self.scoreView setTitle:[NSString stringWithFormat:@"%d",currentScore] forState:UIControlStateNormal];
}
#pragma mark - 游戏结束对话框
-(void)gameOver{
//创建对话框
UIAlertView*alertView=[[UIAlertViewalloc] initWithTitle:@"温馨提醒" message:@"GAME OVER!是否继续游戏?" delegate:self cancelButtonTitle:@"继续游戏" otherButtonTitles:nil];
//显示对话框
[alertView show];
}
#pragma mark - 实现协议方法
-(void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if(buttonIndex==0){
//初始化游戏数据,重新开始
[selfviewDidLoad];
}
}
@end
|
九、实现提示按钮功能
每次点击提示按钮会扣分,再清空答案区按钮并恢复选项区按钮,然后截取正确答案第一个文字添加到答案区第一个按钮上。
方法如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#pragma mark - 提示
-(IBAction)tip{
//每次点击提示扣分100
[self changeScore:-100];
//取出当前的模型对象
JFQuestions*question=self.questions[self.index];
//截取正确答案的第一个文字
NSString*answer=[question.answer substringToIndex:1];
for(UIButton*answerinself.answerView.subviews){
//模拟点击答案区所有按钮,清空答案区
[self answerClick:answer];
}
for(UIButton*optioninself.optionsView.subviews){
if([answer isEqualToString:option.currentTitle]){
//模拟点击匹配到的选项区按钮,添加到答案区
[self optionClick:option];
}
}
}
|
至此整个小练习就完成了,再随意新增一些简单功能,就不多做演示。