软件设计思路和需求分析:
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