[iOS开发项目-7] 超级猜图
来源:互联网 发布:怎么复制淘宝宝贝标题 编辑:程序博客网 时间:2024/04/27 17:58
本项目是取自传智播客的教学项目,加入笔者的修改和润饰。
1. 项目名称:超级猜图
2. 项目截图展示
3. 项目功能
- 点击图片或“大图”按钮,图片放大;再点击图片或点击周围区域,图片复原。
- 点击备选按钮,相应字填入答案区按钮。
- 按“下一题”按钮或答案正确:进入下一题。
- 点击答案区某按钮,相应字回到备选区原来位置。
- 点击“帮助按钮”会清空答案区按钮,并提示正确答案的第一个字。
- 答案正确或错误都有相应的扣分和加分。
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
- [iOS开发项目-7] 超级猜图
- IOS开发笔记28-超级猜图
- iOS学习项目(06-超级猜图)
- iOS开发脚踏实地学习day04-超级猜图
- iOS开发 - 第01篇 - UI基础 - 04 - 超级猜图
- iOS学习之路-超级猜图
- ios学习之个人笔记(超级猜图)
- IOS-UI学习笔记(超级猜图)
- iOS项目开发经验:【常用代码7】
- ios项目开发笔记
- ios开发---项目总结
- ios开发,---学习项目
- iOS开发- 项目配置
- ios项目开发流程
- iOS项目开发规范
- iOS项目开发实例
- iOS 项目开发流程
- iOS项目开发流程
- Java内部类
- OC中重写构造方法及自定义构造方法
- C# 线程管理
- DIV CSS布局中绝对定位和浮动用法
- 排序算法的C语言实现
- [iOS开发项目-7] 超级猜图
- Dubbo > Dubbo服务化改造
- 从今天开始,每天写一篇博客
- Java web 图片上传(文件上传)
- 提高Android Studio中Gradle执行效率
- ubuntu装jdk
- 屏蔽浏览器窗口自带的事件
- loadView、viewDidLoad及viewDidUnload的关系
- VB 连接AutoCad 模块代码