轉載自:http://blog.csdn.net/weisubao/article/details/40075409
本次九宮格案例:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0NXYFhGd192UvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2Lc1DOXlVaWNzYwZlMkZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TO4ADM0kjM5EDNxATM0EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
(1)導入app.plist和各種圖檔素材,友善後續開發。實際開發中,也是如此。
(2)把plist中數組導入進來。
——因為本案例中app.plist最終是一個數組,數組裡面是字典。是以我們需要一個數組類型來接受這個plist檔案。
——我們利用之前掌握的在變量的getter中進行延遲加載資料。
[objc] view plain copy
- #import "ViewController.h"
- @interface ViewController ()
- @property(nonatomic,strong) NSArray *arr1;
- @end
- @implementation ViewController
- - (void)viewDidLoad {
- self.arr1;
- [super viewDidLoad];
- }
- -(NSArray *)arr1{
- if (_arr1==nil) {
- NSString *path=[[NSBundle mainBundle]pathForResource:@"app.plist" ofType:nil];
- _arr1=[NSArray arrayWithContentsOfFile:path];
- NSLog(@"%@",_arr1);
- }
- return _arr1;
- }
- @end
輸出結果是:
[objc] view plain copy
- {
- icon = "icon_00";
- name = "\U5929\U5929\U9177\U8dd1";
- },
- {
- icon = "icon_01";
- name = "\U5168\U6c11\U98de\U673a\U5927\U6218";
- },
- ……
icon後面就是icon的名稱,name後面也是name的名稱,隻不過中文被轉換成了Unicode形式,一個漢字就是一個\U****。
(3)九宮格計算
——關鍵在于利用 / 和 % 運算得到元素所在的行和列,注意%符号運算前後不能有CGFloat,都換成int類型較好。
——CGFloat其實就是float和double的集合。所有用float和double的地方幾乎都可以用CGFloat。會根據目前系統自動解析,如果是32位系統,則用float,如果是64位系統,則解析成double。
[objc] view plain copy
- - (void)viewDidLoad {
- //定義總列數、每個九宮格的寬高
- int totalColumns=3;
- CGFloat appW=90;
- CGFloat appH=100;
- //定義水準和垂直方面的間距
- CGFloat marginX=(self.view.frame.size.width-totalColumns*appW)/(totalColumns+1);
- CGFloat marginY=20;
- //根據arr1中資料數量來初始化并加載一個一個的UIVIew
- for (int index=0; index<self.arr1.count; index++) {
- //計算這個app在幾行幾列
- int row=index/totalColumns;
- int col=index%totalColumns;
- //建立UIView
- UIView *appView=[[UIView alloc]init];
- //根據一些計算,确定不同UIView的位置
- appView.frame=CGRectMake(marginX+col*(marginX+appW), 30+row*(marginY+appH), appW, appH);
- appView.backgroundColor=[UIColor redColor];
- [self.view addSubview:appView];
- }
- [super viewDidLoad];
- // Do any additional setup after loading the view, typically from a nib.
- }
(4)往每個appView裡面添加一個UIImageView、UIlabel和UIButton。直接在for循環中添加,即建立UIView *appView的時候順便把它裡面的東西也建立了。
——其中,UIImageView裡的圖檔用到取得圖檔的名稱,這個可以存放在plist裡面,我們把_arr1裡對應的字典取出來使用即可。
——重點是,UIButton的字型大小無法直接設定,而是用到UIbutton裡的子視圖titleLabel來設定。(因為UIButton裡面其實封裝了兩個子視圖控件,一個是裝文字的UILabel *titleLabel,一個裝圖檔的UIImageView *imageView)
[objc] view plain copy
- for (int index=0; index<self.arr1.count; index++) {
- //計算這個app在幾行幾列
- int row=index/totalColumns;
- int col=index%totalColumns;
- //建立UIView
- UIView *appView=[[UIView alloc]init];
- //根據一些計算,确定不同UIView的位置
- appView.frame=CGRectMake(marginX+col*(marginX+appW), 30+row*(marginY+appH), appW, appH);
- // appView.backgroundColor=[UIColor redColor];
- [self.view addSubview:appView];
- //根據索引拿到plist每個字典的資料
- NSDictionary *appDic=_arr1[index];
- //往appView裡增加子控件icon
- //根據字典拿到裡面的icon名稱
- UIImageView *appIcon=[[UIImageView alloc]init];
- CGFloat iconW=65;
- CGFloat iconH=65;
- CGFloat iconX=(appW-iconW)/2;
- CGFloat iconY=0;
- appIcon.frame=CGRectMake(iconX, iconY, iconW, iconH);
- appIcon.image=[UIImage imageNamed:appDic[@"icon"]];
- [appView addSubview:appIcon];
- //往appView裡增加子控件label
- UILabel *appLabel=[[UILabel alloc]init];
- CGFloat labelW=appW;
- CGFloat labelH=20;
- CGFloat labelX=(appW-labelW)/2;
- CGFloat labelY=iconY+iconH;
- appLabel.frame=CGRectMake(labelX, labelY, labelW, labelH);
- appLabel.text=appDic[@"name"];
- appLabel.textAlignment=NSTextAlignmentCenter;
- appLabel.font=[UIFont systemFontOfSize:14];
- [appView addSubview:appLabel];
- //往appView裡增加子控件button
- UIButton *appBtn=[[UIButton alloc]init];
- CGFloat btnW=65;
- CGFloat btnH=26;
- CGFloat btnX=(appW-btnW)/2;
- CGFloat btnY=labelY+labelH;
- appBtn.frame=CGRectMake(btnX, btnY, btnW, btnH);
- [appBtn setTitle:@"下載下傳" forState:UIControlStateNormal];
- appBtn.titleLabel.font=[UIFont systemFontOfSize:14];
- [appBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen"] forState:UIControlStateNormal];
- [appBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen_highlighted"] forState:UIControlStateNormal];
- [appView addSubview:appBtn];
- }
(5)最重要的:字典轉模型
使用字典的壞處:需要用key值調取和設定資料,有時候會出錯,雖然可以用宏變量改進,但是更要命的寫錯了key值,沒有錯誤提示。
模型:嚴格叫做模型資料。核心就是我們把字典當成一個對象,字典裡面的幾個資料,我們分别轉換成對象的幾個屬性,我們調用和設定資料的時候直接是“對象.屬性”即可。
是以,我們需要建立一個類,這個專門用來存放資料,也就是常說的模型類。
本例中,建立一個JiuGongGe類,在.h中聲明2個變量,和2種初始化方法(規範都是有2種初始化方法,其實核心是一種,第二種還是通過第一種來實作的)。
[objc] view plain copy
- #import <Foundation/Foundation.h>
- @interface JiuGongGe : NSObject
- @property(nonatomic,copy) NSString *name;
- @property(nonatomic,copy) NSString *icon;
- -(instancetype)initWithJiuGongGe:(NSDictionary *)dic;
- +(instancetype)jiuGongGeWith:(NSDictionary *)dic;
- @end
在JiuGongGe.m中實作(注意寫法,記憶):
[objc] view plain copy
- #import "JiuGongGe.h"
- @implementation JiuGongGe
- -(instancetype)initWithJiuGongGe:(NSDictionary *)dic{
- if (self=[super init]) {
- self.name=dic[@"name"];
- self.icon=dic[@"icon"];
- }
- return self;
- }
- +(instancetype)jiuGongGeWith:(NSDictionary *)dic{
- return [[JiuGongGe alloc]initWithJiuGongGe:dic];
- }
- @end
其實,我們的小标題是“字典轉模型”,也就是說隻是把字典轉換成模型(對象),原先字典存放在數組中的,然後通過數組[index]一個個調用字典,現在模型(對象)依然存放在數組中。是以我們需要對數組的那個getter方法進行改進:(注意:需要在ViewController.m中#import "JiuGongGe.h",因為要執行個體化對象)
[objc] view plain copy
- -(NSArray *)arr1{
- if (_arr1==nil) {
- NSString *path=[[NSBundle mainBundle]pathForResource:@"app.plist" ofType:nil];
- NSArray *tmpArr=[NSArray arrayWithContentsOfFile:path];
- NSMutableArray *muArr1=[[NSMutableArray alloc]init];
- for (NSDictionary *dict in tmpArr) {
- JiuGongGe *jiugognge=[JiuGongGe jiuGongGeWith:dict];
- [muArr1 addObject:jiugognge];
- }
- _arr1=muArr1;
- }
- return _arr1;
- }
在ViewDidLoad的那個for循環中,用到的地方都可以用對象.屬性來調用資料了:
[objc] view plain copy
- //根據索引拿到每個對象,此處appDic名稱未改,還是用之前取字典的那個變量,看的不太習慣
- JiuGongGe *appDic=_arr1[index];
- //往appView裡增加子控件icon
- ……
- appIcon.image=[UIImage imageNamed:appDic.icon];
- //往appView裡增加子控件label
- appLabel.text=appDic.name;
(6)jiuGongGeWith:(NSDictionary *)dic;初始化方法的改進
——裡面用到的類名,可以替換成self。因為防止這個類有子類,如果子類調用jiuGongGeWith:(NSDictionary *)dic;時調用到父類的這個方法,裡面寫得名字還是父類的名字,初始化結果是一個父類的對象,而不是子類的對象。是以用self,誰調用就初始化誰的對象。
(7)id類型和instancetype的說明
——instancetype和id一樣,都是萬能指針。
——iOS建議我們使用instancetype代替id。盡管官方很多init方法的傳回值也是id。
——使用id的好處就是,id是萬能指針,我們不用擔心它的傳回值類型不比對的問題。
——使用id的壞處:也正是因為它是萬能指針,我們可以用任意指針接受這個傳回值,比如NSString *str1=****,NSArray *arr1=****,這句代碼寫出來不會報錯。但是有可能不是我們需要的傳回值類型。
——使用instancetype的好處是,如果我們傳回值是一個對象,那麼你用上面兩個任意指針接受這個傳回值,它會有warning警告,我們用類對象JiuGongGe *jiugognge=***,就不會警告。
——instancetype隻能用在傳回值類型上,不能像id一樣用在參數上。
(8)利用xib圖形化布局減少代碼
xib和storyboard的差別在于,storyboard是描述整個程式界面的,而xib多用于局部重複界面的描述。
比如本例中有12個應用,每個應用的視圖都是一樣的,可以用xib來實作,然後再把xib加載進來即可。
xib的建立(用empty):
在ourXib中布局:
——給UIImageView和UILabel分别設定tag為10和20,友善調用。
——拖動控件到界面中,改變大小時候,UIImageView需要把size設定成Freeform才能調整大小。
ourXib設定好後,就可以調用:
——除了圖檔之外的資源,都需要用[NSBundle mainBundle]來調用。
——調用xib檔案的方法是mainBundle的loadNibNamed方法。
[objc] view plain copy
- for (int index=0; index<self.arr1.count; index++) {
- //計算這個app在幾行幾列
- int row=index/totalColumns;
- int col=index%totalColumns;
- //根據索引拿到每個對象
- JiuGongGe *appDic=_arr1[index];
- NSArray *xibArr=[[NSBundle mainBundle]loadNibNamed:@"ourXib" owner:nil options:nil];
- UIView *xibView=[xibArr lastObject];
- xibView.frame=CGRectMake(marginX+col*(marginX+appW), 30+row*(marginY+appH), appW, appH);
- UIImageView *imgView2=(UIImageView *)[xibView viewWithTag:10];
- imgView2.image=[UIImage imageNamed:appDic.icon];
- UILabel *label2=(UILabel *)[xibView viewWithTag:20];
- label2.text=appDic.name;
- //添加到主view中
- [self.view addSubview:xibView];
- }
(9)xib檔案和nib檔案是什麼關系?
——xib檔案是我們開發者在開發的時候看到的東西;
——而運作在使用者手機裡時,xib檔案會被轉化為nib檔案。
我們可以在iOS Simulator産生的沙盒中檢視。
——找不到資源庫路徑,直接用NSLog(@"%@",[NSBundle mainBundle);把路徑列印出來。
查找發現,這個資源庫中确實有個ourXib.nib檔案,xib檔案确實轉化成nib檔案了。