【iOS開發-48】九宮格佈局案例:本身主動佈局、字典轉模型運用、id和instancetype差異、xib反覆視圖運用及與nib關係

本次九宮格案例:數組


(1)導入app.plist和各類圖片素材,方便興許開發。實際開發中,也是如此。app


(2)把plist中數組導入進來。佈局

——因爲本案例中app.plist終因而一個數組,數組裏面是字典。因此咱們需要一個數組類型來接受這個plist文件。字體

——咱們利用以前掌握的在變量的getter中進行延遲載入數據。atom

#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)九宮格計算spa

——關鍵在於利用 / 和 % 運算獲得元素所在的行和列,注意%符號運算先後不能有CGFloat,都換成int類型較好。指針

——CGFloat事實上就是float和double的集合。所有用float和double的地方差點兒都可以用CGFloat。會依據當前系統本身主動解析,假設是32位系統,則用float,假設是64位系統,則解析成double。code

- (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裏相應的字典取出來使用就能夠。orm

——重點是,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文件了。

相關文章
相關標籤/搜索