今天给同学带来一个看图猜字的小游戏通过storyboard,内部涉及很多细节和代码的抽取与封装,内部涉及非常之多的细节和小的处理以及新的知识点,那么废话不多说直接上代码!先看效果图~
#pragma mark - 那么先看storyboard如何构建
//
// ZZViewController.m
// 01-极限猜图
//
// Created by 周昭 on 16/10/25.
// Copyright © 2016年 HT_Technology. All rights reserved.
//
#import "ZZViewController.h"
#import "ZZQuestion.h"
#import "MBProgressHUD+MJ.h"
@interface ZZViewController()
/**
* 提示
*/
- (IBAction)tip;
/**
* 点击头像
*/
- (IBAction)iconClick;
/**
* 点击大图
*/
- (IBAction)bigImg;
/**
* 点击下一题
*/
- (IBAction)nextQuestion;
/**
* 图标
*/
@property (weak, nonatomic) IBOutlet UIButton *iconBtn;
/**
* 序号
*/
@property (weak, nonatomic) IBOutlet UILabel *noLabel;
/**
* 标题
*/
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
/**
* 遮盖
*/
@property (nonatomic, weak) UIButton *cover;
/**
* 所有的题目
*/
@property (nonatomic, strong) NSArray *questions;
/**
* 当前是第几题(当前题目的序号)
*/
@property (nonatomic, assign) int index;
/**
* 下一题按钮
*/
@property (weak, nonatomic) IBOutlet UIButton *nextQuestionBtn;
/**
* 答案的view
*/
@property (weak, nonatomic) IBOutlet UIView *answerView;
/**
* 待选项view
*/
@property (weak, nonatomic) IBOutlet UIView *optionsView;
/**
* 分数按钮
*/
@property (weak, nonatomic) IBOutlet UIButton *scoreBtn;
/**
* 提示按钮
*/
@property (weak, nonatomic) IBOutlet UIButton *tipBtn;
@end
@implementation ZZViewController
#pragma mark - 今天我们就来通过storyboard来创建项目讲解storyboard用法
/**
* 控制状态栏样式
*/
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
- (NSArray *)questions
{
if (_questions == nil) {
#pragma mark - ofType <==> .plist
// 1.加载plist
NSArray *dictArr = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"questions.plist" ofType:nil]];
// 2.字典转模型
NSMutableArray *questionArr = [NSMutableArray array];
for (NSDictionary *dict in dictArr) {
ZZQuestion *question = [ZZQuestion questionWithDict:dict];
[questionArr addObject:question];
}
// 3.赋值
_questions = questionArr;
}
return _questions;
}
- (void)viewDidLoad
{
#pragma mark - 首先第一点改类名
[super viewDidLoad];
#pragma mark - 一进来就要显示那么相当于点击了下一题
self.index = -1;
[self nextQuestion];
}
#pragma mark - 提示
- (IBAction)tip {
// 1.点击所有的答案按钮
for (UIButton *answerBtn in self.answerView.subviews) {
[self answerBtnClick:answerBtn];
}
// 2.取出答案
ZZQuestion *question = self.questions[self.index];
// 3.拿到答案的第一个字
#pragma mark - str = abcdefg 那么这个截取就是截取到数组位置为1的为止
NSString *firstAnswer = [question.answer substringFromIndex:1];
for (UIButton *optionBtn in self.optionsView.subviews) {
if ([optionBtn.currentTitle isEqualToString:firstAnswer]) {
[self optionClick:optionBtn];
break;
}
}
// 4.扣分
[self addScore:-500];
// 5.按钮不可点击
self.tipBtn.enabled = NO;
}
#pragma mark - 点击头像
- (IBAction)iconClick {
if (self.cover == nil) { // 没有遮盖,要放大
#pragma mark - 这里我们要考虑点击了这里相当于执行了什么操作不要傻逼到又写一次动画
[self bigImg];
} else { // 有遮盖,要缩小
[self smallImg];
}
}
#pragma mark - 放大动画
- (IBAction)bigImg {
// 1.添加阴影
UIButton *cover = [[UIButton alloc] init];
cover.frame = self.view.bounds;
cover.backgroundColor = [UIColor blackColor];
cover.alpha = 0.0;
[cover addTarget:self action:@selector(smallImg) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:cover];
self.cover = cover;
// 2.更换引用和头像的位置
[self.view bringSubviewToFront:self.iconBtn];
// 3.执行动画
[UIView animateWithDuration:0.25 animations:^{
// 3.1阴影慢慢显示出来
cover.alpha = 0.7;
// 3.2头像慢慢变大,慢慢的移动到屏幕中间
CGFloat iconW = self.view.frame.size.width;
CGFloat iconH = iconW;
CGFloat iconY = (self.view.frame.size.height - iconH) * 0.5; // 乘法的效率比除法快
self.iconBtn.frame = CGRectMake(0, iconY, iconW, iconH);
}];
}
#pragma mark - 缩小动画
- (void)smallImg {
[UIView animateWithDuration:0.25 animations:^{
// 1.头像慢慢变为原来的位置和尺寸
#pragma mark - 这里我们应该是根据图片的宽度和高度设置那么这里我直接照着storyboard直接写死
self.iconBtn.frame = CGRectMake(85, 120, 150, 150);
// 2.阴影慢慢消失
self.cover.alpha = 0.0;
} completion:^(BOOL finished) {
// 3.动画执行完清空遮盖(从内存中移除)
[self.cover removeFromSuperview];
#pragma mark - 这里是亮点这里我们可以直接判断cover是否为nil执行什么操作
self.cover = nil;
}];
}
#pragma mark - 下一题
- (IBAction)nextQuestion {
// 0.提示按钮可以点击
self.tipBtn.enabled = YES;
// 1.增加索引
self.index++;
// 2.取出模型
ZZQuestion *question = self.questions[self.index];
// 3.设置控件的数据
[self setUpTitleData:question];
// 4.添加正确按钮
[self addAnswerBtn:question];
// 5.添加待选项
[self addOptionBtn:question];
}
/**
* 设置控件的数据
*/
- (void)setUpTitleData:(ZZQuestion *)question
{
// 1.设置序号
self.noLabel.text = [NSString stringWithFormat:@"%d/%lu",self.index + 1,(unsigned long)self.questions.count];
// 2.设置标题
self.titleLabel.text = question.title;
// 3.设置图片
[self.iconBtn setImage:[UIImage imageNamed:question.icon] forState:UIControlStateNormal];
// 4.设置下一题按钮的状态
self.nextQuestionBtn.enabled = self.index != (self.questions.count - 1);
}
/**
* 添加答案
*/
- (void)addAnswerBtn:(ZZQuestion *)question
{
#pragma mark - 必须先删除之前一题的所有
// 1.移除之前所有的按钮
[self.answerView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
// 2.0取得正确答案长度并添加答案按钮
NSUInteger length = question.answer.length;
for (int i = 0; i<length; i++) {
// 2.1创建按钮
UIButton *answerBtn = [[UIButton alloc] init];
[answerBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
// 2.2设置背景
[answerBtn setBackgroundImage:[UIImage imageNamed:@"btn_answer"] forState:UIControlStateNormal];
[answerBtn setBackgroundImage:[UIImage imageNamed:@"btn_answer_highlighted"] forState:UIControlStateHighlighted];
// 2.3设置frame
CGFloat viewW = self.view.frame.size.width;
// 按钮之间的间距
CGFloat margin = 10;
// 按钮的尺寸
CGFloat answerBtnW = 35;
CGFloat answerBtnH = 35;
// 最左边的间距 = 0.5 * (控制器view的宽度 - 按钮个数 * 按钮宽度 - (按钮个数 - 1) * 按钮之间的间距)
CGFloat leftMargin = (viewW - length * answerBtnW - margin * (length - 1)) * 0.5;
// 按钮的x = 最左边的间距 + i * (按钮宽度 + 按钮之间的间距)
CGFloat answerBtnX = leftMargin + i * (margin + answerBtnW);
answerBtn.frame = CGRectMake(answerBtnX, 0, answerBtnW, answerBtnH);
// 2.4添加
[self.answerView addSubview:answerBtn];
// 2.5添加监听
[answerBtn addTarget:self action:@selector(answerBtnClick:) forControlEvents:UIControlEventTouchUpInside];
}
}
/**
* 点击答案按钮
*/
- (void)answerBtnClick:(UIButton *)answerBtn
{
// 1.让答案按钮文字对应的待选按钮显示出来(hidden = NO)
for (UIButton *optionBtn in self.optionsView.subviews) {
#pragma mark - 发现答案文字跟待选项文字一样那么就退回去I(可是如果有俩个一样的文字呢?所以加上当初hidden == YES这个小判断)
if ([optionBtn.currentTitle isEqualToString:answerBtn.currentTitle] && optionBtn.hidden == YES) {
optionBtn.hidden = NO;
break;
}
}
// 2.让被电击按钮的文字消失(去除文字)
[answerBtn setTitle:nil forState:UIControlStateNormal];
// 3.让所有答案按钮变成黑色
for (UIButton *answerBtn in self.answerView.subviews) {
[answerBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
}
}
/**
* 添加待选项
*/
- (void)addOptionBtn:(ZZQuestion *)question
{
#pragma mark - 必须先删除之前一题的所有
// 1.移除之前所有的按钮
[self.optionsView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
// 2.添加新的待选按钮
NSUInteger count = question.options.count;
for (int i = 0; i<count; i++) {
// 3.1创建按钮
UIButton *optionBtn = [[UIButton alloc] init];
// 3.2设置背景
[optionBtn setBackgroundImage:[UIImage imageNamed:@"btn_option"] forState:UIControlStateNormal];
[optionBtn setBackgroundImage:[UIImage imageNamed:@"btn_option_highlighted"] forState:UIControlStateHighlighted];
// 3.3设置frame
// 按钮尺寸
CGFloat optionBtnW = 35;
CGFloat optionBtnH = 35;
// 按钮间的间距
CGFloat margin = 10;
// 控制器view的宽度
CGFloat viewW = self.view.frame.size.width;
int totalColumns = 7;
// 最左边的间距 = 0.5 * (控制器view的宽度 - 总列数 * 按钮宽度 - (总列数 - 1) * 按钮之间的间距)
CGFloat leftMargin = (viewW - totalColumns * optionBtnW - margin * (totalColumns - 1)) * 0.5;
int col = i % totalColumns;
// 按钮的x = 最左边的间距 + 列号 * (按钮宽度 + 按钮之间的间距)
CGFloat optionBtnX = leftMargin + col * (optionBtnW + margin);
int row = i / totalColumns;
// 按钮的y = 行号 * (按钮高度 + 按钮之间的间距)
CGFloat optionBtnY = row * (optionBtnH + margin);
optionBtn.frame = CGRectMake(optionBtnX, optionBtnY, optionBtnW, optionBtnH);
// 3.4添加
[self.optionsView addSubview:optionBtn];
// 3.5设置文字
[optionBtn setTitle:question.options[i] forState:UIControlStateNormal];
[optionBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
// 3.6监听点击
[optionBtn addTarget:self action:@selector(optionClick:) forControlEvents:UIControlEventTouchUpInside];
}
}
/**
* 点击待选按钮
*/
- (void)optionClick:(UIButton *)optionBtn
{
// 1.让点击的待选按钮消失
optionBtn.hidden = YES;
// 2.显示文字到正确的按钮上
for (UIButton *answerBtn in self.answerView.subviews) {
#pragma mark - 因为按钮有状态所以根据按钮的状态来取出文字
// NSString *answerTitle = [answerBtn titleForState:UIControlStateNormal];
NSString *answerTitle = answerBtn.currentTitle;
if (answerTitle.length == 0) { // 没有文字
NSString *optionTitle = optionBtn.currentTitle;
[answerBtn setTitle:optionTitle forState:UIControlStateNormal];
break; // 停止遍历
}
}
// 3.检测答案是否填满
BOOL full = YES;
NSMutableString *tempAnswerTitle = [NSMutableString string];
for (UIButton *answerBtn in self.answerView.subviews) {
// 判断按钮是否有文字
NSString *answerTitle = answerBtn.currentTitle;
if (answerTitle.length == 0) { // 没有文字(按钮没有填满)
full = NO;
}
if (answerTitle) {
[tempAnswerTitle appendString:answerTitle];
}
}
// 4.答案满了
if (full) {
ZZQuestion *question = self.questions[self.index];
if ([tempAnswerTitle isEqualToString:question.answer]) { // 答对了(文字变成彩色)
for (UIButton *answerBtn in self.answerView.subviews) {
[answerBtn setTitleColor:[UIColor colorWithRed:(arc4random()%255)/255.0 green:(arc4random()%255)/255.0 blue:(arc4random()%255)/255.0 alpha:1.0] forState:UIControlStateNormal];
}
// 4.1加分
[self addScore:1500];
// 4.2 0.5秒后跳到下一题
[self performSelector:@selector(nextQuestion) withObject:nil afterDelay:0.9];
} else { // 打错了(文字显示红色)
for (UIButton *answerBtn in self.answerView.subviews) {
[answerBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
}
[MBProgressHUD showError:NSLocalizedString(@"很遗憾不正确哦...", nil)];
}
}
}
/**
* 计算分数
*/
- (void)addScore:(NSInteger)deltaScore
{
if (deltaScore > 0) {
// 1.答对提示
[MBProgressHUD showSuccess:[NSString stringWithFormat:NSLocalizedString(@"恭喜您答对了奖励%ld分数", nil),deltaScore]];
} else {
// 2.提醒提示
[UIView animateWithDuration:1.5 animations:^{ // 分数为负数(一定要取正数)
[MBProgressHUD showMessage:[NSString stringWithFormat:@"很遗憾扣了%ld",(-deltaScore)]];
} completion:^(BOOL finished) {
[MBProgressHUD hideHUD];
}];
}
// 2.计算分数
int score = self.scoreBtn.currentTitle.intValue;
score += deltaScore;
// 3.设置分数
[self.scoreBtn setTitle:[NSString stringWithFormat:@"%d",score] forState:UIControlStateNormal];
}
@end
//
// ZZQuestion.h
// 01-极限猜图
//
// Created by 周昭 on 16/10/25.
// Copyright © 2016年 HT_Technology. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface ZZQuestion : NSObject
/**
* 答案
*/
@property (nonatomic, copy) NSString *answer;
/**
* 标题
*/
@property (nonatomic, copy) NSString *title;
/**
* 图标
*/
@property (nonatomic, copy) NSString *icon;
/**
* 待选项
*/
@property (nonatomic, strong) NSArray *options;
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)questionWithDict:(NSDictionary *)dict;
@end
//
// ZZQuestion.m
// 01-极限猜图
//
// Created by 周昭 on 16/10/25.
// Copyright © 2016年 HT_Technology. All rights reserved.
//
#import "ZZQuestion.h"
@implementation ZZQuestion
- (instancetype)initWithDict:(NSDictionary *)dict
{
if (self = [super init]) {
self.icon = dict[@"icon"];
self.title = dict[@"title"];
self.answer = dict[@"answer"];
self.options = dict[@"options"];
}
return self;
}
+ (instancetype)questionWithDict:(NSDictionary *)dict
{
return [[self alloc] initWithDict:dict];
}
@end