A.需要掌握的
1.添加图片资源(暂时认为@2x跟非@2x代表同一张图片)
2.搭建UI界面
* 文本标签
* 4个按钮
* 中间的图片
3.设置状态栏样式
4.监听下一题按钮的点击
5.延迟加载数据
* 加载plist
* 字典转模型
* KVC的引入
6.切换下一题的序号、图片、标题,下一题按钮的可用性
7.默认显示第1条题目
8.显示大图
* 监听中间图片点击
* 添加遮盖
* 移动图片(注意头像图片的层级顺序)
* 监听“大图按钮”
9.展示答案的个数
10.展示待选答案
11.答案处理
12.提示功能
13.Icon和Launch、@2x
B.实现思路
1.构建基本UI:
(1)背景
(2)按钮
(3)图片
(4)选项
2.数据存储与加载
(1)标题数据
(2)图片数据
(3)可选项数据
(4)答案数据
(5)得分数据
3.大图功能 (点击图片或者按钮)
(1)放大并调整图片位置
(2)虚化背景,使用半透明全覆盖的button
(3)点击背景或者图片,删除半透明背景、恢复图片
a.动画效果, 使用带block参数方法
b.播放完动画再移除阴影元素
4.下一题功能
其调用的功能是整个APP的核心,包含了初始化控件、加载文件数据、删除旧控件、加入新控件、刷新界面
5.提示功能
给出一定的答案
6.帮助功能
(在线分享功能,没有实现)
C.知识点
1.设置状态栏 (iOS7开始)
(1)设置样式
在Controller中重写preferredStateBarStyle方法,返回要设置的值
1 // 设置状态栏是否隐藏
2 - (BOOL)prefersStatusBarHidden {
3 return NO;
4 }
5
6 // 设置状态栏
7 - (UIStatusBarStyle)preferredStatusBarStyle {
8 // 设置状态栏字体为白色
9 return UIStatusBarStyleLightContent;
10 }
状态栏是显示黑色字体:
修改之后:
2.APP图标
只要文件名为 “Icon.png”,就会被设置为APP图标
3.启动画面
一个app在启动过程中会全屏显示叫做Default.png的图片
不用规格Default的使用场合
- Default.png:非retina-iPhone屏幕,320x480
- [email protected]:retina-iPhone屏幕,640x960
- [email protected]:4inch的retina-iPhone屏幕,640x1136
- Default-Portrait~ipad.png:非retain-iPad竖屏屏幕,768x1024
- [email protected]:retain-iPad竖屏屏幕,1536x2048
- Default-Landscape~ipad.png:非retain-iPad横屏屏幕,1024x768
- [email protected]:retain-iPad横屏屏幕,2048x1536
4.背景图和前景元素之间的层次关系
一般按照storyboard中的顺序排列
使用代码把某个元素提到最前
// 2.更换阴影和图片的位置
[self.view bringSubviewToFront:self.icon];
5.使用Button做出中间的图片
为了做出有白边效果的图片,使用了白色图片作为背景,然后设置图片的边距Inset,露出白色背景图
6.延迟加载数据
改写controller中的题目数组questions 的get方法,在调用get方法的时候检测questions是否已经存在,否则才开始加载
7.“大图”功能
虚化背景:使用一张半透明的图片遮挡
图片放大:使用形变转换
动画效果:使用带block参数的动画方法,设置动画事件、完成动画后消除遮盖回收内存
8.恢复图片
点击遮盖 / 点击放大后的图片:消除遮盖,恢复图片
9.消除button被点击的时候变灰
取消勾选 “Highlighted Adjusts Image”
10.显示答案
使用button, 动态生成相应数量的button
使用UIView作为父控件包含多个答案button
在storyboard中创建一个UIView,放到合适位置,调整尺寸
点击 "下一题” 按钮:
删除旧答案,自己控制自己从父视图删除
1 // 5.1 删除全部旧答案的button
2 [self.answerView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
添加新答案
11.加入选项
原理同答案模块,多了在行上面的位置计算
删除旧的所有选项,添加新的选项
1 // 6.1 删除旧选项
2 [self.optionsView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
12.点击选项
(1)被点击的选项按钮消失(hidden = YES)
(2)将选中选项的文字放到答案区的按钮上
(3)把文字放到第一个没有文字的答案按钮上
(4)点击有文字的答案按钮,文字恢复到选项
(5)答案填满后,阻止事件触发
13.点击有文字的答案按钮
(1)被点击的答案按钮文字消失
(2)让答案文字对应的选项重新出现
遍历所有选项,找到对应的选项进行设置
14.判断答案正确
(1)遍历答案区,判断每个答案按钮是否有文字,如果都有文字,证明已经完成,检查答案
(2)答错答案文字变红,退选答案恢复文字颜色为黑色
(3)答对了给出提示,延迟1秒后跳入下一题
17.分数计算
答对加分
18.提示功能
取消现有的所有答案,给出正确答案的第一个字,扣一定分数
19.更改图标
图标有命名规范,最好使用 “icon.png"
把所有图标文件拖放到 “Images.xcassets” 的”AppIcon” 里面
20.修改启动画面
注意:
在现时最新版Xcode6.1中,默认使用 LaunchScreen.xib 作为启动画面
现在有两种方式设置启动图片
- 使用 xib (LaunchScreen)
- 使用 LaunchImage
启动页面配置:
项目 ==> general ==> App Icons and Launch Images
(1)使用 xib
a.默认状态就是使用 xib
b.如果现状态是使用LaunchImage, 设置 “Launch Images Source” 为 “Don’t use asset catalogs”
c.设置 xib 的文件名
d.在 xib 中设计
e.效果
(2)使用 LaunchImage
a.更改配置
项目 ==> general ==> App Icons and Launch Images ==> Launch Images Source 中选择 images,再把Launch Screen File 选项设置为空
b.会发现Images.xcassets 下自动生成了一个 LaunchImage 的Imageset
c.拖入画面
d.效果
主要代码:
ViewController.m:
1 //
2 // ViewController.m
3 // CrazyGuessGame
4 //
5 // Created by hellovoidworld on 14/11/26.
6 // Copyright (c) 2014年 hellovoidworld. All rights reserved.
7 //
8
9 #import "ViewController.h"
10 #import "Question.h"
11
12 @interface ViewController ()
13
14 @property (weak, nonatomic) IBOutlet UILabel *noLabel; // 序号
15 @property (weak, nonatomic) IBOutlet UILabel *titleLabel; // 标题
16 @property (weak, nonatomic) IBOutlet UIButton *icon;
17 @property (weak, nonatomic) IBOutlet UIButton *nextImageButton;
18 @property (weak, nonatomic) IBOutlet UIView *answerView;
19 @property (weak, nonatomic) IBOutlet UIView *optionsView;
20 @property (weak, nonatomic) IBOutlet UIButton *scoreButton;
21
22 - (IBAction)onTipsButtonClicked;
23 - (IBAction)onHelpButtonClicked;
24 - (IBAction)onEnlargeImgButtonClicked;
25 - (IBAction)onNextImgButtonClicked;
26 - (IBAction)onImageClicked;
27
28 /** 所有题目 */
29 @property(nonatomic, strong) NSArray *questions;
30
31 /** 当前题目序号 */
32 @property(nonatomic, assign) int index;
33
34 /** 遮盖阴影 */
35 @property(nonatomic, weak) UIButton *cover;
36
37 /** 原始图片位置、尺寸 */
38 @property(nonatomic, assign) CGRect imageOriginalFrame;
39
40 /** 答案是否已经填满标识 */
41 @property(nonatomic, assign) BOOL isAnswerCompleted;
42
43 /** 得分 */
44 @property(nonatomic, assign) int score;
45
46 @end
47
48 @implementation ViewController
49
50 - (void)viewDidLoad {
51 [super viewDidLoad];
52 // Do any additional setup after loading the view, typically from a nib.
53
54 //存储原始图片的位置、尺寸信息
55 self.imageOriginalFrame = self.icon.frame;
56
57 self.index = -1;
58 [self onNextImgButtonClicked]; // 初次加载,index是0
59 }
60
61 - (void)didReceiveMemoryWarning {
62 [super didReceiveMemoryWarning];
63 // Dispose of any resources that can be recreated.
64 }
65
66 // 改写getter, 延迟加载,此处不要使用self.questions来取得questions,否则陷入死循环
67 - (NSArray *)questions {
68 if (nil == _questions) {
69 // 1.加载plist数据
70 NSArray *questionDictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"questions" ofType:@"plist"]];
71
72 NSMutableArray *questionsArray = [NSMutableArray array];
73 for (NSDictionary *questionDict in questionDictArray) {
74 // 2.将数据封装到Model "Question"
75 Question *question = [Question initWithDictionary:questionDict];
76 [questionsArray addObject:question];
77 }
78
79 // 3.赋值
80 _questions = questionsArray;
81 }
82
83 return _questions;
84 }
85
86 // 设置状态栏是否隐藏
87 - (BOOL)prefersStatusBarHidden {
88 return NO;
89 }
90
91
92 // 设置状态栏
93 - (UIStatusBarStyle)preferredStatusBarStyle {
94 // 设置状态栏字体为白色
95 return UIStatusBarStyleLightContent;
96 }
97
98
99 // 点击“提示”按钮
100 - (IBAction)onTipsButtonClicked {
101 // 1.清除现有的答案
102 for (UIButton *currentAnswerButton in self.answerView.subviews) {
103 [self onAnswerClicked:currentAnswerButton];
104 }
105
106 // 2.取出正确答案片段
107 Question *question = self.questions[self.index];
108 NSString *partOfCorrectAnswer = [question.answer substringToIndex:1];
109
110 // 3.填充到答案区
111 for (UIButton *optionButton in self.optionsView.subviews) {
112 if ([partOfCorrectAnswer isEqualToString:optionButton.currentTitle]) {
113 [self onOptionClicked:optionButton];
114 }
115 }
116
117 // 5.扣取相应分数
118 [self calScore:-500];
119 }
120
121 // 点击“帮助”按钮
122 - (IBAction)onHelpButtonClicked {
123 }
124
125 // 点击“大图”按钮
126 - (IBAction)onEnlargeImgButtonClicked {
127 // 1.1添加阴影
128 UIButton *cover = [[UIButton alloc] init];
129 cover.frame = self.view.bounds;
130 cover.backgroundColor = [UIColor blackColor];
131 cover.alpha = 0;
132
133 // 1.2给阴影添加变回小图的触发事件
134 [cover addTarget:self action:@selector(smallImg) forControlEvents:UIControlEventTouchUpInside];
135
136 self.cover = cover;
137 [self.view addSubview:cover];
138
139 // 2.更换阴影和图片的位置
140 [self.view bringSubviewToFront:self.icon];
141
142 // 3.更改图像大小,显示阴影
143 [UIView animateWithDuration:1 animations:^{
144 cover.alpha = 0.7;
145
146 CGFloat iconWidth = self.view.frame.size.width;
147 CGFloat iconHeight = iconWidth;
148 CGFloat iconX = 0;
149 CGFloat iconY = (self.view.frame.size.height / 2) - (iconHeight / 2);
150 self.icon.frame = CGRectMake(iconX, iconY, iconWidth, iconHeight);
151 }];
152 }
153
154 /** 缩小图片 */
155 - (void) smallImg {
156 [UIView animateWithDuration:1 animations:^{
157 // 1.删除阴影
158 self.cover.alpha = 0;
159
160 // 2.恢复图片
161 self.icon.frame = self.imageOriginalFrame;
162
163 } completion:^(BOOL finished) {
164 // 动画执行完成后
165
166 [self.cover removeFromSuperview];
167 self.cover = nil;
168 }];
169
170 }
171
172 // 点击“下一题”按钮
173 - (IBAction)onNextImgButtonClicked {
174 self.index++;
175
176 // 1.取出相应地Model数据
177 Question *question = self.questions[self.index];
178
179 // 2.设置控件
180 [self setControls:question];
181
182 // 3.设置答案
183 [self addAnswer:question];
184
185 // 4.设置选项
186 [self addOptions:question];
187
188
189 }
190
191 /** 设置控件 */
192 - (void) setControls:(Question *) question {
193 // 1.答案是否已经填满
194 self.isAnswerCompleted = NO;
195
196 // 2.得分
197 [self.scoreButton setTitle:[NSString stringWithFormat:@"%d", self.score] forState:UIControlStateNormal];
198
199 // 2.设置序号
200 self.noLabel.text = [NSString stringWithFormat:@"%d/%d", self.index + 1, self.questions.count];
201
202 // 3.设置标题
203 self.titleLabel.text = question.title;
204
205 // 4.设置图片
206 [self.icon setImage:[UIImage imageNamed:question.icon] forState:UIControlStateNormal];
207
208 // 5.设置“下一题”按钮
209 self.nextImageButton.enabled = (self.index + 1) != self.questions.count;
210 }
211
212 /** 加入答案区 */
213 - (void) addAnswer:(Question *) question {
214 // 5.1 删除全部旧答案的button
215 [self.answerView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
216
217 // 5.1 加入新答案
218 int answerCount = question.answer.length; // 答案字数
219
220 // 初始化尺寸信息
221 CGFloat answerWidth = 35;
222 CGFloat answerHeight = self.answerView.frame.size.height;
223 CGFloat answerMargin = 10;
224 CGFloat answerMarginLeft = (self.answerView.frame.size.width - answerWidth * answerCount - answerMargin * (answerCount - 1) ) / 2;
225
226 for (int i=0; i<question.answer.length; i++) {
227 // 计算位置
228 CGFloat answerX = answerMarginLeft + i * (answerWidth + answerMargin);
229 CGFloat answerY = 0;
230
231 UIButton *answerButton = [[UIButton alloc] initWithFrame:CGRectMake(answerX, answerY, answerWidth, answerHeight)];
232
233 // 设置背景
234 [answerButton setBackgroundImage:[UIImage imageNamed:@"btn_answer"] forState: UIControlStateNormal];
235 [answerButton setBackgroundImage:[UIImage imageNamed:@"btn_answer_highlighted"] forState: UIControlStateHighlighted];
236
237 // 设置按钮点击事件,让按钮文字消失,相应的选项恢复
238 [answerButton addTarget:self action:@selector(onAnswerClicked:) forControlEvents:UIControlEventTouchUpInside];
239
240 [self.answerView addSubview:answerButton];
241 }
242
243 [self.view addSubview:self.answerView];
244 }
245
246 /**
247 设置按钮点击事件,让按钮文字消失,相应的选项恢复
248 */
249 - (void) onAnswerClicked:(UIButton *) answerButton {
250 // 1.设置答案标识
251 self.isAnswerCompleted = NO;
252
253 // 2.恢复相应的选项
254 NSString *answerTitle = [answerButton titleForState:UIControlStateNormal];
255 for (UIButton *optionButton in self.optionsView.subviews) {
256 if ([answerTitle isEqualToString:[optionButton titleForState:UIControlStateNormal]]
257 && optionButton.isHidden) {
258 optionButton.hidden = NO;
259 break;
260 }
261 }
262
263 // 3.清除按钮上的文字
264 [answerButton setTitle:nil forState:UIControlStateNormal];
265
266 // 4.恢复答案文字颜色
267 for (UIButton *answerButton in self.answerView.subviews) {
268 [answerButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
269 }
270 }
271
272 /** 加入选项区 */
273 - (void) addOptions:(Question *) question {
274 // 6.1 删除旧选项
275 [self.optionsView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
276
277 // 6.2 加入新选项
278 int optionsCount = question.options.count;
279 CGFloat optionWidth = 35;
280 CGFloat optionHeight = 35;
281 int columnCount = 7;
282 CGFloat optionMargin = 10;
283 CGFloat optionMarginLeft = (self.optionsView.frame.size.width - optionWidth * columnCount - optionMargin * (columnCount - 1)) / 2;
284
285 for (int i=0; i<optionsCount; i++) {
286 int rowNo = i / columnCount; // 当前行号,从0开始
287 int columnNo = i % columnCount; // 当前列号,从0开始
288 CGFloat optionX = optionMarginLeft + columnNo * (optionWidth + optionMargin);
289 CGFloat optionY = rowNo * (optionHeight + optionMargin);
290
291 UIButton *optionButton = [[UIButton alloc] initWithFrame:CGRectMake(optionX, optionY, optionWidth, optionHeight)];
292
293 [optionButton setBackgroundImage:[UIImage imageNamed:@"btn_option"] forState:UIControlStateNormal];
294 [optionButton setBackgroundImage:[UIImage imageNamed:@"btn_option_highlighted"] forState:UIControlStateHighlighted];
295
296 [optionButton setTitle:question.options[i] forState:UIControlStateNormal];
297
298 [optionButton addTarget:self action:@selector(onOptionClicked:) forControlEvents:UIControlEventTouchUpInside];
299
300 [self.optionsView addSubview:optionButton];
301 }
302
303 [self.view addSubview:self.optionsView];
304 }
305
306 // 使点击到的选项消失
307 - (void) onOptionClicked:(UIButton *) optionButton {
308 // 1.如果答案尚未填满,使选中的选项消失
309 if (self.isAnswerCompleted) {
310 return;
311 }
312
313 optionButton.hidden = YES;
314
315 // 2.使消失的文字出现在答案区上,判断是否已经完成
316 NSString *optionTitle = [optionButton titleForState:UIControlStateNormal];
317
318 // 遍历答案按钮
319 for (int i=0; i<self.answerView.subviews.count; i++) {
320 // 已经遍历到了最后一个答案按钮,证明已经完成了所有答案
321 if (i == self.answerView.subviews.count - 1) {
322 self.isAnswerCompleted = YES;
323 }
324
325 UIButton *answerButton = self.answerView.subviews[i];
326 NSString *answerTitle = [answerButton titleForState:UIControlStateNormal];
327
328 // 如果该答案按钮上没有文字,则是空,填入文字
329 if (answerTitle.length == 0) {
330 // 按钮默认的字体颜色是白色, 手动设为黑色
331 [answerButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
332 [answerButton setTitle:optionTitle forState:UIControlStateNormal];
333
334 break;
335 }
336 }
337
338 [self dealWithAnswer];
339 }
340
341 /** 处理答案 */
342 - (void) dealWithAnswer {
343 if (self.isAnswerCompleted) {
344 Question *question = self.questions[self.index];
345 NSMutableString *answerStr = [NSMutableString string];
346
347 // 拼接已经完成的答案
348 for (UIButton *completedAnswerButton in self.answerView.subviews) {
349 [answerStr appendString:[completedAnswerButton titleForState:UIControlStateNormal]];
350 }
351
352 // 如果答对了
353 if ([answerStr isEqualToString:question.answer]) {
354 // 答案字体转换称为蓝色
355 for (UIButton *correctAnswerButton in self.answerView.subviews) {
356 [correctAnswerButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
357 }
358
359 // 延迟指定时间后跳入下一题
360 [self performSelector:@selector(onNextImgButtonClicked) withObject:nil afterDelay:0.5];
361
362 // 加分
363 [self calScore:1000];
364 }
365 else {
366 for (UIButton *correctAnswerButton in self.answerView.subviews) {
367 // 答案字体转换称为红色
368 [correctAnswerButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
369 }
370 }
371 }
372 }
373
374 /** 原图状态点击放大,大图状态点击恢复原状 */
375 - (IBAction)onImageClicked {
376 // 使用遮盖是否存在判断图片状态
377
378 // 1.遮盖不存在,放大图片
379 if (nil == self.cover) {
380 [self onEnlargeImgButtonClicked];
381 }
382 else {
383 // 2.遮盖存在,恢复图片
384 [self smallImg];
385 }
386 }
387
388 /** 计算分数 */
389 - (void) calScore:(int) score {
390 self.score += score;
391 [self.scoreButton setTitle:[NSString stringWithFormat:@"%d", self.score] forState:UIControlStateNormal];
392 }
393
394 @end
Question.h:
1 //
2 // Question.h
3 // CrazyGuessGame
4 //
5 // Created by hellovoidworld on 14/11/26.
6 // Copyright (c) 2014年 hellovoidworld. All rights reserved.
7 //
8
9 #import <Foundation/Foundation.h>
10
11 @interface Question : NSObject
12
13 @property(nonatomic, copy) NSString *answer;
14 @property(nonatomic, copy) NSString *title;
15 @property(nonatomic, copy) NSString *icon;
16 @property(nonatomic, strong) NSArray *options;
17
18 - (instancetype) initWithDictionary:(NSDictionary *) dictionary;
19 + (instancetype) initWithDictionary:(NSDictionary *) dictionary;
20
21 @end
Question.m
1 //
2 // Question.m
3 // CrazyGuessGame
4 //
5 // Created by hellovoidworld on 14/11/26.
6 // Copyright (c) 2014年 hellovoidworld. All rights reserved.
7 //
8
9 #import "Question.h"
10
11 @implementation Question
12
13 - (instancetype) initWithDictionary:(NSDictionary *) dictionary {
14 if (self = [super init]) {
15 self.title = dictionary[@"title"];
16 self.icon = dictionary[@"icon"];
17 self.answer = dictionary[@"answer"];
18 self.options = dictionary[@"options"];
19 }
20
21 return self;
22 }
23
24 + (instancetype) initWithDictionary:(NSDictionary *) dictionary {
25 return [[self alloc] initWithDictionary:dictionary];
26 }
27
28 @end