ios开发-UI基础-超级猜图

  [注意]转载时请注明出处博客园-吃唐僧肉的小悟空http://www.cnblogs.com/hukezhu/

 

  本篇文章介绍一个比较综合的小应用----超级猜图.

  功能分析:

    • 根据显示的图片,在下面的待选项按钮中选中正确答案按钮,选中的按钮会显示在正确答案按钮中
    • 答案错误,答案颜色变为红色,分数减小
    • 答案正确,答案颜色变为蓝色,两秒自动跳入下一题,分数增加
    • 点击"下一题"可以进入下一个题目
    • 点击"大图",可以放大显示图片,再次点击图片或者背景,图片缩小至原来大小
    • 点击"提示",自动情况答案选项,并显示正确答案的第一个字,并且分数减小  

  应用截图:

下面附上完成这个小应用的思路:

  

  1 1. 通过storyboard设计"超级猜图"的上半部分界面。
  2     1> 使用UIImageView做背景(最后面的背景图片,首先添加这个)
  3     2> 分析界面上的控件,显示图片的也使用button,显示内容的使用label,
  4         下面显示答案按钮和最下面显示待选项按钮的位置使用view
  5     中间的图片可以点击:用button,button有一个inset:分别设置内部内容距离上左下右有多少不能显示
  6 
  7 
  8 2. 实现点击"下一题"功能.(最开始状态是没有数据的,首先点击下一题,我们家在数据之后,才能做其他的功能,所以最开始先完成"下一题"的功能)
  9     * 声明一个index属性来记录当前显示的题目的索引。
 10 
 11     * 当我们点击"下一题"的时候, 从数组中获取对应的题目, 并显示到对应的界面控件上。
 12 
 13 
 14     * 解决:最后一题之后再次点击"下一题"索引越界问题。(此处进行判断)
 15 
 16 /*    0.判断索引值是否越界,并且弹框
 17     if (self.index == self.questions.count - 1) {
 18     //        UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"恭喜" message:@"过关了" delegate:nil
 19     //                                             cancelButtonTitle:@"取消" otherButtonTitles:@"A", nil];
 20     //        [alert show];
 21     UIActionSheet *acionsheet  = [[UIActionSheet alloc]initWithTitle:@"恭喜" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:@"确定" otherButtonTitles:@"A",@"B", nil];
 22     [acionsheet showInView:self.view];
 23     
 24     return;
 25 }
 26 
 27 */
 28 
 29 
 30 3. 在viewDidLoad中初始化第一个问题(显示第一个问题到界面上)。
 31     * 设置self.index = -1
 32     * 手动调用"下一题"按钮的点击事件
 33         [self next];
 34 
 35 
 36 
 37 4. 实现"查看大图"功能(点击右侧"大图"按钮, 显示大图)
 38 
 39     * 实现思路(步骤):
 40         1> 添加一个"阴影"按钮, 因为该"阴影"要实现点击, 所以用"按钮"
 41         2> 然后再把"头像按钮"显示在"阴影"上面。
 42         3> 通过动画的方式改变"头像按钮"的frame(位置和尺寸)变成大图效果。
 43     ** 注意: 取消"自动布局(Auto Layout)"
 44 
 45     ** 点击"遮罩阴影", 回到小图
 46         1> 通过动画慢慢将"遮罩阴影"的透明度变为0
 47         2> 通过动画慢慢将"头像图片"的frame修改为原来的位置
 48         3> 在动画执行完毕后, 移除"遮罩阴影"
 49 
 50     ** 点击图片本身显示大图, 再次点击图片本身显示小图
 51 
 52 
 53 
 54 
 55 
 56 5. 动态生成"答案按钮" 57     * 思路:
 58         0> 在点击"下一题"按钮中实现该功能
 59         1> 创建一个UIView来存放所有的"答案按钮"
 60         2> 根据每个问题的答案的文字个数来创建按钮
 61         3> 每次创建按钮之前, 先把旧的按钮都删除
 62             此处有两种方案:
 63                 1-循环便利数组,执行removefromsuperview
 64                 2-使用makeObjectsPerformSelector:@selector(removeFromSuperview)
 65         4> 指定每个"答案按钮"的尺寸和中间的margin, 然后计算第一个按钮的x值(marginLeft)。
 66         5> 在循环中, 计算每个按钮的x值(y值都是0)。
 67 
 68 
 69 
 70 6. 动态生成"待选项按钮" 71     * 思路:
 72     0> 在点击"下一题"按钮中实现该功能
 73     1> 创建一个UIView来存放所有的"待选项按钮"
 74     2> 根据待选项按钮的个数来创建
 75     3> 清除之前的待选项按钮
 76     4> 利用九宫格的算法创建待选项按钮
 77 
 78 7. 实现"待选按钮"的单击事件
 79     * 隐藏当前被点击的"待选按钮"
 80     * 将当前被点击的"待选按钮"的文字显示到"答案按钮"的左起第一个为空的按钮上
 81     * 如果"答案按钮"已经全部填满了, 不允许再点击"待选按钮"
 82 
 83     ** 注意: 只要父控件不能处理事件, 那么子控件也无法处理事件(另外一种解决方案)。
 84     1> 如果"答案按钮"文字填满了, 则设置option view禁止与用户交互
 85         self.optionsView.userInteractionEnabled = NO;
 86 
 87     2> 当用户再次点击"答案按钮"时 或 点击"下一题"后在创建"待选按钮"的时候再次启用option view与用户的交互功能
 88         self.optionsView.userInteractionEnabled = YES;
 89 
 90 
 91 
 92 
 93 8. 实现"答案按钮"的单击事件
 94     * 设置被点击的"答案按钮"文字为空(nil)
 95     * 设置与当前被点击的"答案按钮"相对应的"待选按钮"显示出来
 96     (
 97      ** 注意:当答案按钮中有两个相同的文字的option按钮时的问题
 98      ** 解决方案一:
 99         1> 为每个option按钮设置一个唯一的tag
100         2> 在点击某个option按钮的时候, 把option按钮的text和tag都设置到answer按钮上
101         3> 在点击answer按钮的时候, 判断answer按钮的文字与tag同时都与某个option按钮匹配时, 再显示这个option按钮
102      ** 解决方案二:
103         执行判断的时候,并且判断字体的hidden属性为YES
104      )
105     * 设置所有"答案按钮"的文字颜色为黑色
106 
107 
108 9. 在"待选按钮"的单击事件中, 判断当前的答案的正确性
109     * 每次点击"待选按钮"都需要做判断, 如果答案按钮"满了", 再去判断正确性
110     * 如果正确:
111     1> 那么设置"答案按钮"的文字颜色为蓝色
112     2> 加分
113     3> 2秒钟后自动跳转到下一题
114 
115     * 如果错误:
116     1> 答案按钮的文字设置为红色
117 
118 
119 10. 点击"提示"按钮
120     * 加分
121     * 清空所有"答案按钮"的文字(相当于点击了每一个"答案按钮", 不是简单的设置"答案按钮"的文字为nil)
122     * 并将正确答案的第一个文字设置到第一个"答案按钮"上(相当于正确答案的option按钮被点击了), 通过调用字符串的substringToIndex:来截取第一个字符的字符串

[注意]转载时请注明出处博客园-吃唐僧肉的小悟空http://www.cnblogs.com/hukezhu/

 [注意]转载时请注明出处博客园-吃唐僧肉的小悟空http://www.cnblogs.com/hukezhu/

附上应用代码结构图:

  

storyboard拖线图:

 附上源代码:

KZAppModel.h

 1 //
 2 //  KZAppModel.h
 3 //  UI基础-04-05-16
 4 //
 5 //  Created by hukezhu on 15/5/16.
 6 //
 7 //
 8 
 9 #import <Foundation/Foundation.h>
10 
11 @interface KZAppModel : NSObject
12 @property (nonatomic,copy) NSString *answer;
13 @property (nonatomic,copy) NSString *icon;
14 @property (nonatomic,copy) NSString *title;
15 @property (nonatomic,strong)NSArray *options;
16 
17 - (instancetype)initWithDict:(NSDictionary *)dict;
18 
19 + (instancetype)appWithDict:(NSDictionary *)dict;
20 @end

KZAppModel.m

 1 //
 2 //  KZAppModel.m
 3 //  UI基础-04-05-16
 4 //
 5 //  Created by hukezhu on 15/5/16.
 6 //
 7 //
 8 
 9 #import "KZAppModel.h"
10 
11 @implementation KZAppModel
12 -(instancetype)initWithDict:(NSDictionary *)dict{
13 
14     if (self = [super init]) {
15         self.answer = dict[@"answer"];
16         self.icon = dict[@"icon"];
17         self.title = dict[@"title"];
18         self.options = dict[@"options"];
19     }
20     return self;
21 }
22 
23 + (instancetype)appWithDict:(NSDictionary *)dict{
24 
25     return  [[self alloc]initWithDict:dict];
26 }
27 @end

ViewController.m

  1 //
  2 //  ViewController.m
  3 //  01-超级猜图
  4 //
  5 //  Created by hukezhu on 15/5/16.
  6 //  [注意]转载时请注明出处博客园-吃唐僧肉的小悟空http://www.cnblogs.com/hukezhu/
  7 //
  8 
  9 #import "ViewController.h"
 10 #import "KZAppModel.h"
 11 
 12 @interface ViewController ()
 13 @property (nonatomic,assign)int index;
 14 @property (weak, nonatomic) IBOutlet UIButton *big;
 15 @property (weak, nonatomic) IBOutlet UIButton *head;
 16 @property (weak, nonatomic) IBOutlet UILabel *noLabel;
 17 @property (weak, nonatomic) IBOutlet UILabel *titleLabel;
 18 - (IBAction)nextClick;
 19 @property (weak, nonatomic) IBOutlet UIView *answerView;
 20 @property (weak, nonatomic) IBOutlet UIView *optionView;
 21 @property (weak, nonatomic) IBOutlet UIButton *coinBtn;
 22 - (IBAction)tipClick;
 23 
 24 - (IBAction)bigImg;
 25 - (IBAction)headClick;
 26 
 27 @property (nonatomic,strong) UIButton *cover;
 28 @property (nonatomic,strong)NSArray *questions;
 29 @end
 30 
 31 @implementation ViewController
 32 
 33 - (void)viewDidLoad {
 34     [super viewDidLoad];
 35     self.index = -1;
 36     [self nextClick];
 37 
 38 }
 39 
 40 /**
 41  *  懒记载
 42  */
 43 - (IBAction)nextClick {
 44     //判断当前索引,如果到达数据数组长度减1
 45     if (self.index == self.questions.count - 1  ) {
 46         UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"已经到达最后一张图片" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"好的", nil];
 47         [alert show];
 48         return;
 49     }
 50     
 51     self.index++;
 52     
 53     //创建一个模型,存储数据
 54     KZAppModel *appModel = self.questions[self.index];
 55 
 56     //添加数据
 57     [self addData:appModel];
 58     
 59     //添加答案按钮
 60     [self addAnswerBtn:appModel];
 61     
 62     //添加待选项按钮
 63     [self addOptionBtn:appModel];
 64 }
 65 
 66 
 67 
 68 /**
 69  *  添加待选项按钮
 70  *
 71  *  @param appModel 数据模型
 72  */
 73 - (void)addOptionBtn:(KZAppModel *)appModel{
 74 
 75     //清除之前的待选项按钮
 76     [self.optionView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
 77     //定义每行显示的待选项按钮的个数
 78     int totalCol = 7;
 79     CGFloat optionW = 40;
 80     CGFloat optionH = 40;
 81     CGFloat optionMargin = 10;
 82     //左边距
 83     CGFloat leftOptionMargin = (self.optionView.frame.size.width - totalCol * optionW - (totalCol - 1 )*optionMargin)*0.5;
 84     //遍历数组,创建待选项按钮(九宫格的创建方法)
 85     for (int i = 0; i < appModel.options.count; i++) {
 86         
 87         UIButton *optionBtn = [[UIButton alloc]init];
 88         //行号
 89         int row = i  / totalCol;
 90         //列号
 91         int col = i % totalCol;
 92         CGFloat optionX = leftOptionMargin + col * (optionMargin + optionW);
 93         CGFloat optionY = optionMargin + row * (optionMargin + optionH);
 94         optionBtn.frame = CGRectMake(optionX, optionY, optionW, optionH);
 95         //设置两种状态下的背景图片
 96         [optionBtn setBackgroundImage:[UIImage imageNamed:@"btn_option"] forState:UIControlStateNormal];
 97         [optionBtn setBackgroundImage:[UIImage imageNamed:@"btn_option_highlighted"] forState:UIControlStateHighlighted];
 98         //给这些待选项按钮添加文字
 99         [optionBtn setTitle:appModel.options[i] forState:UIControlStateNormal];
100         //设置字体颜色
101         [optionBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
102         //将按钮添加到optionView中
103         [self.optionView addSubview:optionBtn];
104         
105         //创建待选项按钮的点击方法
106         [optionBtn addTarget:self action:@selector(optionClick:) forControlEvents:UIControlEventTouchUpInside];
107         
108     }
109 
110 }
111 
112 
113 /**
114  *  添加答案按钮
115  *
116  *  @param appModel 模型数据
117  */
118 - (void)addAnswerBtn:(KZAppModel *)appModel{
119 
120     //清除之前的答案按钮
121     [self.answerView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
122     
123     
124     
125     CGFloat answerW = 30;
126     CGFloat answerH = 30;
127     CGFloat answerY = 0;
128     CGFloat margin = 10;
129     CGFloat leftMargin = (self.answerView.frame.size.width - appModel.answer.length *(margin + answerW))*0.5;
130     //循环遍历数组,创建答案按钮
131     for(int i = 0;  i < appModel.answer.length; i++){
132         
133         UIButton *answerBtn = [[UIButton alloc]init];
134         
135         CGFloat answerX =leftMargin + i *(margin + answerW);
136         answerBtn.frame = CGRectMake(answerX, answerY, answerW, answerH);
137         //设置背景图片
138         [answerBtn setBackgroundImage:[UIImage imageNamed:@"btn_answer"] forState:UIControlStateNormal];
139 
140         //设置显示文字的颜色
141         [answerBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
142         [self.answerView addSubview:answerBtn];
143         
144         //创建点击按钮的点击响应方法
145         [answerBtn addTarget:self action:@selector(answerClick:) forControlEvents:UIControlEventTouchUpInside];
146     }
147 
148 }
149 
150 
151 
152 
153 /**
154  *  添加数据
155  *
156  *  @param appModel 数据模型
157  */
158 -(void)addData:(KZAppModel *)appModel{
159 
160 
161     
162     self.noLabel.text = [NSString stringWithFormat:@"%d/%ld",self.index + 1,self.questions.count];
163     
164     self.titleLabel.text = appModel.title;
165     
166     [self.head setImage:[UIImage imageNamed:appModel.icon] forState:UIControlStateNormal];
167 }
168 
169 
170 - (void)optionClick:(UIButton *)optionBtn{
171 
172     //首先将被点击的待选项按钮的文字取到(后面会讲这个文字隐藏)
173     NSString *title = [optionBtn currentTitle];
174     optionBtn.hidden = YES;
175 
176     //循环遍历答案按钮,遇到第一个不为空的按钮,显示被点击的待选项按钮的文字
177     for (UIButton *answerBtn in self.answerView.subviews) {
178         if ([answerBtn currentTitle] == nil ) {
179             [answerBtn setTitle:title forState:UIControlStateNormal];
180             break;
181         }
182     }
183     
184     //判断答案是否为满,如果满了,就去判断对不对
185     //定义一个可变的字符串,用来拼接循环得到的答案,得到这个答案,去和正确答案比较
186     NSMutableString *tempString = [NSMutableString string];
187     BOOL full = YES;
188     
189     //遍历数组,如果遇到按钮为空的话,就赋值full标记为no,表明没有满,否则的话就去拼接
190     for (UIButton *answerBtn in self.answerView.subviews) {
191          if ([answerBtn currentTitle] == nil ) {
192              full = NO;
193          }else{
194          
195              [tempString appendString:[answerBtn currentTitle]];
196          }
197         
198     }
199     if (full) {
200         
201         //NSLog(@"满了");
202         KZAppModel *appModel = self.questions[self.index];
203         for (UIButton *optionBtn in self.optionView.subviews) {
204             optionBtn.enabled = NO;
205         }
206 
207         
208         if ([tempString isEqualToString:appModel.answer]) {
209             NSLog(@"答对了");
210             for (UIButton *answerBtn in self.answerView.subviews){
211                 [answerBtn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
212 
213             }
214 //            int score = [self.coinBtn currentTitle].intValue ;
215 //            score += 50;
216 //            [self.coinBtn setTitle:[NSString stringWithFormat:@"%d",score] forState:UIControlStateNormal];
217             [self score:50];
218             
219             [self performSelector:@selector(nextClick) withObject:nil afterDelay:2.0];
220             
221             
222             
223         }else{
224         
225             NSLog(@"没答对");
226             for (UIButton *answerBtn in self.answerView.subviews){
227                 [answerBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
228                 [self score:-200];
229             }
230         }
231     }
232 }
233 
234 /**
235  *  答案选线点击的方法
236  *
237  */
238 - (void)answerClick:(UIButton *)answerBtn{
239 
240     //循环遍历待选项按钮,找到与答案相同的文字,删除答案文字,显示待选项文字
241     for (UIButton *optionBtn in self.optionView.subviews) {
242         if ([[optionBtn currentTitle] isEqualToString:[answerBtn currentTitle]] && optionBtn.hidden == YES) {
243             [answerBtn setTitle:nil forState:UIControlStateNormal];
244             optionBtn.hidden = NO;
245             for (UIButton *optionBtn in self.optionView.subviews) {
246                 optionBtn.enabled = YES;
247             }
248         }
249     }
250     
251     for (UIButton *answerBtn in self.answerView.subviews) {
252         [answerBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
253     }
254 }
255 
256 
257 /**
258  *  点击提示的响应方法
259  */
260 - (IBAction)tipClick {
261     //清除所有的答案按钮
262     for (UIButton *answerBtn in self.answerView.subviews) {
263         [self answerClick:answerBtn];
264     }
265     //取出正确答案,显示出来
266     KZAppModel *appModel = self.questions[self.index];
267     NSString *str = [appModel.answer substringToIndex:1];
268     
269     
270 //    /**
271 //     *  最开始想的是这种方法,但是发现更好的办法,,就相当于点击了对应的待选项按钮
272 //     */
273 //    [self.answerView.subviews[0] setTitle:str forState:UIControlStateNormal];
274 //    [self.answerView.subviews[0] setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
275     
276     for (UIButton *optionBtn in self.optionView.subviews) {
277         if ([[optionBtn currentTitle] isEqualToString:str]) {
278             [self optionClick:optionBtn];
279         }
280     }
281     
282     [self score:-100];
283     
284     
285 }
286 
287 - (IBAction)bigImg {
288     //点击大图之后,先创建一个阴影(button),
289     UIButton *cover = [[UIButton alloc]init];
290     self.cover = cover;
291     cover.frame = self.view.bounds;
292     [cover setBackgroundColor:[UIColor blackColor]];
293     cover.alpha = 0.0;
294     [self.view addSubview:cover];
295     //调整图片和阴影位置
296     [self.view bringSubviewToFront:self.head];
297 
298     [cover addTarget:self action:@selector(coverClick) forControlEvents:UIControlEventTouchUpInside];
299     [UIView animateWithDuration:2.0 animations:^{
300         cover.alpha = 0.6;
301         //放大图片
302         
303         CGFloat headW = self.view.frame.size.width;
304         CGFloat headH = headW;
305         CGFloat headX = 0;
306         CGFloat headY = 0.5 * (self.view.frame.size.height- headH);
307         self.head.frame = CGRectMake(headX, headY, headW, headH);
308         
309     }];
310     
311 }
312 
313 
314 /**
315  *  计算分数方法
316  *
317  *  @param score 传入需要增加或者减少的分数(减为负)
318  */
319 - (void)score:(int)score{
320 
321     int tempScore = [self.coinBtn currentTitle].intValue ;
322     tempScore += score;
323     [self.coinBtn setTitle:[NSString stringWithFormat:@"%d",tempScore] forState:UIControlStateNormal];
324     
325 }
326 
327 /**
328  *  图片放大状态下,点击阴影缩小图片
329  */
330 -(void)coverClick{
331 
332     if (self.cover != nil) {
333         [self smallImg];
334 
335     }
336 }
337 /**
338  *  点击头像图片,放大或缩小
339  */
340 - (IBAction)headClick {
341     if (self.cover == nil) {
342         [self bigImg];
343     }else{
344     
345         [self smallImg];
346     }
347 }
348 /**
349  *  缩小图片的方法
350  */
351 - (void)smallImg{
352 
353 
354     [UIView animateWithDuration:2.0 animations:^{
355         self.cover.alpha = 0.0;
356         self.head.frame = CGRectMake(97, 120, 180, 180);
357         self.cover = nil;
358     }];
359     
360 }
361 
362 
363 
364 /**
365  *  懒加载数据
366  *
367  *  @return 返回数据
368  */
369 -(NSArray *)questions{
370 
371     if(_questions == nil){
372         NSString *path = [[NSBundle mainBundle]pathForResource:@"questions" ofType:@"plist"];
373         NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
374         NSMutableArray *tempArray = [NSMutableArray array];
375         for (NSDictionary *dict in dictArray) {
376             KZAppModel *appModel = [KZAppModel appWithDict:dict];
377             [tempArray addObject:appModel];
378         }
379         _questions = tempArray;
380     
381     }
382     return _questions;
383 }
384 
385 @end

KVC大体介绍:

 1 KVC(Key Value Coding)
 2     介绍: 通过给定一个对象的属性名称(以字符串方式), 然后找到对象的相应属性进行赋值。
 3 
 4     * setValue:forKeyPath:
 5     ** 示例: [self setValue:dict[@"icon"] forKeyPath:@"icon"]
 6     ** 含义: 表示根据forKeyPath:@"icon"提供的@"icon"去self对象中查找名字叫icon的属性, 找到以后把dict[@"icon"]中获取到的值赋值给self的icon属性。
 7 
 8 
 9     * setValuesForKyesWithDictionary:
10     ** 含义: 更简便的调用方式。内部相当于调用了多次setValue:forKeyPath:
11 
12     ** 注意:
13     1> 必须保证字典中的key与模型的属性名称一致。
14     2> 必须保证模型的属性个数与字典一致或者模型的属性个数要大于等于字典的个数。
15 
16 * 演示KVC:
17 1> 新建一个model类。
18 2> 演示对字符串类型、数字类型进行KVC赋值
19 3> 通过KVC取值。
20 id v = [对象 valueForKeyPath:@"key"];
21 int v1 = [[对象 valueForKeyPath:@"key"] intValue];
22 
23 4> 把模型转成字典, 把对象中指定的属性转换为字典。
24 NSDictioanry *dict = [对象 dictionaryWithValuesForKeys:@[@"name", @"age"]];
25 
26 5> 把一个person数组中的每个person对象的name都获取出来然后放到一个新的数组中。
27 (
28     NSArray *names = [person数组 valueForKeyPath:@"name"];
29 )
30 
31 6> keyPath介绍, 人拥有一本书, 通过kvc获取人所拥有的书的名称
32 (
33     NSString *bkName = [person valueForKeyPath:@"book.name"];
34     
35     等价于
36     
37     NSString *bkName = person.book.name;
38 )
39 
40 * KVO (Key Value Observing), 监听对象的属性值变化。

[注意]转载时请注明出处博客园-吃唐僧肉的小悟空http://www.cnblogs.com/hukezhu/

原文地址:https://www.cnblogs.com/hukezhu/p/4510478.html