[iOS開發項目-7] 超級猜圖


本項目是取自傳智播客的教學項目,加入筆者的修改和潤飾。

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;

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com