iOS自定义控件
这里做一个类似于下面界面的小案例
1.创建一个空的布局文件 .xib
1 new File -->User Interface -->选择View 2 创建一个空的view ,会自动生成一个 .xib的文件
2.设置我们自己需要经常复用的界面
注意:记得设置"Custom Class"中的 Class属性 与我们的代码文件 .h .m (Cocoa Touch Class文件 )相关联
3.创建我们的Cocoa文件(Cocoa Touch Class文件)
继承 <UIKit/UIKit.h> 包种的 UIView类
.h文件
1 #import <UIKit/UIKit.h> 2 @class CZApp; 3 4 5 @interface CZAppView : UIView 6 7 @property (nonatomic,strong) CZApp *model; 8 9 //为自定义view封装一个类方法,这个类方法的作用就是创建一个view对象 10 +(instancetype) appView; 11 12 @end
.m文件
1 #import "CZAppView.h" 2 #import "CZApp.h" 3 @interface CZAppView() 4 @property (weak, nonatomic) IBOutlet UIImageView *imgViewIcon; 5 @property (weak, nonatomic) IBOutlet UILabel *lblName; 6 @property (weak, nonatomic) IBOutlet UIButton *btnDownload; 7 - (IBAction)btnDownloadClick:(UIButton *)sender; 8 9 @end 10 @implementation CZAppView 11 +(instancetype) appView{ 12 //1.通过xib创建每一个应用(UIView) 13 //通过动态加载xib文件创建里面的view 14 //1.1>找到应用的根目录 15 NSBundle *rootBundle =[NSBundle mainBundle];//NSLog(@"%@",[mainBundle bundlePath]); 16 //1.2>在应用程序根目录下取搜索对应的Xib(nib)文件 17 return [[rootBundle loadNibNamed:@"CZAppView" owner:nil options:nil]lastObject]; 18 } 19 //重写model属性的set方法 20 -(void)setModel:(CZApp *)model{ 21 //先赋值 22 _model = model; 23 24 //解析模型数据,把模型数据赋值给UIView中的各个子控件 25 self.imgViewIcon.image = [UIImage imageNamed:model.icon]; 26 self.lblName.text=model.name; 27 } 28 //下载按钮的单击事件 29 - (IBAction)btnDownloadClick:(UIButton *)sender { 30 //1.禁用当前被点击的按钮 31 sender.enabled = NO; 32 33 //2.弹出一个消息提醒框(这个消息提醒框其实就是一个UILable) 34 UILabel *lblMsg = [[UILabel alloc]init]; 35 //2.1 设置lblMsg的显示文字 36 lblMsg.text=@"正在下载...."; 37 //2.2 设置lblMsg的背景色 38 lblMsg.backgroundColor = [UIColor blackColor]; 39 //2.3设置lblMsg的frame 40 CGFloat viewW = self.superview.frame.size.width; 41 CGFloat viewH = self.superview.frame.size.height; 42 CGFloat msgW = 200; 43 CGFloat msgH = 30; 44 CGFloat msgX= (viewW -msgW)/2; 45 CGFloat msgY = (viewH - msgH)*0.5; 46 lblMsg.frame=CGRectMake(msgX, msgY, msgW, msgH); 47 //2.4设置label的文字颜色 48 lblMsg.textColor = [UIColor redColor]; 49 //2.5设置label居中显示 50 lblMsg.textAlignment = NSTextAlignmentCenter; 51 //2.6设置文字粗体 52 lblMsg.font = [UIFont boldSystemFontOfSize:17]; 53 //2.7设置label的透明度 54 lblMsg.alpha = 0.0;//一开始把透明度设置为0 ,然后通过动画的方式慢慢的改变透明度 55 //2.8 设置Label为"圆角" 56 //设置四个角的"半径" 57 lblMsg.layer.cornerRadius =10; 58 //把多余的部分裁剪掉 59 lblMsg.layer.masksToBounds =YES; 60 //2.9通过动画的方式来显示Label 61 // [UIView animateWithDuration:2.0 animations:^{ 62 // lblMsg.alpha =0.6; 63 // }]; 64 65 //开启一个动画,这个动画要执行1.5秒 66 [UIView animateWithDuration:1.5 animations:^{ 67 //动画代码:将透明度变为0.6 68 lblMsg.alpha = 0.6; 69 } completion:^(BOOL finished) { 70 if(finished) 71 { 72 //这个代码的含义是,隔一段时间后再启动另外一个动画 73 //这个动画的执行时间是1.5秒,但是这个动画会在1.0秒之后再开始执行 74 //UIViewAnimationOptionCurveLinear表示是均速执行动画 75 [UIView animateWithDuration:1.5 delay:1.0 options:UIViewAnimationOptionCurveLinear animations:^{ 76 //这个动画的代码 77 lblMsg.alpha = 0; 78 } completion:^(BOOL finished) { 79 if(finished){ 80 //当Label的透明度变为0以后,再把这个Label从view中移除 81 [lblMsg removeFromSuperview]; 82 } 83 }]; 84 } 85 }]; 86 //3.把lblMsg加到控制器所管理的那个view上 87 [self.superview addSubview:lblMsg]; 88 89 } 90 @end
这样我们的自定义控件就算完成了,接下来要做得就是引用我们自己所做的界面
4.由于我们加载的图片和名称都是来源 .plist文件
所以我们设置一个"模型" 来导入.plist文件
.h文件
1 #import <Foundation/Foundation.h> 2 3 @interface CZApp : NSObject 4 @property(nonatomic,copy) NSString *name; 5 @property(nonatomic,copy) NSString *icon; 6 7 -(instancetype)initWithDict:(NSDictionary *)dict; 8 +(instancetype)appWithDict:(NSDictionary *)dict; 9 @end
.m文件
1 #import "CZApp.h" 2 3 @implementation CZApp 4 -(instancetype)initWithDict:(NSDictionary *)dict{ 5 if(self=[super init]){ 6 self.name=dict[@"name"]; 7 self.icon=dict[@"icon"]; 8 } 9 return self; 10 } 11 +(instancetype)appWithDict:(NSDictionary *)dict{ 12 return [[self alloc]initWithDict:dict]; 13 } 14 @end
5.接下来我们就在控制器ViewController中开始使用我们自定义的控件
1 #import "ViewController.h" 2 #import "CZApp.h" 3 #import "CZAppView.h" 4 5 @interface ViewController () 6 7 //用来保存所有应用的数据 8 @property(nonatomic,strong)NSArray *apps; 9 10 @end 11 12 @implementation ViewController 13 14 //重写apps属性的get方法,进行懒加载数据 15 -(NSArray *)apps{ 16 if(_apps == nil){ 17 //加载数据 18 //1.获取app.plist文件在手机上的路径 19 NSString *path = [[NSBundle mainBundle]pathForResource:@"app.plist" ofType:nil]; 20 21 //2.根据路径加载数据 22 NSArray *arrayDict = [NSArray arrayWithContentsOfFile:path]; 23 24 //3.创建一个可变数据用来保存一个一个的模型对象 25 NSMutableArray *arrayModels = [NSMutableArray array]; //一个空的可变数组 26 27 //4.循环字典数组,把每个字典对象转换成一个模型对象 28 for(NSDictionary *dict in arrayDict){ 29 //创建一个模型 30 CZApp *model = [CZApp appWithDict:dict]; 31 32 //把模型加到arrayModels中 33 [arrayModels addObject:model]; 34 } 35 _apps = arrayModels; 36 } 37 return _apps; 38 } 39 40 - (void)viewDidLoad { 41 [super viewDidLoad]; 42 43 //假设每行的应用的个数 44 int columns = 3; 45 46 //获取控制器所管理的view的宽度 47 CGFloat viewWidth = self.view.frame.size.width; 48 49 //每个应用的宽和高 50 CGFloat appW = 75; 51 CGFloat appH = 90; 52 CGFloat marginTop =30;//第一行距离顶部的距离 53 CGFloat marginX = (viewWidth - columns *appW)/(columns+1); 54 CGFloat marginY = marginX;//假设每行之间的间距与marginX相等 55 56 for(int i=0;i<self.apps.count;i++){ 57 //获取当前这个应用的数据字典 58 CZApp *appModel = self.apps[i]; 59 60 //创建view 61 CZAppView *appView = [CZAppView appView]; 62 63 //2.2 设置appView 的fram属性 64 //计算每个单元格所在的列的索引 65 int colIdx = i%columns; 66 //计算每个单元格的行索引 67 int rowIdx = i/columns; 68 69 CGFloat appX = marginX + colIdx *(appW +marginX); 70 CGFloat appY = marginTop + rowIdx *(appH + marginY); 71 appView.frame = CGRectMake(appX, appY, appW, appH); 72 73 //3.将appView加到self.view(控制器所管理的那个view) 74 [self.view addSubview:appView]; 75 76 //设置数据 77 //把模型数据设置给"自定义view"的model属性 78 //然后重写model属性的set方法,在set方法中解析模型对象中的属性,并把属性值设置给自定义view的各个子控件 79 appView.model = appModel; 80 81 82 } 83 84 85 } 86 87 - (void)didReceiveMemoryWarning { 88 [super didReceiveMemoryWarning]; 89 // Dispose of any resources that can be recreated. 90 } 91 92 @end
再制作一个自动猜图的小案例
这里主要的是动态生成按钮,以及修改launchScreen的启动界面
效果图:
1.修改启动的launchscreen 以及应用小图标icon
简单来说只需要把我们制作的界面放入Images.xcassets中覆盖原来系统的LaunchImage和AppIcon就可以
主要注意的是关于尺寸的选择:
然后修改项目General中的APP Icons and Launch Image
1.先创建模型用于导入 .plist数据
.h
1 #import <Foundation/Foundation.h> 2 3 @interface CZQuestion : NSObject 4 5 @property (nonatomic, copy) NSString *answer; 6 @property (nonatomic, copy) NSString *icon; 7 @property (nonatomic, copy) NSString *title; 8 @property (nonatomic, strong) NSArray *options; 9 10 11 - (instancetype)initWithDict:(NSDictionary *)dict; 12 + (instancetype)questionWithDict:(NSDictionary *)dict; 13 14 15 @end
.m
1 #import "CZQuestion.h" 2 3 @implementation CZQuestion 4 5 - (instancetype)initWithDict:(NSDictionary *)dict 6 { 7 if (self = [super init]) { 8 self.answer = dict[@"answer"]; 9 self.title = dict[@"title"]; 10 self.icon = dict[@"icon"]; 11 self.options = dict[@"options"]; 12 } 13 return self; 14 } 15 16 + (instancetype)questionWithDict:(NSDictionary *)dict 17 { 18 return [[self alloc]initWithDict:dict]; 19 } 20 @end
2.控制器ViewController.m
1 #import "ViewController.h" 2 #import "CZQuestion.h" 3 4 5 @interface ViewController () <UIAlertViewDelegate> 6 7 // 所有问题的数据都在这个数组中 8 @property (nonatomic, strong) NSArray *questions; 9 10 // 控制题目索引, 类型的int类型属性, 默认没有赋值一开始就是0 11 @property (nonatomic, assign) int index; 12 13 // 记录头像按钮原始的frame 14 @property (nonatomic, assign) CGRect iconFrame; 15 16 17 @property (weak, nonatomic) IBOutlet UILabel *lblIndex; 18 @property (weak, nonatomic) IBOutlet UIButton *btnScore; 19 @property (weak, nonatomic) IBOutlet UILabel *lblTitle; 20 @property (weak, nonatomic) IBOutlet UIButton *btnIcon; 21 @property (weak, nonatomic) IBOutlet UIButton *btnNext; 22 @property (weak, nonatomic) IBOutlet UIView *answerView; 23 @property (weak, nonatomic) IBOutlet UIView *optionsView; 24 25 26 27 // 用来引用那个“阴影”按钮的属性 28 @property (weak, nonatomic) UIButton *cover; 29 30 - (IBAction)btnNextClick; 31 32 - (IBAction)bigImage:(id)sender; 33 34 // 头像按钮的单击事件 35 - (IBAction)btnIconClick:(id)sender; 36 37 // 提示 38 - (IBAction)btnTipClick; 39 40 41 @end 42 43 @implementation ViewController 44 45 46 // 懒加载数据 47 - (NSArray *)questions 48 { 49 if (_questions == nil) { 50 // 加载数据 51 NSString *path = [[NSBundle mainBundle] pathForResource:@"questions.plist" ofType:nil]; 52 NSArray *arrayDict = [NSArray arrayWithContentsOfFile:path]; 53 NSMutableArray *arrayModel = [NSMutableArray array]; 54 55 // 遍历把字典转模型 56 for (NSDictionary *dict in arrayDict) { 57 CZQuestion *model = [CZQuestion questionWithDict:dict]; 58 [arrayModel addObject:model]; 59 } 60 _questions = arrayModel; 61 } 62 return _questions; 63 } 64 65 66 // 改变状态栏的文字颜色为白色 67 - (UIStatusBarStyle)preferredStatusBarStyle 68 { 69 return UIStatusBarStyleLightContent; 70 } 71 72 // 隐藏状态栏 73 - (BOOL)prefersStatusBarHidden 74 { 75 return YES; 76 } 77 78 79 - (void)viewDidLoad { 80 [super viewDidLoad]; 81 82 83 // 初始化显示第一题 84 self.index = -1; 85 [self nextQuestion]; 86 87 } 88 89 - (void)didReceiveMemoryWarning { 90 [super didReceiveMemoryWarning]; 91 // Dispose of any resources that can be recreated. 92 } 93 94 // 点击下一题 95 - (IBAction)btnNextClick { 96 97 // 移动到下一题 98 [self nextQuestion]; 99 } 100 101 // 显示大图 102 - (IBAction)bigImage:(id)sender { 103 104 // 记录一下头像按钮的原始frame 105 self.iconFrame = self.btnIcon.frame; 106 107 // 1.创建大小与self.view一样的按钮, 把这个按钮作为一个"阴影" 108 UIButton *btnCover = [[UIButton alloc] init]; 109 // 设置按钮大小 110 btnCover.frame = self.view.bounds; 111 // 设置按钮背景色 112 btnCover.backgroundColor = [UIColor blackColor]; 113 // 设置按钮透明度 114 btnCover.alpha = 0.0; 115 116 // 把按钮加到self.view中 117 [self.view addSubview:btnCover]; 118 119 // 为阴影按钮注册一个单击事件 120 [btnCover addTarget:self action:@selector(samllImage) forControlEvents:UIControlEventTouchUpInside]; 121 122 123 // 2. 把图片设置到阴影的上面 124 // 把self.view中的所有子控件中, 只把self.btnIcon显示到最上层 125 [self.view bringSubviewToFront:self.btnIcon]; 126 127 // 通过self.cover来引用btnCover 128 self.cover = btnCover; 129 130 131 // 3. 通过动画的方式把图片变大 132 CGFloat iconW = self.view.frame.size.width; 133 CGFloat iconH = iconW; 134 CGFloat iconX = 0; 135 CGFloat iconY = (self.view.frame.size.height - iconH) * 0.5; 136 137 [UIView animateWithDuration:0.7 animations:^{ 138 // 设置按钮透明度 139 btnCover.alpha = 0.6; 140 141 // 设置图片的新的frame 142 self.btnIcon.frame = CGRectMake(iconX, iconY, iconW, iconH); 143 }]; 144 } 145 146 // 头像按钮的单击事件 147 - (IBAction)btnIconClick:(id)sender { 148 if (self.cover == nil) { 149 // 显示大图 150 [self bigImage:nil]; 151 } else { 152 [self samllImage]; 153 } 154 } 155 156 // 点击"提示"按钮 157 - (IBAction)btnTipClick { 158 // 1. 分数-1000 159 [self addScore:-1000]; 160 161 162 // 2. 把所有的答案按钮"清空"(其实这里的"清空"最好是调用每个答案按钮的单击事件) 163 for (UIButton *btnAnswer in self.answerView.subviews) { 164 // 让每个答案按钮点击一下 165 [self btnAnswerClick:btnAnswer]; 166 } 167 168 // 3. 根据当前的索引, 从数据数组中(self.questions)中找到对应的数据模型 169 // 从数据模型中获取正确答案的第一个字符, 把待选按钮中和这个字符相等的那个按钮点击一下 170 CZQuestion *model = self.questions[self.index]; 171 //截取正确答案中的第一个字符"字符串" 172 NSString *firstChar = [model.answer substringToIndex:1]; 173 174 // 根据firstChar在option按钮中找到对应的option 按钮, 让这个按钮点击一下 175 for (UIButton *btnOpt in self.optionsView.subviews) { 176 if ([btnOpt.currentTitle isEqualToString:firstChar]) { 177 [self optionButtonClick:btnOpt]; // 设置某个option 按钮点击一下 178 break; 179 } 180 } 181 } 182 183 184 // "阴影"的单击事件 185 - (void)samllImage 186 { 187 [UIView animateWithDuration:0.7 animations:^{ 188 // 1. 设置btnIcon(头像)按钮的frame还原 189 self.btnIcon.frame = self.iconFrame; 190 // 2. 让"阴影"按钮的透明度变成0 191 self.cover.alpha = 0.0; 192 } completion:^(BOOL finished) { 193 if (finished) { 194 // 移出"阴影"按钮 195 [self.cover removeFromSuperview]; 196 // 当头像图片变成小图以后, 再把self.cover设置成nil 197 self.cover = nil; 198 } 199 }]; 200 } 201 202 203 // 实现UIAlertView的代理方法 204 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex 205 { 206 // NSLog(@"%ld", buttonIndex); 207 if (buttonIndex == 0) { 208 // 让程序在回到第0个问题 209 self.index = -1; 210 [self nextQuestion]; 211 } 212 } 213 214 215 216 // 下一题 217 - (void)nextQuestion 218 { 219 // 1. 让索引++ 220 self.index++; 221 222 // 判断当前索引是否越界, 入股索引越界, 则提示用户 223 if (self.index == self.questions.count) { 224 //NSLog(@"答题完毕!!!!"); 225 226 // 弹出一个对话框 227 UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"操作提示" message:@"恭喜通关!" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil]; 228 229 // 显示对话框 230 [alertView show]; 231 return; 232 } 233 234 235 // 2. 根据索引获取当前的模型数据 236 CZQuestion *model = self.questions[self.index]; 237 238 239 240 // 3. 根据模型设置数据 241 [self settingData:model]; 242 243 244 // 4. 动态创建"答案按钮" 245 [self makeAnswerButtons:model]; 246 247 248 // 5. 动态创建"待选按钮" 249 [self makeOptionsButton:model]; 250 251 252 } 253 254 255 // 创建待选按钮 256 - (void)makeOptionsButton:(CZQuestion *)model 257 { 258 // 0. 设置option view 可以与用户交互 259 self.optionsView.userInteractionEnabled = YES; 260 261 262 // 1. 清除待选按钮的view中的所有子控件 263 [self.optionsView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 264 265 // 2. 获取当前题目的待选文字的数组 266 NSArray *words = model.options; 267 268 // 3. 根据待选文字循环来创建按钮 269 270 // 指定每个待选按钮的大小 271 CGFloat optionW = 35; 272 CGFloat optionH = 35; 273 // 指定每个按钮之间的间距 274 CGFloat margin = 10; 275 // 指定每行有多少个按钮 276 int columns = 7; 277 // 计算出每行第一个按钮距离左边的距离 278 CGFloat marginLeft = (self.optionsView.frame.size.width - columns * optionW - (columns - 1) * margin) / 2; 279 280 281 for (int i = 0; i < words.count; i++) { 282 // 创建一个按钮 283 UIButton *btnOpt = [[UIButton alloc] init]; 284 285 // 给每个option 按钮一个唯一的tag值 286 btnOpt.tag = i; 287 288 // 设置按钮背景 289 [btnOpt setBackgroundImage:[UIImage imageNamed:@"btn_option"] forState:UIControlStateNormal]; 290 [btnOpt setBackgroundImage:[UIImage imageNamed:@"btn_option_highlighted"] forState:UIControlStateHighlighted]; 291 292 // 设置按钮文字 293 [btnOpt setTitle:words[i] forState:UIControlStateNormal]; 294 295 // 设置文字颜色为黑色 296 [btnOpt setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 297 298 299 // 计算当前按钮的列的索引和行的索引 300 int colIdx = i % columns; 301 int rowIdx = i / columns; 302 303 CGFloat optionX = marginLeft + colIdx * (optionW + margin); 304 CGFloat optionY = 0 + rowIdx * (optionH + margin); 305 306 // 设置按钮frame 307 btnOpt.frame = CGRectMake(optionX, optionY, optionW, optionH); 308 309 // 把按钮添加到optionsView中 310 [self.optionsView addSubview:btnOpt]; 311 312 // 为待选按钮注册单击事件 313 [btnOpt addTarget:self action:@selector(optionButtonClick:) forControlEvents:UIControlEventTouchUpInside]; 314 } 315 316 317 } 318 319 // 待选按钮的单击事件 320 - (void)optionButtonClick:(UIButton *)sender 321 { 322 // 1. 隐藏当前被点击的按钮 323 sender.hidden = YES; 324 325 // 2. 把当前被点击的按钮的文字显示到第一个为空的"答案按钮"上 326 //NSString *text = [sender titleForState:UIControlStateNormal]; // 获取按钮指定状态下的文字 327 NSString *text = sender.currentTitle; // 获取按钮当前状态下的文字 328 329 330 // 2.1 把文字显示到答案按钮上 331 332 // 遍历每一个答案按钮 333 for (UIButton *answerBtn in self.answerView.subviews) { 334 // 判断每个"答案按钮"上的文字是否为nil 335 if (answerBtn.currentTitle == nil) { 336 337 // 把当前点击的待选按钮的文字设置给对应的答案按钮 338 [answerBtn setTitle:text forState:UIControlStateNormal]; 339 // 把当前点击的待选按钮的tag值也设置给对应的答案按钮 340 answerBtn.tag = sender.tag; 341 342 break; 343 } 344 } 345 346 347 348 349 350 351 // 3. 判断答案按钮是否已经填满了 352 // 一开始假设答案按钮是填满的 353 BOOL isFull = YES; 354 // 声明一个用来保存用户输入的答案的字符串 355 NSMutableString *userInput = [NSMutableString string]; 356 357 for (UIButton *btnAnswer in self.answerView.subviews) { 358 if (btnAnswer.currentTitle == nil) { 359 isFull = NO; 360 break; 361 } else { 362 // 如果当前答案按钮上面有文字, 那么就把这个文字拼接起来 363 [userInput appendString:btnAnswer.currentTitle]; 364 } 365 } 366 367 // 如果已经填满, 则禁止option view 控件与用户的交互 368 if (isFull) { 369 // 禁止"待选按钮"被点击 370 self.optionsView.userInteractionEnabled = NO; 371 372 // 获取当前题目的正确答案 373 CZQuestion *model = self.questions[self.index]; 374 375 376 // 4. 如果答案按钮被填满了, 那么就判断用户点击输入的答案是否与标准答案一致, 377 if ([model.answer isEqualToString:userInput]) { 378 // 如果一致, 则设置答案按钮的文字颜色为蓝色, 同时在0.5秒之后跳转下一题 379 // 0. 如果正确+100分 380 [self addScore:100]; 381 //1. 设置所有的答案按钮的文字颜色为 蓝色 382 [self setAnswerButtonsTitleColor:[UIColor blueColor]]; 383 384 // 延迟0.5秒后, 跳转到下一题 385 [self performSelector:@selector(nextQuestion) withObject:nil afterDelay:0.5]; 386 387 } else { 388 // 如果答案不一致(答案错误), 设置答案按钮的文字颜色为红色 389 // 设置所有的答案按钮的文字颜色为 红色 390 [self setAnswerButtonsTitleColor:[UIColor redColor]]; 391 } 392 } 393 } 394 395 // 根据指定的分数, 来对界面上的按钮进行加分和减分 396 - (void)addScore:(int)score 397 { 398 // 1. 获取按钮上现在分值 399 NSString *str = self.btnScore.currentTitle; 400 401 // 2. 把这个分值转换成数字类型 402 int currentScore = str.intValue; 403 404 // 3. 对这个分数进行操作 405 currentScore = currentScore + score; 406 407 // 4. 把新的分数设置给按钮 408 [self.btnScore setTitle:[NSString stringWithFormat:@"%d", currentScore] forState:UIControlStateNormal]; 409 } 410 411 412 // 统一设置答案按钮的文字颜色 413 - (void)setAnswerButtonsTitleColor:(UIColor *)color 414 { 415 // 遍历每一个答案按钮, 设置文字颜色 416 for (UIButton *btnAnswer in self.answerView.subviews) { 417 [btnAnswer setTitleColor:color forState:UIControlStateNormal]; 418 } 419 } 420 421 422 423 // 加载数据, 把模型数据设置到界面的控件上 424 - (void)settingData:(CZQuestion *)model 425 { 426 // 3. 把模型数据设置到界面对应的控件上 427 self.lblIndex.text = [NSString stringWithFormat:@"%d / %ld", (self.index + 1), self.questions.count]; 428 self.lblTitle.text = model.title; 429 [self.btnIcon setImage:[UIImage imageNamed:model.icon] forState:UIControlStateNormal]; 430 431 // 4. 设置到达最后一题以后, 禁用"下一题按"钮 432 self.btnNext.enabled = (self.index != self.questions.count - 1); 433 } 434 435 436 // 创建答案按钮 437 - (void)makeAnswerButtons:(CZQuestion *)model 438 { 439 // 这句话的意思:让subviews这个数组中的每个对象, 分别调用一次removeFromSuperview方法, 内部执行了循环,无需我们自己来些循环 440 [self.answerView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 441 442 // 5.1 获取当前答案的文字的个数 443 NSInteger len = model.answer.length; 444 // 设置按钮的frame 445 CGFloat margin = 10; // 假设每个按钮之间的间距都是10 446 CGFloat answerW = 35; 447 CGFloat answerH = 35; 448 CGFloat answerY = 0; 449 CGFloat marginLeft = (self.answerView.frame.size.width - (len * answerW) - (len - 1) * margin) / 2; 450 451 // 5.2 循环创建答案按钮, 有几个文字就创建几个按钮 452 for (int i = 0; i < len; i++) { 453 // 创建按钮 454 UIButton *btnAnswer = [[UIButton alloc] init]; 455 // 设置按钮的背景图 456 [btnAnswer setBackgroundImage:[UIImage imageNamed:@"btn_answer"] forState:UIControlStateNormal]; 457 [btnAnswer setBackgroundImage:[UIImage imageNamed:@"btn_answer_highlighted"] forState:UIControlStateHighlighted]; 458 459 // 计算按钮的x值 460 CGFloat answerX = marginLeft + i * (answerW + margin); 461 btnAnswer.frame = CGRectMake(answerX, answerY, answerW, answerH); 462 463 // 设置答案按钮的文字颜色 464 [btnAnswer setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 465 466 // 把按钮加到answerView中 467 [self.answerView addSubview:btnAnswer]; 468 469 470 // 为答案按钮注册单击事件 471 [btnAnswer addTarget:self action:@selector(btnAnswerClick:) forControlEvents:UIControlEventTouchUpInside]; 472 473 } 474 } 475 476 // 参数sender, 就表示当前点击的答案按钮 477 - (void)btnAnswerClick:(UIButton *)sender 478 { 479 // 0. 启用option view与用户的交互 480 self.optionsView.userInteractionEnabled = YES; 481 482 // 1. 设置所有的答案按钮的文字颜色为黑色 483 [self setAnswerButtonsTitleColor:[UIColor blackColor]]; 484 485 486 // 2. 在"待选按钮"中找到与当前被点击的答案按钮文字相同待选按钮,设置该按钮显示出来。 487 for (UIButton *optBtn in self.optionsView.subviews) { 488 // 比较判断待选按钮的文字是否与当前被点击的答案按钮的文字一致 489 // if ([sender.currentTitle isEqualToString:optBtn.currentTitle]) { 490 // optBtn.hidden = NO; 491 // break; 492 // } 493 494 if (sender.tag == optBtn.tag) { 495 optBtn.hidden = NO; 496 break; 497 } 498 } 499 500 // 1. 清空当前被点击的答案按钮的文字 501 [sender setTitle:nil forState:UIControlStateNormal]; 502 503 504 } 505 @end