软件设计思路和需求分析:
1.主要用到UIButton和UILabel,由于在ios7中都是全屏显示,所以需要把状态栏手动显示出来
通过重写preferredStatusBarStyle方法,返回UIStatusBarStyleLightContent;即可显示出状态栏
2.游戏上半部分可以用storyboard设计,下半部分有两个View控件,并用代码动态添加答案按钮和备选按钮
3.素材信息存在plist中,所以程序运行前期可以懒加载plist,并实现字典转模型,这里用到KVC应用
key value coding 键值编码
使用setValuesForKeys时,要求
类中属性必须有字典中的所有键值,不可以少,可以多
类中成员都是字典中的属性,并提供2个类方法
在Controller中通过提供的类方法返回这个对象
4.图片放大:点击图片,点击“大图”
这里可以设置一个蒙板,当图片放大时,使蒙板的alpha从0.0变化到0.5,并设置图片移动到视图顶层,实现穿越功能,全程动画显示
计算好图片放大的目标位置后,动画显示
5.图片缩小:点击图片,点击蒙板
这里可以通过判断蒙板的alpha属性是否有值来决定是否缩小,获取到图片原先frame,动画缩小
6.下一题按钮的操作:
如果已经到最后一题,则返回通关提示
如果没有,则返回索引对象的题目模型,设置一系列属性值,并设置答案按钮的UIView布局和备选按钮的UIView布局
并为答案按钮设置点击事件,为备选按钮设置点击事件
7.提示按钮:
不管答案按钮中输入了多少个字,使用按钮的setTitle nil。。。方法清除已输入的值
获取当前题目的正确答案,并返回答案字符串的第一个字
使用一次提示,金币-1000分
8.备选按钮点击事件
点击一个按钮,隐藏它,并让它的值移动到空的答案按钮中,
如果输入完成后答案错误,则字体变红,
如果答案正确,则显示蓝色,并且金币+500分,等待0.5秒返回下一题事件
9.答案按钮点击事件
点击答案按钮的任意一个,则取消颜色,并返回使点击的按钮Title设为nil,按钮返回到原来的位置
10.收尾工作
app图标,通过images.xcassets中的AppIcon来设置
app初始界面通过LaunchImages来设置,
图片设置的时候还要区分3GS/4/5的像素问题
部分功能还可以使用其他特效或者方法,帮助按钮可以实现其他功能,预留。
一下为全部代码:
======questions.plist========
==============================Controller结构=================================
LFViewController.m:
#import "LFViewController.h" #import "LFQuestion.h" #define kButtonW 35.0 #define kButtonH 35.0 #define kButtonMargin 10.0 #define kTotalCol 7 @interface LFViewController () @property (weak, nonatomic) IBOutlet UILabel *noLabel; @property (weak, nonatomic) IBOutlet UILabel *titleLabel; @property (weak, nonatomic) IBOutlet UIButton *scoreButton; /** 图片 */ @property (weak, nonatomic) IBOutlet UIButton *iconView; /** 遮罩按钮 */ @property (nonatomic, strong) UIButton *cover; @property (weak, nonatomic) IBOutlet UIButton *nextButton; @property (weak, nonatomic) IBOutlet UIView *answerView; @property (weak, nonatomic) IBOutlet UIView *optionView; /** 题目列表 */ @property (nonatomic, strong) NSArray *questions; /** 题目索引 */ @property (nonatomic, assign) int index; @end @implementation LFViewController - (NSArray *)questions { if (!_questions) { _questions = [LFQuestion questions]; } return _questions; } - (UIButton *)cover { if (!_cover) { _cover = [[UIButton alloc] initWithFrame:self.view.bounds]; _cover.backgroundColor = [UIColor blackColor]; _cover.alpha = 0.0f; [self.view addSubview:_cover]; [_cover addTarget:self action:@selector(bigImage) forControlEvents:UIControlEventTouchUpInside]; } return _cover; } - (void)viewDidLoad { // 如果是"加载"对象的父类方法,父类方法的调用,要放在第一句 [super viewDidLoad]; self.index = -1; [self nextQuestion]; } /** 修改状态栏 */ - (UIStatusBarStyle)preferredStatusBarStyle { // 修改状态栏的颜色(白色) return UIStatusBarStyleLightContent; } /** 修改分数 */ // 抽代码的原则: // 1> 将公共部分的代码抽取出来 // 2> 根据各自的特性设置参数 - (void)changeScore:(int)score { int currentScore = [self.scoreButton.currentTitle intValue]; currentScore += score; [self.scoreButton setTitle:[NSString stringWithFormat:@"%d", currentScore] forState:UIControlStateNormal]; } /** 提示按钮 */ - (IBAction)tips { // 1. 将答案区的所有按钮清空 for (UIButton *btn in self.answerView.subviews) { [self answerClick:btn]; } // 2. 找到正确答案的第一个字,显示到答案区中的第一个按钮上 LFQuestion *question = self.questions[self.index]; // 越光宝盒 NSString *firstWord = [question.answer substringToIndex:1]; // 3. 遍历所有的备选按钮,找到第一个匹配的文字,模拟点击 for (UIButton *btn in self.optionView.subviews) { if ([btn.currentTitle isEqualToString:firstWord]) { [self optionClick:btn]; // 减分操作 [self changeScore:-1000]; break; } } } /** 下一题 */ - (IBAction)nextQuestion { // 1. 题目索引递增 self.index++; if (self.index >= self.questions.count) { // 播放一个动画效果,或者其他的操作…… NSLog(@"通关了!"); return; } // 2. 取出索引对应的题目模型 LFQuestion *question = self.questions[self.index]; // 3. 设置基本信息 [self setupBasicInfo:question]; // 4. 创建答案按钮 [self createAnswerButtons:question]; // 5. 创建备选答案按钮 [self createOptionButtons:question]; } /** 设置基本信息 */ - (void)setupBasicInfo:(LFQuestion *)question { self.noLabel.text = [NSString stringWithFormat:@"%d/%d", self.index + 1, self.questions.count]; self.titleLabel.text = question.title; [self.iconView setImage:question.image forState:UIControlStateNormal]; self.nextButton.enabled = (self.index != self.questions.count - 1); } /** 创建答案按钮 */ - (void)createAnswerButtons:(LFQuestion *)question { // 0> 将答案区的按钮全部删除 for (UIButton *btn in self.answerView.subviews) { [btn removeFromSuperview]; } // 1> 按钮个数和答案的字数有关 int length = question.answer.length; CGFloat answerViewW = self.answerView.bounds.size.width; CGFloat answerX = (answerViewW - length * kButtonW - (length - 1) * kButtonMargin) * 0.5; for (int i = 0; i < length; i++) { CGFloat x = answerX + i * (kButtonW + kButtonMargin); UIButton *answerBtn = [[UIButton alloc] initWithFrame:CGRectMake(x, 0, kButtonW, kButtonH)]; [answerBtn setBackgroundImage:[UIImage imageNamed:@"btn_answer"] forState:UIControlStateNormal]; [answerBtn setBackgroundImage:[UIImage imageNamed:@"btn_answer_highlighted"] forState:UIControlStateHighlighted]; [answerBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [self.answerView addSubview:answerBtn]; // 添加监听方法 [answerBtn addTarget:self action:@selector(answerClick:) forControlEvents:UIControlEventTouchUpInside]; } } /** 创建备选答案按钮 */ - (void)createOptionButtons:(LFQuestion *)question { // 判断备选区视图中按钮的个数,如果不等于question.options.count,删除原有按钮,重新新建 if (self.optionView.subviews.count != question.options.count) { for (UIButton *btn in self.optionView.subviews) { [btn removeFromSuperview]; } CGFloat optionViewW = self.optionView.bounds.size.width; CGFloat optionX = (optionViewW - kTotalCol * kButtonW - (kTotalCol - 1) * kButtonMargin) * 0.5; for (int i = 0; i < question.options.count; i++) { int row = i / kTotalCol; int col = i % kTotalCol; CGFloat x = optionX + col * (kButtonW + kButtonMargin); CGFloat y = row * (kButtonH + kButtonMargin); UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(x, y , kButtonW, kButtonH)]; [btn setBackgroundImage:[UIImage imageNamed:@"btn_option"] forState:UIControlStateNormal]; [btn setBackgroundImage:[UIImage imageNamed:@"btn_option_highlighted"] forState:UIControlStateHighlighted]; [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [self.optionView addSubview:btn]; // 添加监听方法,点击事件 [btn addTarget:self action:@selector(optionClick:) forControlEvents:UIControlEventTouchUpInside]; } } // 设置按钮标题,遍历optionView,依次设置每一个按钮的标题 int i = 0; for (UIButton *btn in self.optionView.subviews) { // 设置按钮标题 [btn setTitle:question.options[i++] forState:UIControlStateNormal]; // 恢复所有隐藏的按钮 btn.hidden = NO; } } /** 答案按钮的点击事件 */ - (void)answerClick:(UIButton *)btn { // 1. 是否有文字,如果没有,直接返回 if (btn.currentTitle.length == 0) return; // 2. 如果有文字 // 1> 将对应的备选按钮恢复隐藏 for (UIButton *button in self.optionView.subviews) { if ([button.currentTitle isEqualToString:btn.currentTitle] && button.isHidden) { button.hidden = NO; // 2> 清空答案按钮中的文字 [btn setTitle:nil forState:UIControlStateNormal]; break; } } // 3. 点击答案按钮后,意味这答案不完整了,将所有按钮的颜色设置为黑色 for (UIButton *btn in self.answerView.subviews) { [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; } } /** 备选按钮点击事件 */ - (void)optionClick:(UIButton *)btn { // 1> 把备选按钮中的文字,填充到答案区 // 找答案区中第一个按钮文字为空的按钮 for (UIButton *button in self.answerView.subviews) { if (button.currentTitle.length == 0) { [button setTitle:btn.currentTitle forState:UIControlStateNormal]; break; } } // 2> 把按钮隐藏 btn.hidden = YES; // 3> 判断胜负 // 3.1 所有的答案按钮都填满,遍历所有答案区的按钮 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) { // 用户选择的答案和当前题目的答案向对比 LFQuestion *question = self.questions[self.index]; if ([question.answer isEqualToString:strM]) { // 修改答案区按钮的颜色 -> 蓝色 for (UIButton *btn in self.answerView.subviews) { [btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; } // 加分操作 [self changeScore:500]; // 等待0.5s之后,跳到下一题 [self performSelector:@selector(nextQuestion) withObject:nil afterDelay:0.5]; } else { NSLog(@"错错错!"); // 修改答案区按钮的颜色 -> 红色 for (UIButton *btn in self.answerView.subviews) { [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; } } } } /** 大图 */ - (IBAction)bigImage { /** 当按钮的alpha < 0.001 的时候,按钮不响应点击事件 */ // 1. 增加蒙版(跟根视图一样大小) if (self.cover.alpha == 0.0) { // 2. 将图片移动到视图的顶层 [self.view bringSubviewToFront:self.iconView]; // 3. 动画放大图片 // 1> 计算目标位置 CGFloat viewW = self.view.bounds.size.width; CGFloat imageW = viewW; CGFloat imageH = imageW; CGFloat imageY = (self.view.bounds.size.height - imageH) * 0.5; [UIView animateWithDuration:1.0f animations:^{ self.cover.alpha = 0.5; self.iconView.frame = CGRectMake(0, imageY, imageW, imageH); }]; } else { // 图片已经是放大显示的了 [UIView animateWithDuration:1.0 animations:^{ // 1. 动画变小 self.iconView.frame = CGRectMake(85, 80, 150, 150); // 2. 遮罩透明,看不见了 self.cover.alpha = 0.0f; }]; } } @end
================================题目模型结构==============================
LFQuestion.h:
#import <Foundation/Foundation.h> @interface LFQuestion : NSObject /** 答案 */ @property (nonatomic, copy) NSString *answer; /** 提示文字 */ @property (nonatomic, copy) NSString *title; /** 图片名称 */ @property (nonatomic, copy) NSString *icon; /** 备选文字数组 */ @property (nonatomic, strong) NSArray *options; /** 图像 */ @property (nonatomic, strong, readonly) UIImage *image; /** 用字典实例化对象的成员方法 */ - (instancetype)initWithDict:(NSDictionary *)dict; /** 用字典实例化对象的类方法,又称工厂方法 */ + (instancetype)questionWithDict:(NSDictionary *)dict; /** 从plist加载对象数组 */ + (NSArray *)questions; @end
LFQuestion.m:
#import "LFQuestion.h" @interface LFQuestion() { UIImage *_image; } @end @implementation LFQuestion - (UIImage *)image { if (!_image) { _image = [UIImage imageNamed:self.icon]; } return _image; } - (instancetype)initWithDict:(NSDictionary *)dict { self = [super init]; if (self) { // 使用setValuesForKeys要求类的属性必须在字典中存在,可以比字典中的键值多,但是不能少! [self setValuesForKeysWithDictionary:dict]; } 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:[LFQuestion questionWithDict:dict]]; } return arrayM; } // 如果要在开发时,跟踪对象的明细信息,可以重写description方法,类似于java的toString() - (NSString *)description { // 包含对象类型名称,以及对象的指针地址 return [NSString stringWithFormat:@"<%@: %p> {answer: %@, title: %@, icon: %@, options: %@}", [self class], self, self.answer, self.title, self.icon, self.options]; } @end