【iOS開發-44】通過案例談iOS代碼重構:合並、格式化輸出、宏變量、利用數組字典存儲數據,以及利用plist的終極知識

分類:編程 時間:2017-03-21

首先我們今天的案例就是如下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:
            numLabel.text=@"1/5";
            descLabel.text=@"This is the letter A";
            imgView1.image=[UIImage imageNamed:@"a.png"];
            break;
        case 1:
            numLabel.text=@"2/5";
            descLabel.text=@"This is the letter B";
            imgView1.image=[UIImage imageNamed:@"b.png"];
            break;
        case 2:
            numLabel.text=@"3/5";
            descLabel.text=@"This is the letter C";
            imgView1.image=[UIImage imageNamed:@"c.png"];
            break;
        case 3:
            numLabel.text=@"4/5";
            descLabel.text=@"This is the letter D";
            imgView1.image=[UIImage imageNamed:@"d.png"];
            break;
        case 4:
            numLabel.text=@"5/5";
            descLabel.text=@"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:
            //numLabel.text=@"1/5";
            descLabel.text=@"This is the letter A";
            imgView1.image=[UIImage imageNamed:@"a.png"];
            break;
        case 1:
            //numLabel.text=@"2/5";
            descLabel.text=@"This is the letter B";
            imgView1.image=[UIImage imageNamed:@"b.png"];
            break;
        case 2:
            //numLabel.text=@"3/5";
            descLabel.text=@"This is the letter C";
            imgView1.image=[UIImage imageNamed:@"c.png"];
            break;
        case 3:
            //numLabel.text=@"4/5";
            descLabel.text=@"This is the letter D";
            imgView1.image=[UIImage imageNamed:@"d.png"];
            break;
        case 4:
            //numLabel.text=@"5/5";
            descLabel.text=@"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"]=@"a.png";
    dic1[@"desc"]=@"This is the letter A";
    
    NSMutableDictionary *dic2=[NSMutableDictionary dictionary];
    dic2[@"icon"]=@"b.png";
    dic2[@"desc"]=@"This is the letter B";
    
    NSMutableDictionary *dic3=[NSMutableDictionary dictionary];
    dic3[@"icon"]=@"c.png";
    dic3[@"desc"]=@"This is the letter C";
    
    NSMutableDictionary *dic4=[NSMutableDictionary dictionary];
    dic4[@"icon"]=@"d.png";
    dic4[@"desc"]=@"This is the letter D";
    
    NSMutableDictionary *dic5=[NSMutableDictionary dictionary];
    dic5[@"icon"]=@"e.png";
    dic5[@"desc"]=@"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]=@"a.png";
        dic1[kDESCRIP]=@"This is the letter A";
        
        NSMutableDictionary *dic2=[NSMutableDictionary dictionary];
        dic2[kICONKEY]=@"b.png";
        dic2[kDESCRIP]=@"This is the letter B";
        
        NSMutableDictionary *dic3=[NSMutableDictionary dictionary];
        dic3[kICONKEY]=@"c.png";
        dic3[kDESCRIP]=@"This is the letter C";
        
        NSMutableDictionary *dic4=[NSMutableDictionary dictionary];
        dic4[kICONKEY]=@"d.png";
        dic4[kDESCRIP]=@"This is the letter D";
        
        NSMutableDictionary *dic5=[NSMutableDictionary dictionary];
        dic5[kICONKEY]=@"e.png";
        dic5[kDESCRIP]=@"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]=@"a.png";
//        dic1[kDESCRIP]=@"This is the letter A";
//        
//        NSMutableDictionary *dic2=[NSMutableDictionary dictionary];
//        dic2[kICONKEY]=@"b.png";
//        dic2[kDESCRIP]=@"This is the letter B";
//        
//        NSMutableDictionary *dic3=[NSMutableDictionary dictionary];
//        dic3[kICONKEY]=@"c.png";
//        dic3[kDESCRIP]=@"This is the letter C";
//        
//        NSMutableDictionary *dic4=[NSMutableDictionary dictionary];
//        dic4[kICONKEY]=@"d.png";
//        dic4[kDESCRIP]=@"This is the letter D";
//        
//        NSMutableDictionary *dic5=[NSMutableDictionary dictionary];
//        dic5[kICONKEY]=@"e.png";
//        dic5[kDESCRIP]=@"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;

總結:要自己動手。雖然都明白其中原理,但是真正操作起來,會遇到一些很小但是很重要的問題,一個一個的解決,這樣的積累估計就是新手和老鳥的區別。


Tags: interface enabled change import 開發

文章來源:


ads
ads

相關文章
ads

相關文章

ad