毕业设计总结——自闭症儿童识别面部表情训练系统
很开心完成了这个app,在新年愿望里其中有一个就是能有app上线AppStore,虽然未达到但相信会完成的。
从15年12月份考研之后到今年的3月初,根据指导老师黄玲老师的需求做需求分析文档、设计文档、UML建模还有每一星期的计划报告,从需求分析到完成设计,体验了一次完整的软件开发过程。
开学来到学校,同学们都在忙着找工作,校招、考公务员,那时我的心思全在项目上面,以致于到现在寝室里只剩我还在奔波找工作,小伙伴们准备打包行李回家/(ㄒoㄒ)/~~。不过,独立完成开发APP还是很爽的。
好了,开始正题!
我选择的毕业设计是自闭症儿童识别面部表情训练系统,老师的需求是通过玩游戏的形式让自闭症儿童识别人脸面部表情。参照过三星做的一款自闭症儿童辅助治疗app——LOOK AT ME,首尔大学的心理学教授,研究自闭症的专家来参与制作,是三星的良心之作。国内也有一款叫星星语,但只是一款辅助自闭症儿童进行与人交流的app。做不到三星那样的高大上,但有些地方是可以学习的。比如其中的拍摄人脸表情训练,于是,我想到了用上传用户头像。也就是实现出来是,给出一个题目让儿童拍摄人脸照片,(前提是与父母一起做这个训练,与父母做训练有利于儿童进行社会交流能力),完成后由父母评定拍摄照片是否正确,而LOOK AT ME是系统判定。
用的是hei苹果,Xcode版本是15年下载的6.0。
情景训练部分代码:
1 // 2 // sightViewController.m 3 // 情绪识别训练 4 // 5 // Created by lpx on 16/3/16. 6 // Copyright (c) 2016年 lpx. All rights reserved. 7 // 8 9 #import "sightViewController.h" 10 11 @interface sightViewController () 12 @property (weak, nonatomic) IBOutlet UIImageView *imaView; 13 @property(nonatomic, strong) NSData *fileData; 14 @property (weak, nonatomic) IBOutlet UILabel *showLabel; 15 @end 16 17 @implementation sightViewController 18 19 int count = 0; //计数 20 21 //返回菜单 22 - (IBAction)backBtnClick:(id)sender { 23 [self dismissModalViewControllerAnimated:YES]; 24 } 25 #pragma mark - 修改状态栏样式 26 - (UIStatusBarStyle)preferredStatusBarStyle { 27 // 把statusBar的样式修改为白色 28 return UIStatusBarStyleLightContent; 29 } 30 31 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 32 { 33 self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 34 if (self) { 35 // Custom initialization 36 } 37 return self; 38 } 39 40 - (void)viewDidLoad { 41 [super viewDidLoad]; 42 //换行 43 _showLabel.lineBreakMode = UILineBreakModeWordWrap; 44 _showLabel.numberOfLines = 0; 45 46 self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Bg-02.png"]]; 47 48 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 49 NSString *documentsDirectory = [paths objectAtIndex:0]; 50 NSString *imageFilePath = [documentsDirectory stringByAppendingPathComponent:@"selfPhoto.jpg"]; 51 NSLog(@"imageFile->>%@",imageFilePath); 52 UIImage *selfPhoto = [UIImage imageWithContentsOfFile:imageFilePath];// 53 self.imaView.image = selfPhoto; 54 [self.imaView.layer setCornerRadius:CGRectGetHeight([self.imaView bounds]) / 10];//修改半径 55 self.imaView.layer.masksToBounds = YES; 56 _showLabel.text = @"请拍一张高兴的人脸照片"; 57 count=0;//防止再次载入的时候 点击正确按钮无反应 58 } 59 60 - (IBAction)btnClik:(UIButton *)sender { 61 62 UIActionSheet* actionSheet = [[UIActionSheet alloc] 63 initWithTitle:@"请选择文件来源" 64 delegate:self 65 cancelButtonTitle:@"取消" 66 destructiveButtonTitle:nil 67 otherButtonTitles:@"照相机",@"本地相簿",nil]; 68 [actionSheet showInView:self.view]; 69 } 70 - (IBAction)xiugai:(UIButton *)sender { 71 72 73 [self.imaView setHidden:YES]; 74 } 75 76 - (IBAction)zhengque:(UIButton *)sender { 77 count =count+1; 78 switch (count) { 79 case 1: 80 _showLabel.text=@"请拍一张悲伤的人脸照片"; 81 break; 82 case 2: 83 _showLabel.text=@"请拍一张惊讶的人脸照片"; 84 break; 85 case 3: 86 _showLabel.text=@"请拍一张害怕的人脸照片"; 87 break; 88 case 4: 89 _showLabel.text=@"请拍一张生气的照片"; 90 break; 91 case 5: 92 _showLabel.text=@"请拍一张中性的人脸照片"; 93 break; 94 default: 95 _showLabel.text=@"跟爸爸或妈妈合照一张,结束今天的训练!"; 96 break; 97 } 98 } 99 100 #pragma mark - 101 #pragma UIActionSheet Delegate 102 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex 103 { 104 // NSLog(@"buttonIndex = [%d]",buttonIndex); 105 switch (buttonIndex) { 106 case 0://照相机 107 { 108 UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; 109 imagePicker.delegate = self; 110 imagePicker.allowsEditing = YES; 111 imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; 112 // [self presentModalViewController:imagePicker animated:YES]; 113 [self presentViewController:imagePicker animated:YES completion:nil]; 114 } 115 break; 116 case 1://本地相簿 117 { 118 UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; 119 imagePicker.delegate = self; 120 imagePicker.allowsEditing = YES; 121 imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; 122 // [self presentModalViewController:imagePicker animated:YES]; 123 [self presentViewController:imagePicker animated:YES completion:nil]; 124 } 125 break; 126 default: 127 break; 128 } 129 } 130 131 #pragma mark - 132 #pragma UIImagePickerController Delegate 133 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info 134 { 135 if ([[info objectForKey:UIImagePickerControllerMediaType] isEqualToString:(__bridge NSString *)kUTTypeImage]) { 136 UIImage *img = [info objectForKey:UIImagePickerControllerEditedImage]; 137 [self performSelector:@selector(saveImage:) withObject:img afterDelay:0.5]; 138 } 139 else if ([[info objectForKey:UIImagePickerControllerMediaType] isEqualToString:(__bridge NSString *)kUTTypeMovie]) { 140 NSString *videoPath = [[info objectForKey:UIImagePickerControllerMediaURL] path]; 141 self.fileData = [NSData dataWithContentsOfFile:videoPath]; 142 } 143 // [picker dismissModalViewControllerAnimated:YES]; 144 [picker dismissViewControllerAnimated:YES completion:nil]; 145 } 146 147 - (void)saveImage:(UIImage *)image { 148 BOOL success; 149 NSFileManager *fileManager = [NSFileManager defaultManager]; 150 NSError *error; 151 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 152 NSString *documentsDirectory = [paths objectAtIndex:0]; 153 NSString *imageFilePath = [documentsDirectory stringByAppendingPathComponent:@"selfPhoto.jpg"]; 154 NSLog(@"imageFile->>%@",imageFilePath); 155 success = [fileManager fileExistsAtPath:imageFilePath]; 156 if(success) { 157 success = [fileManager removeItemAtPath:imageFilePath error:&error]; 158 } 159 UIImage *smallImage = [self thumbnailWithImageWithoutScale:image size:CGSizeMake(300.0f, 300.0f)];//将图片尺寸改为300*300 160 [UIImageJPEGRepresentation(smallImage, 1.0f) writeToFile:imageFilePath atomically:YES];//写入文件 161 UIImage *selfPhoto = [UIImage imageWithContentsOfFile:imageFilePath];//读取图片文件 162 self.imaView.image = selfPhoto; 163 } 164 165 - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker 166 { 167 // [picker dismissModalViewControllerAnimated:YES]; 168 [picker dismissViewControllerAnimated:YES completion:nil]; 169 } 170 171 //保持原来的长宽比,生成一个缩略图 172 - (UIImage *)thumbnailWithImageWithoutScale:(UIImage *)image size:(CGSize)asize 173 { 174 UIImage *newimage; 175 if (nil == image) { 176 newimage = nil; 177 } 178 else{ 179 CGSize oldsize = image.size; 180 CGRect rect; 181 if (asize.width/asize.height > oldsize.width/oldsize.height) { 182 rect.size.width = asize.height*oldsize.width/oldsize.height; 183 rect.size.height = asize.height; 184 rect.origin.x = (asize.width - rect.size.width)/2; 185 rect.origin.y = 0; 186 } 187 else{ 188 rect.size.width = asize.width; 189 rect.size.height = asize.width*oldsize.height/oldsize.width; 190 rect.origin.x = 0; 191 rect.origin.y = (asize.height - rect.size.height)/2; 192 } 193 UIGraphicsBeginImageContext(asize); 194 CGContextRef context = UIGraphicsGetCurrentContext(); 195 CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]); 196 UIRectFill(CGRectMake(0, 0, asize.width, asize.height));//clear background 197 [image drawInRect:rect]; 198 newimage = UIGraphicsGetImageFromCurrentImageContext(); 199 UIGraphicsEndImageContext(); 200 } 201 202 [self.imaView setHidden:NO]; //点击修改button的时候imgview不可见 在这里回复可见 203 return newimage; 204 } 205 @end
训练分为四部分,接下来就是基础训练。
基础训练,我是以播放帧动画的形式表现人脸情绪表情,播放动画的同时还会播放一段故事情景语音,带儿童进入一个故事情景训练,我想这样的训练效果会更好。
基础训练具体代码实现:
1 // 2 // highViewController.m 3 // 情绪识别训练 4 // 5 // Created by lpx on 16/3/16. 6 // Copyright (c) 2016年 lpx. All rights reserved. 7 // 8 9 #import "highViewController.h" 10 #import <AudioToolbox/AudioToolbox.h> 11 12 typedef enum 13 { 14 kHappy = 0, // 高兴 15 kPain, // 悲伤 16 kAngry, // 生气 17 kFear, // 害怕 18 kSurprise, // 惊讶 19 kDetest, // 厌恶 20 kNormal, // 中性 21 } kAnimationType; 22 23 @interface highViewController () 24 { 25 //表情数据字典 26 NSMutableDictionary *_emotionDict; 27 28 // 音效的数据字典 29 NSMutableDictionary *_soundDict; 30 } 31 @end 32 33 @implementation highViewController 34 //返回菜单 35 - (IBAction)backBtnClick:(id)sender { 36 [self dismissModalViewControllerAnimated:YES]; 37 } 38 39 #pragma mark - 修改状态栏样式 40 - (UIStatusBarStyle)preferredStatusBarStyle { 41 // 把statusBar的样式修改为白色 42 return UIStatusBarStyleLightContent; 43 } 44 45 /** 46 用数据字典来实现音效的管理 47 */ 48 - (SystemSoundID)loadSoundId:(NSString *)soundFile 49 { 50 NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:soundFile ofType:nil]]; 51 SystemSoundID soundId; 52 AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url), &soundId); 53 return soundId; 54 } 55 56 //初始化界面入口 57 - (void)viewDidLoad { 58 [super viewDidLoad]; 59 60 //设置背景图片 61 self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Bg-02.png"]]; 62 63 // 数据初始化工作,加载数据字典成员变量 64 65 // 1. 需要指定路径 66 NSString *path = [[NSBundle mainBundle]pathForResource:@"Emotion" ofType:@"plist"]; 67 68 // 2. 加载数据字典 69 _emotionDict = [NSMutableDictionary dictionaryWithContentsOfFile:path]; 70 71 // 3. 初始化音效字典 72 _soundDict = [NSMutableDictionary dictionary]; 73 74 } 75 76 - (IBAction)animationBtnClick:(UIButton *)sender { 77 if ([_emotionView isAnimating]) { 78 return; 79 } 80 NSDictionary *dict; 81 switch (sender.tag) { 82 case kHappy: 83 dict = _emotionDict[@"happy"]; 84 break; 85 case kPain: 86 dict = _emotionDict[@"pain"]; 87 break; 88 case kAngry: 89 dict = _emotionDict[@"angry"]; 90 break; 91 case kFear: 92 dict = _emotionDict[@"fear"]; 93 break; 94 case kSurprise: 95 dict = _emotionDict[@"surprise"]; 96 break; 97 case kDetest: 98 dict = _emotionDict[@"detest"]; 99 break; 100 case kNormal: 101 dict = _emotionDict[@"normal"]; 102 break; 103 default: 104 break; 105 } 106 107 NSMutableArray *imageList = [NSMutableArray array]; 108 for (NSInteger i = 0; i < [dict[@"frames"]integerValue]; i++) { 109 NSString *imageFile = [NSString stringWithFormat:dict[@"imageFormat"], i]; 110 UIImage *image = [UIImage imageNamed:imageFile]; 111 [imageList addObject:image]; 112 } 113 114 NSArray *array = dict[@"soundFiles"]; 115 // 2) 判断数组中是否有数据,如果有数据做进一步处理 116 SystemSoundID soundId = 0; 117 if (array.count > 0) { 118 // 3) 我们根据数组中得文件名,判断音频字典中是否有对应的记录,如果没有,建立新的音频数据字典 119 for (NSString *fileName in array) { 120 SystemSoundID playSoundId = [_soundDict[fileName]unsignedLongValue]; 121 122 // 如果在字典中没有定义音频代号,初始化音频Id,并且加入字典 123 if (playSoundId <= 0) { 124 playSoundId = [self loadSoundId:fileName]; 125 // 将playSoundId加入到数据字典,向字典中增加数值,不是用add 126 // 向NSDict NSArray中添加数值需要“包装” 127 // @() 会把一个NSInteger的数字,变成NSNumber的对象 128 [_soundDict setValue:@(playSoundId) forKey:fileName]; 129 } 130 } 131 132 // 每一个动画的声音可以是多个,采用随机数的方式播放音效 133 NSInteger seed = arc4random_uniform(array.count); 134 135 NSString *fileName = array[seed]; 136 137 soundId = [_soundDict[fileName]unsignedLongValue]; 138 139 } 140 141 142 // 设置图像的动画属性 143 [_emotionView setAnimationImages:imageList]; 144 [_emotionView setAnimationDuration:[dict[@"frames"]integerValue] / 10.0]; 145 [_emotionView setAnimationRepeatCount:1]; 146 [_emotionView startAnimating]; 147 148 // 播放声音 149 if (soundId > 0) { 150 151 AudioServicesPlaySystemSound(soundId); 152 153 } 154 155 } 156 @end
第三部分是看图猜字游戏训练,给出一张人脸表情,填该图的情绪表情。
具体代码实现:
1 // 2 // BasicViewController.m 3 // 情绪识别训练 4 // 5 // Created by lpx on 16/3/16. 6 // Copyright (c) 2016年 lpx. All rights reserved. 7 // 8 #import "BasicViewController.h" 9 #import "MUModel.h" 10 #import <AVFoundation/AVFoundation.h> 11 12 // 定义 答案区域按钮的宽高 13 #define kButtonWidht 30 14 15 // 定义 答案区域按钮之间的间距 16 #define kMargin 10 17 18 // 屏幕的size 19 #define kScreenSize ([UIScreen mainScreen].bounds.size) 20 21 @interface BasicViewController () 22 23 @property (weak, nonatomic) IBOutlet UILabel *indexLabel; 24 @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 25 @property (weak, nonatomic) IBOutlet UIButton *imagebutton; 26 @property (weak, nonatomic) IBOutlet UIView *answerview; 27 @property (weak, nonatomic) IBOutlet UIView *optionView; 28 @property (weak, nonatomic) IBOutlet UIButton *tipButton; 29 @property (weak, nonatomic) IBOutlet UIButton *nextButton; 30 @property (weak, nonatomic) IBOutlet UIButton *coinButton; 31 @property (nonatomic, strong) NSArray *dataArray; 32 // 表示第几题 33 @property (nonatomic, assign) NSInteger index; 34 @end 35 36 @implementation BasicViewController 37 //返回菜单 38 - (IBAction)backBtnClick:(id)sender { 39 [self dismissModalViewControllerAnimated:YES]; 40 } 41 42 #pragma mark - 修改状态栏样式 43 - (UIStatusBarStyle)preferredStatusBarStyle { 44 // 把statusBar的样式修改为白色 45 return UIStatusBarStyleLightContent; 46 } 47 48 #pragma mark - 懒加载数据 49 - (NSArray *)dataArray { 50 if (nil == _dataArray) { 51 52 // 1. 读取文件路径 53 NSString *path = [[NSBundle mainBundle] pathForResource:@"questions.plist" ofType:nil]; 54 55 // 2. 读取内容到临时数组 56 NSArray *tempArray = [NSArray arrayWithContentsOfFile:path]; 57 58 // 3. 创建一个可变数组 59 NSMutableArray *mutableArray = [NSMutableArray array]; 60 61 // 4. 遍历临时数组中的字典转为模型 , 存放到可变数组 62 for (NSDictionary *dict in tempArray) { 63 MUModel *guessModel = [MUModel guessModelDict:dict]; 64 [mutableArray addObject:guessModel]; 65 } 66 67 // 5. 把可变数组赋值给 _dataArray 68 _dataArray = mutableArray; 69 } 70 return _dataArray; 71 } 72 73 #pragma mark - 页面加载 74 - (void)viewDidLoad { 75 [super viewDidLoad];//界面入口 76 77 // 初始化index = 1 78 _index = 1; 79 80 [self setupUI]; 81 // _nextButton.enabled = NO; 82 } 83 84 #pragma mark - 设置UI界面 85 - (void)setupUI { 86 // 给控件设置数据 87 // 取出_index - 1 在 数组中对应的数据 88 MUModel *guessModel = self.dataArray[_index - 1]; 89 90 // 索引label 91 _indexLabel.text = [NSString stringWithFormat:@"%ld/%ld",_index, self.dataArray.count]; 92 93 // titleLabel 94 _titleLabel.text = guessModel.title; 95 96 //换行 97 _titleLabel.lineBreakMode = UILineBreakModeWordWrap; 98 _titleLabel.numberOfLines = 0; 99 100 // 设置 imageButton 101 // 取出图片名称 102 NSString *imageName = guessModel.icon; 103 104 // 实例化一个image对象 105 UIImage *image = [UIImage imageNamed:imageName]; 106 107 [_imagebutton setImage:image forState:UIControlStateNormal]; 108 109 /** 110 答案区域中, button的个数是跟答案的长度有关 111 根据每道题的答案个数来决定按钮的个数 112 113 初始设置 114 [self setupAnswerView]; 115 116 答案的个数是不同的, 需要根据每一题的答案长度来确定 117 接收的参数: 答案的长度 118 修改后: 119 [self setupAnswerViewWithLenght:length]; 120 */ 121 122 // 获取答案 123 NSString *answer = guessModel.answer; 124 125 // 答案长度 126 NSInteger length = answer.length; 127 128 [self setupAnswerViewWithLenght:length]; 129 130 /** 131 选项区域 3行 7 列 , 21 个按钮 132 按钮的个数不会发生变化, 变化的知识按钮上的文字 133 134 初始设置: 135 [self setupOptionView]; 136 137 修改: 138 选项区域中的button上需要设置文本 139 传递参数: model中 options 数组, 存放button上需要的文字 140 */ 141 [self setupOptionViewWithOptions:guessModel.options]; 142 } 143 144 #pragma mark - 答案区域设置 145 - (void)setupAnswerViewWithLenght:(NSInteger)count { 146 // 表示答案的长度, button的个数 147 // int count = 5; 148 // (屏幕的宽度 - button的总宽度 - margin的总宽度)/ 2; 149 CGFloat startX = (kScreenSize.width - count * kButtonWidht - (count - 1) * kMargin)/2; 150 151 // 添加本题的按钮的时候, 要把上一题的按钮给移除掉 152 // for (UIView *view in _answerView.subviews) { 153 // [view removeFromSuperview]; 154 // } 155 /** 156 让数组中所有的元素都执行 removeFromSuperview 157 */ 158 [_answerview.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 159 160 for (int i = 0; i < count; i++) { 161 // 计算button的x值 162 CGFloat buttonX = i * kButtonWidht + i * kMargin + startX; 163 164 // 实例化button 165 UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(buttonX, 0, kButtonWidht, kButtonWidht)]; 166 // 设置button的背景图片 167 [button setBackgroundImage:[UIImage imageNamed:@"Btn-option45"] forState:UIControlStateNormal]; 168 [button setBackgroundImage:[UIImage imageNamed:@"btn_answer_highlighted"] forState:UIControlStateHighlighted]; 169 // 设置文本颜色 170 [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 171 // 添加点击事件 172 [button addTarget:self 173 action:@selector(didClickAnswerButton:) 174 forControlEvents:UIControlEventTouchUpInside]; 175 // 添加到 answerView上 176 [_answerview addSubview:button]; 177 } 178 } 179 180 #pragma mark - 选项区域 181 - (void)setupOptionViewWithOptions:(NSArray *)options { 182 int column = 7; // 确定列数 183 NSInteger count = options.count; // 21个按钮 184 // 计算margin 185 CGFloat margin = (kScreenSize.width - column * kButtonWidht) / (column + 1); 186 // 把button给移除掉 187 [_optionView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 188 // 循环添加button 189 for (int i = 0; i < count; i++) { 190 // 得到行索引和列索引 191 NSInteger rowIndex = i / column; 192 NSInteger columnIndex = i % column; 193 // 计算x, y 194 CGFloat buttonX = (columnIndex + 1) * margin + columnIndex * kButtonWidht; 195 CGFloat buttonY = rowIndex * margin + rowIndex * kButtonWidht; 196 // 实例化按钮 197 UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(buttonX, buttonY, kButtonWidht, kButtonWidht)]; 198 // 设置背景图片 199 [button setBackgroundImage:[UIImage imageNamed:@"Btn-option45"] forState:UIControlStateNormal]; 200 [button setBackgroundImage:[UIImage imageNamed:@"btn_option_highlighted"] forState:UIControlStateHighlighted]; 201 // 设置按钮的文字 202 [button setTitle:options[i] forState:UIControlStateNormal]; 203 // 修改文本的颜色 204 [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 205 // 添加按钮的点击事件 206 [button addTarget:self 207 action:@selector(didClickOptionButton:) 208 forControlEvents:UIControlEventTouchUpInside]; 209 // 添加按钮到optionView 210 [_optionView addSubview:button]; 211 } 212 } 213 214 #pragma mark - 点击答案区域的按钮 215 - (void)didClickAnswerButton:(UIButton *)answerButton { 216 // 0. 对文本长度做判断 217 if (answerButton.currentTitle.length == 0) { 218 return; 219 } 220 /** 221 0. 如果被点击按钮没有文本, 就直接返回, 不必再执行后面的代码 222 1. 取出被点击按钮的文本 223 2. 清空被点击按钮的文本 224 3. 在选项区域中 对应到文本的按钮 显示出来 225 4. 如果用户输入错误导致字体变红, 当用户点击答案区域的按钮的时候, 就表示用户还没有输入完成 226 5. 打开选项区域的用户交互功能(如果用户完成输入, 会把选项区域给禁用) 227 */ 228 229 // 1. 取出文本 230 NSString *title = answerButton.currentTitle; 231 232 // 2.清空被点击按钮的文本 233 [answerButton setTitle:@"" forState:UIControlStateNormal]; 234 235 // 3. 在选项区域中, 对应到文本的按钮, 显示出来 236 [_optionView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 237 UIButton *optionButton = obj; 238 239 // 取出button文本和 title进行比较, 如果相同, 就显示出来 240 if ([optionButton.currentTitle isEqualToString:title]) { 241 optionButton.hidden = NO; 242 243 *stop = YES; 244 } 245 }]; 246 247 // 4. 把按钮的红色改为黑色 248 [_answerview.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 249 UIButton *answerButton = obj; 250 251 [answerButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 252 }]; 253 254 // 5. 打开选项区域的用户交互 255 _optionView.userInteractionEnabled = YES; 256 } 257 258 #pragma mark - 点击选项区域的按钮 259 - (void)didClickOptionButton:(UIButton *)optionButton { 260 /** 261 取button的title 一定要分状态 262 [optionButton titleForState:UIControlStateNormal]; 263 264 optionButton.currentTitle 265 266 1. 把被点击按钮的文字取出来 267 2. 隐藏被点击按钮 268 3. 被点击按钮的文字出现在 答案区域的按钮上 269 4. 判断用户是否输入完成 270 5. 判断用户是否输入正确 271 */ 272 273 // 1. 取出文字 274 NSString *title = optionButton.currentTitle; 275 276 // 2. 隐藏被点击按钮 277 optionButton.hidden = YES; 278 279 // 3. 被点击按钮的文字 显示到 答案区域 280 // obj --> 数组中的对象 281 // idx --> 下标 282 // *stop --> 如果设置为 yes 的化,会立即跳出遍历 283 [_answerview.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 284 285 UIButton *answerButton = obj; 286 if(answerButton.currentTitle.length==0) 287 { 288 [answerButton setTitle:title forState:UIControlStateNormal]; 289 *stop = YES; 290 } 291 }]; 292 // 4. 判断用户是否输入完成 293 // isComplete = YES 表示, 用户输入完成 294 __block BOOL isComplete = YES; 295 [_answerview.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 296 // 类型转换 297 UIButton *answerButton = obj; 298 if (answerButton.currentTitle.length == 0) { 299 // 该按钮还没有被设置文本 300 isComplete = NO; 301 *stop = YES; 302 } 303 }]; 304 if (isComplete) { // 表示用户输入完成 305 // 用户不能再点击 选项区域中的按钮 306 // userInteractionEnabled = NO 禁止任何用户交互, 如果是父view设置了这个属性为NO, 那么它的子view也将不会接受用户交互 307 _optionView.userInteractionEnabled = NO; 308 /** 309 5. 判断用户是否输入正确 310 5.1 取出用户输入的答案 311 5.2 取出当前题目的正确答案 312 5.3 进行比较 313 */ 314 315 // 5.1 取出用户输入的答案 316 317 // 定义一个可变字符串 318 NSMutableString *userAnswer = [NSMutableString string]; 319 [_answerview.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 320 UIButton *answerButton = obj; 321 // 拼接每一个button上的文本 322 [userAnswer appendString:answerButton.currentTitle]; 323 }]; 324 325 // 5.2 取出当前题目的正确答案 326 MUModel *guessModel = self.dataArray[_index - 1]; 327 NSString *rightAnswer = guessModel.answer; 328 329 // 5.3.2.1 取出当前的金币数量 330 NSInteger currentCoin = _coinButton.currentTitle.integerValue; 331 // 5.3 进行比较 332 if ([userAnswer isEqualToString:rightAnswer]) { 333 /** 334 用户输入正确 335 336 1. 自动的跳入下一题 337 2. 增加100 金币 338 */ 339 /******** 答案正确播放音效 ********/ 340 //1.获取音效文件 341 NSString *path = [[NSBundle mainBundle]pathForResource:@"你真棒.mp3" ofType:nil]; 342 NSURL *url = [NSURL fileURLWithPath:path]; 343 SystemSoundID soundID = 0; 344 AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url), &soundID); 345 AudioServicesPlaySystemSound(soundID); 346 347 //播放提示 348 // 实例化UIAlertController , ios7 UIAlertView 349 UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"答对了!" message:@"你真厉害(ง •̀_•́)ง" preferredStyle:UIAlertControllerStyleAlert]; 350 // 展示alert 351 [self presentViewController:alertController animated:YES completion:nil]; 352 [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:@selector(creatAlert:) userInfo:alertController repeats:NO]; 353 354 355 356 // 5.3.1 自动跳入下一题 357 [self performSelector:@selector(didClickNextButton:) withObject:nil afterDelay:1]; 358 359 /** 360 5.3.2 增加100金币 361 1. 取出当前的金币数量 362 2. 更新(加/减) 363 3. 赋值回去 364 */ 365 // 5.3.2.1 增加100金币 366 currentCoin += 100; 367 368 } else { 369 /** 370 用户输入不正确 371 372 1. 答案区域文本的字体变红 373 2. 减少1000金币 374 */ 375 376 // 5.3.1 当用户输入错误, 答案区域的文字变为红色 377 [_answerview.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 378 UIButton *answerButton = obj; 379 380 [answerButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; 381 382 }]; 383 384 385 // 5.3.2.2 减少1000金币 386 currentCoin -= 1000; 387 } 388 389 // 5.3.2.3 把修改过的金币数量赋值给 _coinButotn 390 NSString *coinString = [NSString stringWithFormat:@"%ld",currentCoin]; 391 392 [_coinButton setTitle:coinString forState:UIControlStateNormal]; 393 } 394 } 395 396 //UIAlertView计时器 397 -(void)creatAlert:(NSTimer * )timer{ 398 UIAlertController *alertController = [timer userInfo]; 399 [alertController dismissViewControllerAnimated:YES completion:nil]; 400 alertController = nil; 401 } 402 403 #pragma mark - 点击提示按钮 404 - (IBAction)didClickTipButton:(id)sender { 405 406 /** 407 1. 减去1000金币 408 2. 取出正确答案的第一个字 409 3. 在答案区域的第一个button上显示 第一个字, 其他按钮文本清空 410 4. 选项区域中, 只有第一字对应的button隐藏, 其他的显示出来 411 5. 答案区域的文本要变为黑色 412 6. 允许选项区域用户交互 413 7. 对用户的金币数量做判断, 如果不够减就提示用户 414 */ 415 416 // 1. 减去1000金币 417 // 1.1 取出当前的金币数量 418 NSInteger currentCoin = _coinButton.currentTitle.integerValue; 419 420 // 1.2 减去1000 421 currentCoin -= 100; 422 // 1.3 赋值回去 423 NSString *coinString = [NSString stringWithFormat:@"%ld", currentCoin]; 424 [_coinButton setTitle:coinString forState:UIControlStateNormal]; 425 426 // 2. 取出正确答案的第一个字 427 // 2.1 先取出正确答案 428 MUModel *guessModel = self.dataArray[_index - 1]; 429 NSString *rightAnswer = guessModel.answer; 430 431 // 2.2 取第一个字 432 NSString *firstString = [rightAnswer substringToIndex:1]; 433 434 // 3. 答案区域第一个按钮显示 firstString , 其他按钮的文本清空掉 435 [_answerview.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 436 UIButton *answerButton = obj; 437 438 if (idx == 0) { // 第一个button , 设置正确答案的第一个字 439 [answerButton setTitle:firstString forState:UIControlStateNormal]; 440 } else { // 如果不是第一个按钮, 就直接清空文本 441 [answerButton setTitle:@"" forState:UIControlStateNormal]; 442 } 443 444 }]; 445 446 // 4. 选项区域中和第一个字对应的button隐藏, 其他的全部显示 447 [_optionView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 448 UIButton *optionButton = obj; 449 450 if ([optionButton.currentTitle isEqualToString:firstString]) { 451 // 如果字符串相等, 就把button隐藏 452 optionButton.hidden = YES; 453 } else { 454 // 不等的话就显示出来 455 optionButton.hidden = NO; 456 } 457 }]; 458 459 // 5. 修改答案区域文本颜色为黑色 460 [_answerview.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 461 UIButton *answerButton = obj; 462 463 [answerButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 464 }]; 465 466 // 6. 允许选项区域的用户交互 467 _optionView.userInteractionEnabled = YES; 468 } 469 #pragma mark - 470 #pragma mark - 点击"下一题"按钮 471 - (IBAction)didClickNextButton:(id)sender { 472 473 // 0. 是否是最后一道题进行判断 474 if (_index == self.dataArray.count) { 475 476 return; 477 } 478 479 /** 480 0. 先进行判断是否是最后一道题, 如果是, 就直接返回 (防止最后一道题回答正确之后,导致的崩溃) 481 1. 让index + 1 482 2. 切换界面数据 483 3. 到最后一题的时候, 按钮禁用 484 4. 如果用户输入完成, optionView的用户交互功能会被禁用, 在这里要打开 485 */ 486 487 // 1. 让index + 1 488 _index++; 489 490 // 2. 切换数据 491 [self setupUI]; 492 493 // 3. 到最后一题的时候禁用按钮 494 // 按钮被禁用将不再响应任何用户交互(点击按钮之后不会再调用相应的方法) 495 _nextButton.enabled = (_index != self.dataArray.count); 496 497 // 4. 开启optionView的用户交互功能 498 _optionView.userInteractionEnabled = YES; 499 } 500 @end
最后一个情景训练,框架跟上一个训练是一样的,只不过把图片改成了播放情景视频,给出训练题目,观看视频,然后回答问题。
具体代码实现:
1 // 2 // VideoViewController.m 3 // 情绪识别训练 4 // 5 // Created by lpx on 16/4/11. 6 // Copyright (c) 2016年 lpx. All rights reserved. 7 // 8 9 #import "VideoViewController.h" 10 #import <MediaPlayer/MediaPlayer.h> 11 #import "MUModel.h" 12 #import <AVFoundation/AVFoundation.h> 13 14 // 屏幕的size 15 #define kScreenSize ([UIScreen mainScreen].bounds.size) 16 // 定义 答案区域按钮的宽高 17 #define kButtonWidht 60 18 19 // 定义 答案区域按钮之间的间距 20 #define kMargin 10 21 22 @interface VideoViewController () 23 @property (weak, nonatomic) IBOutlet UILabel *indexLabel; 24 @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 25 @property (weak, nonatomic) IBOutlet UIButton *imagebutton; 26 @property (weak, nonatomic) IBOutlet UIView *optionView; 27 @property (weak, nonatomic) IBOutlet UIButton *nextButton; 28 @property (weak, nonatomic) IBOutlet UIButton *previousBtn; 29 @property (nonatomic, strong) MPMoviePlayerController *vc; 30 @property (nonatomic, strong) NSArray *dataArray; 31 // 表示第几题 32 @property (nonatomic, assign) NSInteger index; 33 @end 34 35 @implementation VideoViewController 36 37 - (void)viewDidLoad { 38 [super viewDidLoad]; 39 self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Bg-02.png"]]; 40 41 // 初始化index = 1 42 _index = 1; 43 44 [self setupUI]; 45 } 46 47 - (IBAction)returnBtn:(id)sender { 48 [self dismissModalViewControllerAnimated:YES]; 49 50 } 51 52 #pragma mark - 懒加载数据 53 - (NSArray *)dataArray { 54 if (nil == _dataArray) { 55 56 // 1. 读取文件路径 57 NSString *path = [[NSBundle mainBundle] pathForResource:@"MovieQuestion.plist" ofType:nil]; 58 59 // 2. 读取内容到临时数组 60 NSArray *tempArray = [NSArray arrayWithContentsOfFile:path]; 61 62 // 3. 创建一个可变数组 63 NSMutableArray *mutableArray = [NSMutableArray array]; 64 65 // 4. 遍历临时数组中的字典转为模型 , 存放到可变数组 66 for (NSDictionary *dict in tempArray) { 67 MUModel *guessModel = [MUModel guessModelDict:dict]; 68 69 [mutableArray addObject:guessModel]; 70 } 71 72 // 5. 把可变数组赋值给 _dataArray 73 _dataArray = mutableArray; 74 75 } 76 return _dataArray; 77 } 78 79 #pragma mark - 设置UI界面 80 - (void)setupUI { 81 82 // 给控件设置数据 83 // 取出_index - 1 在 数组中对应的数据 84 MUModel *guessModel = self.dataArray[_index - 1]; 85 86 // 索引label 87 _indexLabel.text = [NSString stringWithFormat:@"%ld/%ld",(long)_index, (unsigned long)self.dataArray.count]; 88 89 // titleLabel 90 _titleLabel.text = guessModel.title; 91 92 //设置 imageButton 93 // 取出图片名称 94 NSString *imageName = guessModel.icon; 95 // // 实例化一个image对象 96 UIImage *image = [UIImage imageNamed:imageName]; 97 // 98 [_imagebutton setImage:image forState:UIControlStateNormal]; 99 // 100 101 //换行 102 _titleLabel.lineBreakMode = UILineBreakModeWordWrap; 103 _titleLabel.numberOfLines = 0; 104 105 /** 106 107 按钮的个数不会发生变化, 变化的知识按钮上的文字 108 初始设置: 109 [self setupOptionView]; 110 111 修改: 112 选项区域中的button上需要设置文本 113 传递参数: model中 options 数组, 存放button上需要的文字 114 */ 115 [self setupOptionViewWithOptions:guessModel.options]; 116 117 _optionView.userInteractionEnabled = NO; 118 } 119 120 #pragma mark - 选项区域 121 - (void)setupOptionViewWithOptions:(NSArray *)options { 122 // 确定列数 123 int column = 4; 124 125 NSInteger count = options.count; 126 127 // 计算margin 128 CGFloat margin = (kScreenSize.width - column * kButtonWidht) / (column + 1); 129 130 // 把button给移除掉 131 132 [_optionView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 133 134 // 循环添加button 135 for (int i = 0; i < count; i++) { 136 // 得到行索引和列索引 137 NSInteger rowIndex = i / column; 138 NSInteger columnIndex = i % column; 139 140 // 计算x, y 141 CGFloat buttonX = (columnIndex + 1) * margin + columnIndex * kButtonWidht; 142 CGFloat buttonY = rowIndex * margin + rowIndex * kButtonWidht; 143 144 // 实例化按钮 145 UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(buttonX, buttonY, kButtonWidht, kButtonWidht)]; 146 147 // 设置背景图片 148 [button setBackgroundImage:[UIImage imageNamed:@"Btn-answer57"] forState:UIControlStateNormal]; 149 [button setBackgroundImage:[UIImage imageNamed:@"btn_option_highlighted"] forState:UIControlStateHighlighted]; 150 151 // 设置按钮的文字 152 [button setTitle:options[i] forState:UIControlStateNormal]; 153 154 // 修改文本的颜色 155 [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 156 157 // 添加按钮的点击事件 158 [button addTarget:self 159 action:@selector(didClickOptionButton:) 160 forControlEvents:UIControlEventTouchUpInside]; 161 162 // 添加按钮到optionView 163 [_optionView addSubview:button]; 164 } 165 } 166 167 #pragma mark - 点击选项区域的按钮 168 - (void)didClickOptionButton:(UIButton *)optionButton { 169 170 // 1. 取出文字 171 NSString *title = optionButton.currentTitle; 172 // 5.2 取出当前题目的正确答案 173 MUModel *guessModel = self.dataArray[_index - 1]; 174 175 NSString *rightAnswer = guessModel.answer; 176 177 //如果回答正确 178 if (title==rightAnswer) { 179 180 NSString *path = [[NSBundle mainBundle]pathForResource:@"你真棒.mp3" ofType:nil]; 181 NSURL *url = [NSURL fileURLWithPath:path]; 182 SystemSoundID soundID = 0; 183 AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url), &soundID); 184 AudioServicesPlaySystemSound(soundID); 185 186 //播放提示 187 188 // 1. 实例化UIAlertController , ios7 UIAlertView 189 UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"答对了!" message:@"你真厉害(ง •̀_•́)ง" preferredStyle:UIAlertControllerStyleAlert]; 190 [self presentViewController:alertController animated:YES completion:nil]; 191 [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:@selector(creatAlert:) userInfo:alertController repeats:NO]; 192 193 // 自动跳入下一题 194 [self performSelector:@selector(nextBtn:) withObject:nil afterDelay:1]; 195 196 } 197 } 198 199 //计时器 200 -(void)creatAlert:(NSTimer * )timer{ 201 UIAlertController *alertController = [timer userInfo]; 202 [alertController dismissViewControllerAnimated:YES completion:nil]; 203 alertController = nil; 204 } 205 //播放视频 206 - (IBAction)play:(id)sender { 207 MUModel *guessModel = self.dataArray[_index - 1]; 208 NSString *movieName = guessModel.moviename; 209 //创建播放器 210 NSString *path = [[NSBundle mainBundle]pathForResource:movieName ofType:nil]; 211 MPMoviePlayerController *vc = [[MPMoviePlayerController alloc]initWithContentURL:[NSURL fileURLWithPath:path]]; 212 vc.view.frame = self.view.bounds; 213 [self.view addSubview:vc.view]; 214 self.vc = vc; 215 //播放 216 [self.vc play]; 217 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myMovieFinishedCallback:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; 218 } 219 //播放完成后销毁视频控制器 220 -(void)myMovieFinishedCallback:(NSNotification*)notify 221 { 222 //视频播放对象 223 MPMoviePlayerController* vc = [notify object]; 224 //销毁播放通知 225 [[NSNotificationCenter defaultCenter] removeObserver:self 226 name:MPMoviePlayerPlaybackDidFinishNotification 227 object:vc]; 228 [vc.view removeFromSuperview]; 229 230 _optionView.userInteractionEnabled = YES; 231 232 } 233 234 - (IBAction)nextBtn:(id)sender { 235 236 _previousBtn.enabled=YES; 237 // 0. 是否是最后一道题进行判断 238 if (_index == self.dataArray.count) { 239 return; 240 } 241 242 /** 243 0. 先进行判断是否是最后一道题, 如果是, 就直接返回 (防止最后一道题回答正确之后,导致的崩溃) 244 1. 让index + 1 245 2. 切换界面数据 246 3. 到最后一题的时候, 按钮禁用 247 4. 如果用户输入完成, optionView的用户交互功能会被禁用, 在这里要打开 248 */ 249 250 // 1. 让index + 1 251 _index++; 252 253 // 2. 切换数据 254 [self setupUI]; 255 256 // 3. 到最后一题的时候禁用按钮 257 // 按钮被禁用将不再响应任何用户交互(点击按钮之后不会再调用相应的方法) 258 _nextButton.enabled = (_index != self.dataArray.count); 259 260 // 4. 开启optionView的用户交互功能 261 262 } 263 264 - (IBAction)previousBtn:(id)sender { 265 _nextButton.enabled=YES; 266 if (_index<0) { 267 return; 268 } 269 _index=_index-2; 270 271 if (_index ==1 ) { 272 _previousBtn.enabled=NO; 273 } 274 275 [self setupUI]; 276 } 277 @end