標籤:style blog http color io os 使用 ar for
本次九宮格案例:
(1)匯入app.plist和各種圖片素材,方便後續開發。實際開發中,也是如此。
(2)把plist中數組匯入進來。
——因為本案例中app.plist最終是一個數組,數組裡面是字典。所以我們需要一個數群組類型來接受這個plist檔案。
——我們利用之前掌握的在變數的getter中進行消極式載入資料。
#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輸出結果是:
{ 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。
- (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)
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種初始化方法,其實核心是一種,第二種還是通過第一種來實現的)。
#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中實現(注意寫法,記憶):
#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",因為要執行個體化對象)
-(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迴圈中,用到的地方都可以用對象.屬性來調用資料了:
//根據索引拿到每個對象,此處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方法。
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檔案了。
【iOS開發-48】九宮格布局案例:自動布局、字典轉模型運用、id和instancetype區別、xib重複視圖運用及與nib關係