標籤:des blog http io os 使用 ar for strong
首先我們今天的案例就是如下5個頁面通過上一張下一張來切換:
(1)第一步,基本是以很傻很直接的方式來建立,這裡用到的主要點有:
——把物件變數設定為全域變數使得可以在其他方法中調用來設定它們的屬性
——設定了一個全域變數index,預設是0,然後通過增加減少這個index值並結合switch來調用不同的資料。
——利用先調用一次change方法初始化頁面,使得頁面定格在第一幀。
——利用按鈕的enabled屬性來設定按鈕是否可以被點擊,然後結合index的值分別在第1張和第5張時分別把上一張和下一張按鈕設定為灰色不可點擊。當然初始化頁面的時候也要判斷一下把上一張按鈕設定為灰色。
——這裡的change方法和btnCheck方法都是代碼重構的產物,因為這兩個方法都需要在preOne和nextOne方法中被調用,所以為了避免重複代碼,所以把這些重複的部分都封裝成了方法。
#import "ViewController.h"//把一些對象定義成全域變數,這樣可以多個方法中調用@interface ViewController (){ UIButton *btnPre; UIButton *btnNext; UILabel *numLabel; UILabel *descLabel; UIImageView *imgView1; int index1;}@end@implementation ViewController- (void)viewDidLoad { btnPre=[UIButton buttonWithType:UIButtonTypeRoundedRect]; btnPre.frame=CGRectMake(50, 150, 60, 30); [btnPre setTitle:@"上一張" forState:UIControlStateNormal]; [btnPre addTarget:self action:@selector(preOne) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnPre]; btnNext=[UIButton buttonWithType:UIButtonTypeRoundedRect]; btnNext.frame=CGRectMake(250, 150, 60, 30); [btnNext setTitle:@"下一張" forState:UIControlStateNormal]; [btnNext addTarget:self action:@selector(nextOne) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnNext]; numLabel=[[UILabel alloc]init]; numLabel.frame=CGRectMake(170, 50, 100, 30); [self.view addSubview:numLabel]; descLabel=[[UILabel alloc]init]; descLabel.frame=CGRectMake(110, 300, 200, 30); [self.view addSubview:descLabel]; imgView1=[[UIImageView alloc]init]; imgView1.frame=CGRectMake(130, 100, 100, 100); [self.view addSubview:imgView1]; //因為一開始的index為0,所以我們直接調用change方法,相當於把第一幀的頁面調出來初始化頁面 [self change]; btnPre.enabled=(index1!=0); [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib.}-(void)preOne{ index1--; [self btnCheck]; [self change];}-(void)nextOne{ index1++; [self btnCheck]; [self change];}//按鈕的enabled屬性,如果是NO,則變成灰色。其實以下語句是三目運算推到而來-(void)btnCheck{ btnPre.enabled=(index1!=0); btnNext.enabled=(index1!=4);}-(void)change{ switch (index1) { case 0: [email protected]"1/5"; [email protected]"This is the letter A"; imgView1.image=[UIImage imageNamed:@"a.png"]; break; case 1: [email protected]"2/5"; [email protected]"This is the letter B"; imgView1.image=[UIImage imageNamed:@"b.png"]; break; case 2: [email protected]"3/5"; [email protected]"This is the letter C"; imgView1.image=[UIImage imageNamed:@"c.png"]; break; case 3: [email protected]"4/5"; [email protected]"This is the letter D"; imgView1.image=[UIImage imageNamed:@"d.png"]; break; case 4: [email protected]"5/5"; [email protected]"This is the letter E"; imgView1.image=[UIImage imageNamed:@"e.png"]; break; default: break; }}@end
(2)對switch部分進行改造:利用格式化輸出重構代碼:用第二行代碼代替注釋掉的那5行代碼。
-(void)change{ numLabel.text=[NSString stringWithFormat:@"%d/%d",index1+1,5]; switch (index1) { case 0: //[email protected]"1/5"; [email protected]"This is the letter A"; imgView1.image=[UIImage imageNamed:@"a.png"]; break; case 1: //[email protected]"2/5"; [email protected]"This is the letter B"; imgView1.image=[UIImage imageNamed:@"b.png"]; break; case 2: //[email protected]"3/5"; [email protected]"This is the letter C"; imgView1.image=[UIImage imageNamed:@"c.png"]; break; case 3: //[email protected]"4/5"; [email protected]"This is the letter D"; imgView1.image=[UIImage imageNamed:@"d.png"]; break; case 4: //[email protected]"5/5"; [email protected]"This is the letter E"; imgView1.image=[UIImage imageNamed:@"e.png"]; break; default: break; }}
(3)利用字典和數組把資料單獨出來,並實現資料的刪減和代碼之間的獨立,即增減資料後,不需要修改我們顯示“總頁數”等這些代碼,但增減資料仍需要通過增減代碼來實現。(在開頭定義一個全域變數NSArray *arr1)
- (void)viewDidLoad { …… NSMutableDictionary *dic1=[NSMutableDictionary dictionary]; dic1[@"icon"][email protected]"a.png"; dic1[@"desc"][email protected]"This is the letter A"; NSMutableDictionary *dic2=[NSMutableDictionary dictionary]; dic2[@"icon"][email protected]"b.png"; dic2[@"desc"][email protected]"This is the letter B"; NSMutableDictionary *dic3=[NSMutableDictionary dictionary]; dic3[@"icon"][email protected]"c.png"; dic3[@"desc"][email protected]"This is the letter C"; NSMutableDictionary *dic4=[NSMutableDictionary dictionary]; dic4[@"icon"][email protected]"d.png"; dic4[@"desc"][email protected]"This is the letter D"; NSMutableDictionary *dic5=[NSMutableDictionary dictionary]; dic5[@"icon"][email protected]"e.png"; dic5[@"desc"][email protected]"This is the letter E"; arr1=[NSArray arrayWithObjects:dic1,dic2,dic3,dic4,dic5, nil]; //以上代碼需要添加在self change上,否則這個初始化是沒有資料可以初始化的 [self change]; btnPre.enabled=(index1!=0); [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib.}
初始化了資料之後,其他地方都可以進行簡化了,比如計算頁數,比如取資料,就可以利用字典和數組來取資料:
-(void)btnCheck{ btnPre.enabled=(index1!=0); btnNext.enabled=(index1!=arr1.count-1);//計算頁數的相關代碼}
-(void)change{ numLabel.text=[NSString stringWithFormat:@"%d/%d",index1+1,5]; //取出對應的資料 NSDictionary *dic=arr1[index1]; //設定icon圖片 imgView1.image=[UIImage imageNamed:dic[@"icon"]]; //設定標題文字 descLabel.text=dic[@"desc"];}
以上一步的好處在於我們增減資料的時候只需要在增減新的dic6等等,然後把dic6之類的添加到數組arr1中即可。其餘地方不需要手動修改數字,因為我們引用數位地方是用了數組的count屬性,引用資料的地方使用了數組和字典的相關屬性,不是寫死的,而是活的。
(4)利用宏變數避免代碼出現錯誤的可能性,尤其是在多人協作開發時,利用宏變數的提示可減少誤輸入的可能。
#define kICONKEY @"icon"#define kDESCRIP @"desc"
所以其他響應的地方都應該替換成宏變數。
(5)利用property建立變數,注意,雖然老師說建議控制項對象用weak,而一般對象用strong,但是發現,使用weak根本無法執行個體化對象,所以此處暫時還是用strong,等查明原因再說。響應的下面的變數都可以用_****代替或者用self.***代替。建議此處用後者。
@interface ViewController (){// UIButton *btnPre;// UIButton *btnNext;// UILabel *numLabel;// UILabel *descLabel;// UIImageView *imgView1;// int index1;}@property(nonatomic,retain) UIButton *btnPre;@property(nonatomic,retain) UIButton *btnNext;@property(nonatomic,retain) UILabel *numLabel;@property(nonatomic,retain) UILabel *descLabel;@property(nonatomic,strong) UIImageView *imgView1;@property(nonatomic,strong) NSArray *arr1;@property(nonatomic,assign) int index1;@end
(6)消極式載入,懶載入。只有需要的時候才初始化載入資料。也就是說,我們可以把資料型的屬性的初始化放在這個資料的getter方法中,且做一個判斷是否要重新載入。
我們預設的arr1的getter方法是:
-(NSArray *)arr1{ return _arr1;}
修改為如下,即如果這個數組載入過資料,則不用重複載入,而且是用self.arr1調用到它的時候才載入,這就是消極式載入:
-(NSArray *)arr1{ if (_arr1==nil) {//此處用_arr1而不用self.arr1是避免死迴圈,因為self.arr1也是調用這個函數,會一直迴圈調用自身 NSMutableDictionary *dic1=[NSMutableDictionary dictionary]; dic1[kICONKEY][email protected]"a.png"; dic1[kDESCRIP][email protected]"This is the letter A"; NSMutableDictionary *dic2=[NSMutableDictionary dictionary]; dic2[kICONKEY][email protected]"b.png"; dic2[kDESCRIP][email protected]"This is the letter B"; NSMutableDictionary *dic3=[NSMutableDictionary dictionary]; dic3[kICONKEY][email protected]"c.png"; dic3[kDESCRIP][email protected]"This is the letter C"; NSMutableDictionary *dic4=[NSMutableDictionary dictionary]; dic4[kICONKEY][email protected]"d.png"; dic4[kDESCRIP][email protected]"This is the letter D"; NSMutableDictionary *dic5=[NSMutableDictionary dictionary]; dic5[kICONKEY][email protected]"e.png"; dic5[kDESCRIP][email protected]"This is the letter E"; _arr1=[NSArray arrayWithObjects:dic1,dic2,dic3,dic4,dic5, nil]; } return _arr1;}
(7)再進一步:把資料獨立存放在plist中,以後增減資料只是修改plist檔案,而不需要在代碼中增減資料。
先建立plist檔案:
然後,在代碼中引用,注釋掉的那些資料都已經存放在plist檔案中了,用最下面的幾行來使用plist檔案即可,以後有增減資料,只要改動plist檔案,不需要改動代碼:
-(NSArray *)arr1{ if (_arr1==nil) {// NSMutableDictionary *dic1=[NSMutableDictionary dictionary];// dic1[kICONKEY][email protected]"a.png";// dic1[kDESCRIP][email protected]"This is the letter A";// // NSMutableDictionary *dic2=[NSMutableDictionary dictionary];// dic2[kICONKEY][email protected]"b.png";// dic2[kDESCRIP][email protected]"This is the letter B";// // NSMutableDictionary *dic3=[NSMutableDictionary dictionary];// dic3[kICONKEY][email protected]"c.png";// dic3[kDESCRIP][email protected]"This is the letter C";// // NSMutableDictionary *dic4=[NSMutableDictionary dictionary];// dic4[kICONKEY][email protected]"d.png";// dic4[kDESCRIP][email protected]"This is the letter D";// // NSMutableDictionary *dic5=[NSMutableDictionary dictionary];// dic5[kICONKEY][email protected]"e.png";// dic5[kDESCRIP][email protected]"This is the letter E"; // _arr1=[NSArray arrayWithObjects:dic1,dic2,dic3,dic4,dic5, nil]; //取得mainBundle,即程式主資料夾 NSBundle *path=[NSBundle mainBundle]; //用取得的mainBundle來尋找檔案,返迴路徑 NSString *pathFile=[path pathForResource:@"imgdata" ofType:@"plist"]; _arr1=[NSArray arrayWithContentsOfFile:pathFile]; } return _arr1;}
(8)補充:怎麼查看這個mainBundle資產庫?
NSBundle* path=[NSBundle mainBundle]就是拿到這個資產庫的路徑,返回的是NSBundle對象,其實是一個路徑,可以通過這個來訪問資產庫裡面的所有資源。其實它具體放在哪裡?無須上網尋找,直接用NSLog(@"%@",path);把這個路徑列印出來不就ok了嘛。
NSBundle </Users/Andy/Library/Developer/CoreSimulator/Devices/64EDA842-5B0C-448D-BF2B-B063D09B60CB/data/Containers/Bundle/Application/E0E6FE95-99D1-4F70-84CD-D73059EA71DF/hello.app>
順著上面這個路徑就找到了這個hello.app包>>>右擊顯示包內容。大功告成。
(9)圖片的大小不一樣,如何是好?
一般我們會給UIImageView設定好固定的寬高,但是圖片如果有大有小怎麼辦?需要用到調用“內容模式”contentMode這個屬性,即調整UIImageView裡面內容怎麼縮放擺放的。一般預設的是展開圖片直至填滿整個UIViewView。這樣通常會改變圖片的寬高比,使得圖片變形。我們一般常用的時,在保持圖片寬高比的情況下,儘可能的填充這個UIImageView即可,這個屬性以及值就是(以上代碼為什麼沒有添加,因為我們做得時候就設定了圖片都是100*100,UIImageView也是100*100,所以不需要用到這個屬性):
self.imgView1.contentMode=UIViewContentModeScaleAspectFit;
(10)文字太多,自動換行怎麼設定?
以標題文字的descLabel為例,我們本案例中文字並不是很多,且給了這個descLabel寬度200,足夠用,所有只有一行。如果我們把寬度設定為100,就發現顯示不下,最後有個...表示省略內容。設定多行,有個numberOfLine屬性,你可以設定具體的2,3,4等行數數字,也可以直接用0,表示無所謂多少行。
需要注意的時,設定多行的時候,你的descLabel高度要足夠,不然依然是顯示...省略符號。我們原先是30的高度,此處改成了60。
self.descLabel.frame=CGRectMake(110, 300, 100, 60); self.descLabel.numberOfLines=0;
總結:要自己動手。雖然都明白其中原理,但是真正操作起來,會遇到一些很小但是很重要的問題,一個一個的解決,這樣的積累估計就是新手和老鳥的區別。
【iOS開發-44】通過案例談iOS代碼重構:合并、格式化輸出、宏變數、利用數組字典儲存資料,以及利用plist的終極知識