在iOS開發基礎-九宮格(1)中,屬性變量 apps 是從plist文件中加載數據的,在 viewDidLoad 方法中的第20行、26行中,直接經過字典的鍵名來獲取相應的信息,使得 ViewController 直接與數據打交道,若是頻繁這樣使用,可能會不當心把鍵名寫錯,而程序卻不會報錯。所以,考慮把字典數據轉換成一個模型,把數據封裝到模型中去,讓 ViewController 與模型交互,而不與數據直接打交道。html
修改iOS開發基礎-九宮格(1)中的代碼:數組
字典轉模型的好處:app
1)下降代碼的耦合度;ide
2)將字典轉模型部分代碼集中在一到處理,下降代碼出錯機率;編碼
3)程序中,直接對模型的屬性進行操做,提供編碼效率。atom
4)調用時,沒必要關心模型內部的任何細節。spa
新建一個類命名爲 WJQAppInfo 繼承自 NSObject ,在 WJQAppInfo.h 頭文件中聲明公共的屬性和方法:code
1 //WJQAppInfo.h,模型類 2 #import <UIKit/UIKit.h> 3 4 @interface WJQAppInfo : NSObject 5 @property (nonatomic, copy) NSString *name; //圖片名稱 6 @property (nonatomic, copy) NSString *desc; //圖片信息描述 7 @property (nonatomic, strong, readonly) UIImage *image; 8 9 - (instancetype)initWithDict:(NSDictionary *)dict; 10 + (instancetype)appInfoWithDict:(NSDictionary *)dict; //工廠方法 11 @end
定義屬性時,會自動生成 setter 和 getter 方法,以及一個帶下劃線的成員變量。若是是 readonly 屬性,則只生成 getter 方法,也沒喲成員變量。orm
instancetype 會讓編譯器檢查實例化對象的準確類型,其只能用於返回類型,不能當作參數使用。htm
WJQAppInfo 類的實現文件代碼以下:
1 //WJQAppInfo.m 2 #import "WJQAppInfo.h" 3 4 @interface WJQAppInfo () 5 { 6 UIImage *_imageABC; 7 } 8 @end 9 10 @implementation WJQAppInfo 11 12 - (instancetype)initWithDict:(NSDictionary *)dict { 13 self = [super init]; 14 if (self) { 15 self.name = dict[@"name"]; 16 self.desc = dict[@"desc"]; 17 } 18 return self; 19 } 20 21 + (instancetype)appInfoWithDict:(NSDictionary *)dict { 22 return [[self alloc] initWithDict:dict]; 23 } 24 25 - (UIImage *)image { 26 if (!_imageABC) { 27 _imageABC = [UIImage imageNamed:self.name]; 28 } 29 return _imageABC; 30 } 31 32 @end
在 ViewController.m 文件中導入 WJQAppInfo 頭文件。
修改屬性 apps 的 getter 方法,代碼以下:
1 //字典轉換成WJQAppInfo模型 2 - (NSArray *)apps { 3 if (!_apps) { 4 NSString *path = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"plist"]; 5 //_apps = [NSArray arrayWithContentsOfFile:path]; 6 NSArray *array = [NSArray arrayWithContentsOfFile:path]; 7 NSMutableArray *arrayM = [NSMutableArray array]; 8 for (NSDictionary *dict in array) { 9 //遍歷數組,將數據在的字典依次轉化爲WJQAppInfo對象,並添加到臨時對象arrayM中去 10 //用字典來實例化對象的工廠方法 11 [arrayM addObject:[WJQAppInfo appInfoWithDict:dict]]; 12 } 13 _apps = arrayM; 14 } 15 return _apps; 16 }
修改 viewDidLoad 代碼以下:
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 4 int totalColumn = 3; //3列 5 CGFloat margin = (self.view.frame.size.width - totalColumn*appViewWidth) / (totalColumn + 1); 6 int count = self.apps.count; 7 NSLog(@"%d", count); 8 9 for (int i = 0; i < count; i++) { 10 int row = i/totalColumn; //行號,從0開始 11 int column = i%totalColumn; //列號,從0開始 12 CGFloat appViewX = margin + (margin + appViewWidth) * column; //子視圖的X座標 13 CGFloat appViewY = margin + (margin + appViewHeight) * row; //子視圖的Y座標 14 15 //建立UIView控件 16 UIView *appView = [[UIView alloc] initWithFrame:CGRectMake(appViewX, appViewY, appViewWidth, appViewHeight)]; 17 [self.view addSubview:appView]; 18 //建立上述UIView控件的子視圖,建立圖像信息 19 UIImageView *appImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 80, 50)]; 20 WJQAppInfo *appInfo = self.apps[i]; 21 UIImage *appImage = appInfo.image; 22 appImageView.image = appImage; //設置圖片 23 [appImageView setContentMode:UIViewContentModeScaleAspectFit]; 24 [appView addSubview:appImageView]; 25 //建立上述UIView控件的子視圖,建立UILabel信息描述標籤 26 UILabel *appLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 50, 80, 20)]; 27 [appLabel setText:appInfo.desc]; //設置圖片信息描述 28 [appLabel setTextAlignment:NSTextAlignmentCenter]; 29 [appLabel setFont:[UIFont systemFontOfSize:12.0]]; 30 [appLabel setTextColor:[UIColor blueColor]]; 31 [appView addSubview:appLabel]; 32 //建立下載按鈕 33 UIButton *appButton = [UIButton buttonWithType:UIButtonTypeCustom]; 34 appButton.frame = CGRectMake(0, 70, 80, 20); 35 [appButton setBackgroundImage:[UIImage imageNamed:@"download"] forState:UIControlStateNormal]; 36 [appButton setBackgroundImage:[UIImage imageNamed:@"downloaded"] forState:UIControlStateHighlighted]; 37 [appButton setTitle:@"下載" forState:UIControlStateNormal]; 38 appButton.titleLabel.font = [UIFont systemFontOfSize:12.0]; 39 [appButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; 40 [appView addSubview:appButton]; 41 [appButton addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside]; 42 } 43 }
使用 KVC 從新寫模型類中的 initWithDict: 方法:
1 //WJQAppInfo.m 2 - (instancetype)initWithDict:(NSDictionary *)dict { 3 self = [super init]; 4 if (self) { 5 [self setValue:dict[@"name"] forKey:@"name"]; 6 [self setValue:dict[@"desc"] forKey:@"desc"]; 7 } 8 return self; 9 }
或者使用 setValuesForKeysWithDictionary: 方法再對該方法進行修改:
1 //WJQAppInfo.m 2 - (instancetype)initWithDict:(NSDictionary *)dict { 3 self = [super init]; 4 if (self) { 5 [self setValuesForKeysWithDictionary:dict]; 6 } 7 return self; 8 }
setValuesForKeysWithDictionary: 方法會遍歷形參 dict 中每一個鍵值調用 setValue:forKey: 方法,可是當字典中有某個鍵值而調用的對象沒有相應的屬性時,系統會崩潰。
參考博客:iOS開發UI篇—字典轉模型