[iOS开发项目-7] 超级猜图

来源:互联网 发布:怎么复制淘宝宝贝标题 编辑:程序博客网 时间:2024/04/27 17:58

本项目是取自传智播客的教学项目,加入笔者的修改和润饰。

1. 项目名称:超级猜图

2. 项目截图展示

这里写图片描述

3. 项目功能

  1. 点击图片或“大图”按钮,图片放大;再点击图片或点击周围区域,图片复原。
  2. 点击备选按钮,相应字填入答案区按钮。
  3. 按“下一题”按钮或答案正确:进入下一题。
  4. 点击答案区某按钮,相应字回到备选区原来位置。
  5. 点击“帮助按钮”会清空答案区按钮,并提示正确答案的第一个字。
  6. 答案正确或错误都有相应的扣分和加分。

4. 项目代码

  • 模型代码:Question.h
#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>@interface Question : NSObjec@property (nonatomic, copy) NSString *answer;@property (nonatomic, copy) NSString *icon;@property (nonatomic, copy) NSString *title;@property (nonatomic, strong) NSArray *options;- (instancetype)initWithDict:(NSDictionary *)dict;+ (instancetype)questionWithDict:(NSDictionary *)dict;@property (nonatomic, strong, readonly) UIImage *image;/** 返回所有题目数组 */+ (NSArray *)questions;/** 打乱备选文字的数组 */- (void)randamOptions;@end
  • 模型代码:Question.m
#import "Question.h"@implementation Question@synthesize image = _image;- (UIImage *)image{    if (_image == nil) {        _image = [UIImage imageNamed:self.icon];    }    return _image;}- (instancetype)initWithDict:(NSDictionary *)dict{    self = [super init];    if (self) {        [self setValuesForKeysWithDictionary:dict];        // 对备选按钮进行乱序,只在加载的时候,做一次乱序        [self randamOptions];    }    return self;}+ (instancetype)questionWithDict:(NSDictionary *)dict{    return [[self alloc] initWithDict:dict];}+ (NSArray *)questions{    NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"questions.plist" ofType:nil]];    NSMutableArray *arrayM = [NSMutableArray array];    for (NSDictionary *dict in array) {        [arrayM addObject:[self questionWithDict:dict]];    }    return arrayM;}- (void)randamOptions{    // 对options数组乱序    self.options = [self.options sortedArrayUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) {        int seed = arc4random_uniform(2);        if (seed) {            return [str1 compare:str2];        } else {            return [str2 compare:str1];        }    }];    NSLog(@"%@", self.options);}@end
  • ViewController.m
#import "ViewController.h"#import "Question.h"#define kButtonWidth    35#define kButtonHeight   35#define kButtonMargin   10#define kTotolCol       7@interface ViewController ()@property (weak, nonatomic) IBOutlet UIButton *iconButton;//中间图像@property (weak, nonatomic) IBOutlet UIButton *scoreButton;//分数@property (weak, nonatomic) IBOutlet UILabel *noLabel;@property (weak, nonatomic) IBOutlet UILabel *titleLabel;@property (weak, nonatomic) IBOutlet UIButton *nextQuestionButton;@property (nonatomic, strong) NSArray *questions;//模型数组@property (weak, nonatomic) IBOutlet UIView *answerView;//摆放答案按钮的答案区@property (weak, nonatomic) IBOutlet UIView *optionsView;//摆放备选答案的备选答案区@property (nonatomic, assign) int index;//题目索引@property (nonatomic, strong) UIButton *cover;//蒙板@end@implementation ViewController//getter方法懒加载问题数组- (NSArray *)questions{    if (_questions == nil) {        _questions = [Question questions];    }    return _questions;}//getter方法懒加载蒙板- (UIButton *)cover{    if (_cover == nil) {        //1. 设置蒙板大小:与窗口大小一致        _cover = [[UIButton alloc] initWithFrame:self.view.bounds];        //2. 设置蒙板颜色:灰色        _cover.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];        //3. 添加模版到view        [self.view addSubview:_cover];        //4. 使蒙板无法响应事件(alpha<0.01无法接受到消息)        _cover.alpha = 0.0;        //设定蒙板的监听事件:点击蒙板触发bigImage方法        [_cover addTarget:self action:@selector(bigImage) forControlEvents:UIControlEventTouchUpInside];    }    return _cover;}- (void)viewDidLoad{    [super viewDidLoad];    //初始界面    self.index = -1;    [self nextQuestion];}-(UIStatusBarStyle)preferredStatusBarStyle{    return UIStatusBarStyleLightContent;}#pragma mark - 大图小图切换/** *  大图小图显示切换 */- (IBAction)bigImage{    // 如果没有放大,就放大,否则就缩小    // 1. 通过蒙板的alpha来判断按钮是否已经被放大:当蒙板无法响应时间的时候即图片被点击放大    if (self.cover.alpha == 0.0) { // 放大        // 2. 将图像按钮弄到最前面        // bringSubviewToFront将子视图前置        [self.view bringSubviewToFront:self.iconButton];        // 3. 动画放大图像按钮        CGFloat w = self.view.bounds.size.width;        CGFloat h = w;        CGFloat y = (self.view.bounds.size.height - h) * 0.5;        [UIView animateWithDuration:1.0f animations:^{            //图片放大后的大小            self.iconButton.frame = CGRectMake(0, y, w, h);            //蒙板的alpha            self.cover.alpha = 1.0;        }];    } else { // 缩小        [UIView animateWithDuration:1.0 animations:^{            // 将图像恢复初始位置和大小            self.iconButton.frame = CGRectMake(90, 90, 150, 150);            // 蒙板不可用            self.cover.alpha = 0.0;        }];    }}#pragma mark - 下一题/** *  下一题目(主要的方法,尽量保留简洁的代码,主要体现思路和流程即可) */- (IBAction)nextQuestion{    // 1. 当前答题的索引,索引递增    self.index++;    // 2. 从数组中按照索引取出题目模型数据,取出某一数组元素    Question *question = self.questions[self.index];    // 3. 设置基本信息    [self setupBasicInfo:question];    // 4. 设置答案按钮    [self creatAnswerButtons:question];    // 5. 设置备选按钮    [self creatOptionsButtons:question];}//设置基本信息- (void)setupBasicInfo:(Question *)question{    //1. 序号    self.noLabel.text = [NSString stringWithFormat:@"%d/%d", self.index + 1, self.questions.count];    //2. 问题名称    self.titleLabel.text = question.title;    //3. 图像    [self.iconButton setImage:[UIImage imageNamed:question.icon] forState:UIControlStateNormal];    // 如果到达最后一题,禁用下一题按钮    self.nextQuestionButton.enabled = (self.index < self.questions.count - 1);}//设置答案按钮- (void)creatAnswerButtons:(Question *)question{    // 首先清除掉答题区内的所有按钮    // 所有的控件都继承自UIView,多态的应用    for (UIView *btn in self.answerView.subviews) {        [btn removeFromSuperview];    }    //按钮的总宽度    CGFloat answerW = self.answerView.bounds.size.width;    //按钮的个数等于答案的字数    int length = question.answer.length;    //最左边的按钮的x坐标    CGFloat answerX = (answerW - kButtonWidth * length - kButtonMargin * (length - 1)) * 0.5;    // 创建所有答案的按钮    for (int i = 0; i < length; i++) {        //第i个按钮的x坐标        CGFloat x = answerX + i * (kButtonMargin + kButtonWidth);        UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(x, 0, kButtonWidth, kButtonHeight)];        //默认情况下白色;点击之后黑色        [btn setBackgroundImage:[UIImage imageNamed:@"btn_answer"] forState:UIControlStateNormal];        [btn setBackgroundImage:[UIImage imageNamed:@"btn_answer_highlighted"] forState:UIControlStateHighlighted];        //按钮中文字黑色        [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];        [self.answerView addSubview:btn];        // 添加按钮监听方法:answerClick:        [btn addTarget:self action:@selector(answerClick:) forControlEvents:UIControlEventTouchUpInside];    }}// 设置备选按钮- (void)creatOptionsButtons:(Question *)question{    // 问题:每次调用下一题方法时,都会重新创建21个按钮    // 解决:如果按钮已经存在,并且是21个,只需要更改按钮标题即可    if (self.optionsView.subviews.count != question.options.count) {        // 清楚所有按钮        for (UIView *view in self.optionsView.subviews) {            [view removeFromSuperview];        }        //备选按钮区宽度        CGFloat optionW = self.optionsView.bounds.size.width;        CGFloat optionX = (optionW - kTotolCol * kButtonWidth - (kTotolCol - 1) * kButtonMargin) * 0.5;        for (int i = 0; i < question.options.count; i++) {            int row = i / kTotolCol;            int col = i % kTotolCol;            CGFloat x = optionX + col * (kButtonMargin + kButtonWidth);            CGFloat y = row * (kButtonMargin + kButtonHeight);            UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(x, y, kButtonWidth, kButtonHeight)];            [btn setBackgroundImage:[UIImage imageNamed:@"btn_option"] forState:UIControlStateNormal];            [btn setBackgroundImage:[UIImage imageNamed:@"btn_option_highlighted"] forState:UIControlStateHighlighted];            [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];            [self.optionsView addSubview:btn];            // 添加按钮监听方法:optionClick:            [btn addTarget:self action:@selector(optionClick:) forControlEvents:UIControlEventTouchUpInside];        }        NSLog(@"创建候选按钮");    }    // 如果按钮已经存在,在点击下一题的时候,只需要设置标题即可    int i = 0;    for (UIButton *btn in self.optionsView.subviews) {        // 设置备选答案        [btn setTitle:question.options[i++] forState:UIControlStateNormal];        // 回复所有按钮的隐藏状态        btn.hidden = NO;    }}#pragma mark - 候选按钮点击方法/** 点击候选按钮:文字移动到答案按钮 */- (void)optionClick:(UIButton *)button{    // 1. 在答案区找到第一个文字为空的按钮    UIButton *btn = [self firstAnswerButton];    // 如果没有找到按钮,直接返回    if (btn == nil) return;    // 2. 将button的标题设置给答案区的按钮    [btn setTitle:button.currentTitle forState:UIControlStateNormal];    // 3. 将button隐藏    button.hidden = YES;    // 4. 判断结果    [self judge];}/** 判断结果 */- (void)judge{    // 如果四个按钮都有文字,才需要判断结果    // 遍历所有答题区的按钮    BOOL isFull = YES;    NSMutableString *strM = [NSMutableString string];    for (UIButton *btn in self.answerView.subviews) {        if (btn.currentTitle.length == 0) {            // 只要有一个按钮没有字            isFull = NO;            break;        } else {            // 有字,拼接临时字符串            [strM appendString:btn.currentTitle];        }    }    if (isFull) {        // 判断是否和答案一致        // 根据self.index获得当前的question        Question *question = self.questions[self.index];        // 如果一致,进入下一题        if ([strM isEqualToString:question.answer]) {            [self setAnswerButtonsColor:[UIColor blueColor]];            // 增加分数            [self changeScore:800];            // 等待0.5秒,进入下一题            [self performSelector:@selector(nextQuestion) withObject:nil afterDelay:0.5];        } else {            // 如果不一致,修改按钮文字颜色,提示用户            [self setAnswerButtonsColor:[UIColor redColor]];        }    }}/** 修改答题区按钮的颜色 */- (void)setAnswerButtonsColor:(UIColor *)color{    for (UIButton *btn in self.answerView.subviews) {        [btn setTitleColor:color forState:UIControlStateNormal];    }}// 在答案区找到第一个文字为空的按钮- (UIButton *)firstAnswerButton{    // 取按钮的标题    // 遍历答题区所有按钮    for (UIButton *btn in self.answerView.subviews) {        if (btn.currentTitle.length == 0) {//当前按钮上的文字长度为0            return btn;        }    }    return nil;}#pragma mark - 答题区按钮点击方法- (void)answerClick:(UIButton *)button{    // 1. 如果按钮没有字,直接返回    if (button.currentTitle.length == 0) return;    // 2. 如果有字,清除文字,候选区按钮显示    // 1> 使用button的title去查找候选区中对应的按钮    UIButton *btn = [self optionButtonWithTilte:button.currentTitle isHidden:YES];    // 2> 在备选答案区显示对应按钮    btn.hidden = NO;    // 3> 在答案区清除button的文字    [button setTitle:@"" forState:UIControlStateNormal];    // 4> 只要点击了按钮上的文字,意味着答题区的内容不完整    [self setAnswerButtonsColor:[UIColor blackColor]];}- (UIButton *)optionButtonWithTilte:(NSString *)title isHidden:(BOOL)isHidden{    // 遍历候选区中的所有按钮    for (UIButton *btn in self.optionsView.subviews) {        if ([btn.currentTitle isEqualToString:title] && btn.isHidden == isHidden) {            return btn;        }    }    return nil;}#pragma mark - 提示功能- (IBAction)tipClick{    // 1. 把答题区中所有的按钮清空    for (UIButton *btn in self.answerView.subviews) {        // 用代码执行点击答题按钮的操作        [self answerClick:btn];    }    // 2. 把正确答案的第一个字,设置到答题区中    // 1> 知道答案的第一个字    Question *question = self.questions[self.index];    NSString *first = [question.answer substringToIndex:1];    UIButton *btn = [self optionButtonWithTilte:first isHidden:NO];    [self optionClick:btn];    // 扣分    [self changeScore:-1000];}#pragma mark - 分数处理- (void)changeScore:(int)score{    // 取出当前的分数    int currentScore = self.scoreButton.currentTitle.intValue;    // 使用score调整分数    currentScore += score;    // 重新设置分数    [self.scoreButton setTitle:[NSString stringWithFormat:@"%d", currentScore] forState:UIControlStateNormal];}@end

5. 本项目必须掌握的代码段

  • 使控件看不见而且无法响应事件
_cover.alpha = 0.0;
  • 将子视图前置
[self.view bringSubviewToFront:self.iconButton];
  • 加入动画
 [UIView animateWithDuration:1.0f animations:^{            //图片放大后的大小            self.iconButton.frame = CGRectMake(0, y, w, h);            //蒙板的alpha            self.cover.alpha = 1.0;}];
  • 清除答案区view所有按钮
    for (UIView *btn in self.answerView.subviews) {        [btn removeFromSuperview];    }
  • 子视图的个数
self.optionsView.subviews.count
  • 找到第一个空的按钮
- (UIButton *)firstAnswerButton{    for (UIButton *btn in self.answerView.subviews) {        if (btn.currentTitle.length == 0) {            return btn;        }    }    return nil;}
  • 延迟n秒执行某方法
[self performSelector:@selector(nextQuestion) withObject:nil afterDelay:0.5];

6. 笔记

  • UIButton的一些属性
@property(nonatomic,readonly,retain) NSString *currentTitle; @property(nonatomic,readonly,retain) UIColor  *currentTitleColor;        @property(nonatomic,readonly,retain) UIImage  *currentImage;             @property(nonatomic,readonly,retain) UIImage  *currentBackgroundImage;
0 0
原创粉丝点击