標籤:
iOS開發UI篇—從代碼的逐步最佳化看MVC
一、要求
要求完成下面一個小的應用程式。
二、一步步對代碼進行最佳化
注意:在開發過程中,最佳化的過程是一步一步進行的。(如果一個人要吃五個包子才能吃飽,那麼他是否直接吃第五個,前面四個不用吃就飽了?)
1.完成基本要求的代碼(使用了字典轉模型和xib連線)
(1)檔案結構
(2)主要代碼
字典轉模型部分:
YYappInfo.h標頭檔
1 // 2 // YYappInfo.h 3 // 12-視圖改進(1) 4 // 5 // Created by apple on 14-5-25. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h>10 11 @interface YYappInfo : NSObject12 @property(nonatomic,copy)NSString *name;13 @property(nonatomic,copy)NSString *icon;14 @property(nonatomic,strong,readonly)UIImage *img;15 16 -(instancetype)initWithDict:(NSDictionary *)dict;17 /**Factory 方法*/18 +(instancetype)appInfoWithDict:(NSDictionary *)dict;19 @end
YYappInfo.m檔案
1 // 2 // YYappInfo.m 3 // 12-視圖改進(1) 4 // 5 // Created by apple on 14-5-25. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYappInfo.h"10 @interface YYappInfo()11 {12 UIImage *_img;13 }14 @end15 @implementation YYappInfo16 -(instancetype)initWithDict:(NSDictionary *)dict17 {18 if (self=[super init]) {19 self.name=dict[@"name"];20 self.icon=dict[@"icon"];21 }22 return self;23 }24 25 +(instancetype)appInfoWithDict:(NSDictionary *)dict26 {27 return [[self alloc]initWithDict:dict];28 }29 30 -(UIImage *)img31 {32 _img=[UIImage imageNamed:self.icon];33 return _img;34 }35 @end
xib部分(YYappInfoView.h檔案):
註:(xib視圖和YYappInfoView進行了關聯,三個屬性均進行了連線)
1 // 2 // YYappInfoView.h 3 // 12-視圖改進(1) 4 // 5 // Created by apple on 14-5-25. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import <UIKit/UIKit.h>10 11 @interface YYappInfoView : UIView12 @property (strong, nonatomic) IBOutlet UIImageView *appInfoViewimg;13 14 @property (strong ,nonatomic) IBOutlet UILabel *appInfoViewlab;15 @property (strong, nonatomic) IBOutlet UIButton *appInfoViewbtn;16 17 @end
主要功能實現部分:
YYViewController.m檔案
1 // 2 // YYViewController.m 3 // 12-視圖改進(1) 4 // 5 // Created by apple on 14-5-25. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYViewController.h"10 #import "YYappInfo.h"11 #import "YYappInfoView.h"12 13 @interface YYViewController ()14 @property(nonatomic,strong)NSArray *apps;15 @end16 17 //開發思路18 //1.載入plist檔案(字典轉模型提供介面)19 //2.使用xib檔案完成單個的view20 //3.計算座標,使用for迴圈把view展現到介面上21 //4.最佳化代碼22 @implementation YYViewController23 24 //get方法,懶載入25 -(NSArray *)apps26 {27 if (!_apps) {28 NSString *path = [[NSBundle mainBundle]pathForResource:@"app.plist" ofType:nil];29 NSArray * arrayM = [NSArray arrayWithContentsOfFile:path];30 31 NSMutableArray *appinfoarray=[NSMutableArray array];32 for (NSDictionary *dict in arrayM) {33 [appinfoarray addObject:[YYappInfo appInfoWithDict:dict]];34 }35 _apps = appinfoarray;36 }37 return _apps;38 }39 40 - (void)viewDidLoad41 {42 [super viewDidLoad];43 NSLog(@"%d",self.apps.count);44 45 int totalloc = 3;46 CGFloat appviewW = 80;47 CGFloat appviewH = 90;48 CGFloat margin = (self.view.frame.size.width-totalloc*appviewW)/(totalloc+1);49 50 int count=self.apps.count;51 for (int i = 0; i < count; i++) {52 int row = i/totalloc;53 int loc = i%totalloc;54 55 CGFloat appviewX = margin + (margin + appviewW) * loc;56 CGFloat appviewY = margin + (margin + appviewH) * row;57 58 YYappInfo *appinfo=self.apps[i];59 60 //拿出xib中的資料61 NSArray *arryM=[[NSBundle mainBundle]loadNibNamed:@"appInfoxib" owner:nil options:nil];62 YYappInfoView *appinfoview=[arryM firstObject];63 //設定位置64 appinfoview.frame=CGRectMake(appviewX, appviewY, appviewW, appviewH);65 //設定值66 appinfoview.appInfoViewimg.image=appinfo.img;67 appinfoview.appInfoViewlab.text=appinfo.name;68 //添加到視圖69 appinfoview.appInfoViewbtn.tag=i;70 [appinfoview.appInfoViewbtn addTarget:self action:@selector(Click:) forControlEvents:UIControlEventTouchUpInside];71 [self.view addSubview:appinfoview];72 }73 }74 -(void)Click:(UIButton *)btn75 {76 btn.enabled=NO;77 YYappInfo *appinfo=self.apps[btn.tag];78 UILabel *lab=[[UILabel alloc]initWithFrame:CGRectMake(60, 450, 200, 20)];79 [lab setBackgroundColor:[UIColor lightGrayColor]];80 [lab setTextAlignment:NSTextAlignmentCenter];81 [lab setText:[NSString stringWithFormat:@"%@成功下載",appinfo.name]];82 [self.view addSubview:lab];83 84 lab.alpha=1.0;85 [UIView animateWithDuration:2.0 animations:^{86 lab.alpha=0;87 }completion:^(BOOL finished) {88 [lab removeFromSuperview];89 }];90 }91 @end
2.對1進行最佳化(把資料呈現部分封裝到視圖)
說明:在1的基礎上尋找還會有那些可以最佳化的部分
1)改進思路:
(1)1中主檔案的66~67行對控制項屬性的設定能否拿到視圖中進行?
(2)1中61~62行是從xib檔案中讀取資訊的操作,且和主控制器沒有什麼太大的關聯,能否把它也封裝到視圖中進行?
(3)當上述兩個步驟完成後,主檔案69行以後的按鈕操作和按鈕單擊事件就顯得很突兀,放在主控制器中已經不再合適,是否可以把它放到視圖中進行處理
2)按照上述思路最佳化後的代碼如下:
最佳化視圖,在視圖部分之對外提供一個介面,把資料的處理封裝在內部
YYappInfoView.h檔案代碼:
1 // 2 // YYappInfoView.h 3 // 12-視圖改進(1) 4 // 5 // Created by apple on 14-5-25. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import <UIKit/UIKit.h>10 @class YYappInfo;11 @interface YYappInfoView : UIView12 13 //讀取14 //+(instancetype)appInfoView;15 //只對外開放一個資料介面16 +(instancetype)appInfoViewWithappInfo:(YYappInfo *)appinfo;17 @end
YYappInfoView.m檔案代碼
說明:該檔案中的屬性和click等均已做了連線的操作。
1 // 2 // YYappInfoView.m 3 // 12-視圖改進(1) 4 // 5 // Created by apple on 14-5-25. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYappInfoView.h"10 #import "YYappInfo.h"11 //私人擴充,把屬性拿進來12 @interface YYappInfoView ()13 @property (strong, nonatomic) IBOutlet UIImageView *appInfoViewimg;14 @property (strong ,nonatomic) IBOutlet UILabel *appInfoViewlab;15 @property (strong, nonatomic) IBOutlet UIButton *appInfoViewbtn;16 @property(strong,nonatomic)YYappInfo *appinfo;17 18 @end19 @implementation YYappInfoView20 21 +(instancetype)appInfoView22 {23 NSArray *arryM=[[NSBundle mainBundle]loadNibNamed:@"appInfoxib" owner:nil options:nil];24 YYappInfoView *appinfoview=[arryM firstObject];25 return appinfoview;26 }27 28 +(instancetype)appInfoViewWithappInfo:(YYappInfo *)appinfo29 {30 YYappInfoView *appInfoView=[self appInfoView];31 appInfoView.appinfo=appinfo;32 return appInfoView;33 }34 35 -(void)setAppinfo:(YYappInfo *)appinfoc36 {37 //這裡一定要記錄變化38 _appinfo=appinfoc;39 self.appInfoViewimg.image=appinfoc.img;
self.appInfoViewlab.text=appinfoc.name;
41 }
42 - (IBAction)Click {43 44 self.appInfoViewbtn.enabled=NO;45 //YYappInfo *appinfo=self.apps[];46 47 YYappInfo *appinfo=self.appinfo;48 UILabel *lab=[[UILabel alloc]initWithFrame:CGRectMake(60, 450, 200, 20)];49 [lab setBackgroundColor:[UIColor lightGrayColor]];50 [lab setTextAlignment:NSTextAlignmentCenter];51 [lab setText:[NSString stringWithFormat:@"%@成功下載",appinfo.name]];52 //把lab添加到父視圖(即view中)53 [self.superview addSubview:lab];54 55 lab.alpha=1.0;56 [UIView animateWithDuration:2.0 animations:^{57 lab.alpha=0;58 }completion:^(BOOL finished) {59 [lab removeFromSuperview];60 }];61 }62 63 64 @end
最佳化後的主控制器部分
YYViewController.m檔案代碼
1 // 2 // YYViewController.m 3 // 12-視圖改進(1) 4 // 5 // Created by apple on 14-5-25. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYViewController.h"10 #import "YYappInfo.h"11 #import "YYappInfoView.h"12 13 @interface YYViewController ()14 @property(nonatomic,strong)NSArray *apps;15 @end16 @implementation YYViewController17 18 -(NSArray *)apps19 {20 if (!_apps) {21 NSString *path = [[NSBundle mainBundle]pathForResource:@"app.plist" ofType:nil];22 NSArray * arrayM = [NSArray arrayWithContentsOfFile:path];23 24 NSMutableArray *appinfoarray=[NSMutableArray array];25 for (NSDictionary *dict in arrayM) {26 [appinfoarray addObject:[YYappInfo appInfoWithDict:dict]];27 }28 _apps = appinfoarray;29 }30 return _apps;31 }32 33 - (void)viewDidLoad34 {35 [super viewDidLoad];36 NSLog(@"%d",self.apps.count);37 38 int totalloc = 3;39 CGFloat appviewW = 80;40 CGFloat appviewH = 90;41 CGFloat margin = (self.view.frame.size.width-totalloc*appviewW)/(totalloc+1);42 43 int count=self.apps.count;44 for (int i = 0; i < count; i++) {45 int row = i/totalloc;46 int loc = i%totalloc;47 48 CGFloat appviewX = margin + (margin + appviewW) * loc;49 CGFloat appviewY = margin + (margin + appviewH) * row;50 51 /*思路:52 要達到的效果 appinfoview.appinfo=appinfo;53 最佳化後即變成 appinfoview.appinfo=self.apps[i];54 要進行上面代碼的操作,需要在視圖中新增加一個appinfo類的屬性,這樣資料——》視圖的轉換即可以不需要在主控制器中完成,讓程式結構一目瞭然55 */56 YYappInfo *appinfo=self.apps[i];57 YYappInfoView *appinfoview=[YYappInfoView appInfoViewWithappInfo:appinfo];58 //設定位置59 appinfoview.frame=CGRectMake(appviewX, appviewY, appviewW, appviewH);60 //添加61 [self.view addSubview:appinfoview];62 }63 }64 @end
3.對2進一步最佳化(把資料處理部分拿到模型中去進行)
(1)思路:把字典轉模型部分的資料處理操作,拿到模型中去處理,這樣外界不需要再關心資料處理的內部細節。
(2)最佳化後的代碼如下
YYappInfo.h檔案中向外開放一個介面,返回一個處理好的數組。
1 // 2 // YYappInfo.h 3 // 12-視圖改進(1) 4 // 5 // Created by apple on 14-5-25. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h>10 11 @interface YYappInfo : NSObject12 @property(nonatomic,copy)NSString *name;13 @property(nonatomic,copy)NSString *icon;14 @property(nonatomic,strong)UIImage *img;15 16 -(instancetype)initWithDict:(NSDictionary *)dict;17 /**Factory 方法*/18 +(instancetype)appInfoWithDict:(NSDictionary *)dict;19 +(NSArray *)appinfoarray;20 @end
YYappInfo.m檔案中的資料處理
1 // 2 // YYappInfo.m 3 // 12-視圖改進(1) 4 // 5 // Created by apple on 14-5-25. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYappInfo.h"10 @interface YYappInfo()11 @end12 @implementation YYappInfo13 -(instancetype)initWithDict:(NSDictionary *)dict14 {15 if (self=[super init]) {16 self.name=dict[@"name"];17 self.icon=dict[@"icon"];18 }19 return self;20 }21 22 +(instancetype)appInfoWithDict:(NSDictionary *)dict23 {24 return [[self alloc]initWithDict:dict];25 }26 27 -(UIImage *)img28 {29 _img=[UIImage imageNamed:self.icon];30 return _img;31 }32 33 //把資料處理部分拿到模型中來處理34 +(NSArray *)appinfoarray35 {36 NSString *path = [[NSBundle mainBundle]pathForResource:@"app.plist" ofType:nil];37 NSArray * arrayM = [NSArray arrayWithContentsOfFile:path];38 39 NSMutableArray *appinfoarray=[NSMutableArray array];40 for (NSDictionary *dict in arrayM) {41 [appinfoarray addObject:[YYappInfo appInfoWithDict:dict]];42 }43 return appinfoarray;44 }45 @end
主控制器中不再需要關心資料處理的內部細節
YYViewController.m檔案現在只是負責模型和視圖之間的協調工作了,怎麼樣?差不多了吧。
1 // 2 // YYViewController.m 3 // 12-視圖改進(1) 4 // 5 // Created by apple on 14-5-25. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYViewController.h"10 #import "YYappInfo.h"11 #import "YYappInfoView.h"12 13 @interface YYViewController ()14 @property(nonatomic,strong)NSArray *apps;15 @end16 @implementation YYViewController17 18 -(NSArray *)apps19 {20 if (!_apps) {21 _apps=[YYappInfo appinfoarray];22 }23 return _apps;24 }25 26 - (void)viewDidLoad27 {28 [super viewDidLoad];29 30 int totalloc = 3;31 CGFloat appviewW = 80;32 CGFloat appviewH = 90;33 CGFloat margin = (self.view.frame.size.width-totalloc*appviewW)/(totalloc+1);34 35 int count=self.apps.count;36 for (int i = 0; i < count; i++) {37 38 int row = i/totalloc;39 int loc = i%totalloc;40 41 CGFloat appviewX = margin + (margin + appviewW) * loc;42 CGFloat appviewY = margin + (margin + appviewH) * row;43 44 YYappInfo *appinfo=self.apps[i];45 YYappInfoView *appinfoview=[YYappInfoView appInfoViewWithappInfo:appinfo];46 appinfoview.frame=CGRectMake(appviewX, appviewY, appviewW, appviewH);47 [self.view addSubview:appinfoview];48 }49 }50 @end
實現效果:
4.補充說明
View的封裝思路
(1) 如果一個view內部的子控制項比較多,一般會考慮自訂一個view,把它內部子控制項的建立屏蔽起來,不讓外界關心
(2) 外界可以傳入對應的模型資料給view,view拿到模型資料後給內部的子控制項設定對應的資料
三、mvc機制簡單說明
說明:
(1)在開發過程中,作為控制器處理的量級應該很輕,不該操心的不操心。協調好模型和視圖就ok了,要學會當一個好老闆。
(2)三個部分各司其職,資料模型只負責資料的處理,視圖部分只負責把拿到的資料進行顯示,兩個部分都是被動的,等待著大管家控制器的調遣。
(3)在OC中,如果視圖和資料模型之間有通道,那控制器是否處於失控狀態呢?
iOS開發UI篇—從代碼的逐步最佳化看MVC