模型的應用 && Xib &&代理模式的應用【應用管理】

模型

模型取代字典的好處

  • 使⽤用字典的壞處
    • ⼀一般情況下,設置數據和取出數據都使⽤用“字符串類型的key”,編寫這些key時,編譯器不會有任何友善提示,需要手敲。例如:
      dict[@"name"] = @"Jack";
      NSString *name = dict[@"name"];
    • 手敲字符串key,key容易寫錯 。Key如果寫錯了,編譯器不會有任何警告和報錯,造成設錯數據或者取錯數據。
  • 使⽤用模型的好處
    • 所謂模型,其實就是數據模型,專門⽤用來存放數據的對象,⽤用它來表⽰示數據會更加專業。
    • 模型設置數據和取出數據都是通過它的屬性,屬性名如果寫錯了,編譯器會馬上報錯,因此,保證了數據的正確性。
    • 使⽤模型訪問屬性時,編譯器會提供一系列的提⽰,提⾼編碼效率。例如:
      app.name = @"Jack”;
      NSString *name = app.name;

字典轉模型

  • 字典轉模型的過程最好封裝在模型內部
  • 模型應該提供⼀一個可以傳⼊入字典參數的構造⽅方法
    • -(instancetype)initWithDict:(NSDictionary*)dict;
    • +(instancetype)xxxWithDict:(NSDictionary*)dict;
    • instancetype在類型表⽰上,跟id一樣,可以表示任何對象類型
      instancetype只能用在返回值類型上,不能像id一樣用在參數類型上
      instancetype比id多一個好處:編譯器會檢測instancetype的真實類型

構建一個模型類 App 來存儲數據

//  App.h
// 模型類:用來存放數據的類

#import <Foundation/Foundation.h>
@interface App : NSObject
// 模型的名稱
@property (nonatomic, copy) NSString *name;
// 模型的圖標
@property (nonatomic, copy) NSString *icon;
// 通過字典來初始化模型對象
- (instancetype) initWithDict:(NSDictionary *)dict;
+ (instancetype) appWithDict:(NSDictionary *)dict;
@end
//  App.m

#import "App.h"
@implementation App

- (instancetype)initWithDict:(NSDictionary *)dict
{
    if(self = [super init]){
        self.name = dict[@"name"];
        self.icon = dict[@"icon"];
    }
    return self;
}

+ (instancetype)appWithDict:(NSDictionary *)dict
{
    return [[self alloc] initWithDict:dict];
}
@end

字典轉模型的過程

這裏寫圖片描述


Xib文件

Xib的創建及使用

  • Xib文件可以用來描述某一塊局部的UI界⾯
  • 如何創建Xib文件
    • 首先創建一個繼承UIView的自定義view。假如這個類叫:APPView。
    • 新建一個AppView.xib文件來描述AppView內部的結構。修改UIView的類型爲AppView真實類型,即把AppView.xib文件的class 改成 AppView如下圖所示:
      這裏寫圖片描述
  • Xib文件的加載

    • 調用方法
      NSArray *objs = [[NSBundle mainBundle] loadNibNamed:@"AppView" owner:nil
      options:nil];
    • 這個方法會創建xib中的所有對象,並且將對象按順序放到objs數組中。
      如果xib如下圖所⽰,那麼objs數組中依次會有3個對象:1個UIView、1個UILabel、1個UIButton
      這裏寫圖片描述
    • 在開發階段,面向開發者的是xib文件; 當把應用裝到⼿機上時,xib⽂件就會轉爲nib⽂件

    使用xib封裝一個自定義view的步驟

  • 1> 新建一個繼承UIView的自定義view,假設類名叫做(AppView)
  • 2> 新建一個AppView.xib文件來描述MJAppView內部的結構
  • 3> 修改UIView的類型爲AppView真實類型
  • 4> 將內部的子控件跟AppView進行屬性連線
  • 5> AppView中創建一個模型屬性
  • 6> 重寫模型屬性的set方法,因爲在set方法中可以拿到外界傳遞的模型數據
  • 7> 把模型數據拆開,分別設置數據到對應的子控件中
  • 8> 補充:提供一個創建MJAppView的類方法,將讀取xib文件的代碼屏蔽起來

    Xib和storyboard對比

  • 共同點:
    • 都用來描述軟件界面
    • 都用Interface Builder⼯具來編輯
  • 不同點
    • Xib是輕量級的,用來描述局部的UI界面
    • Storyboard是重量級的,用來描述整個軟件的多個界面,並且能展⽰多個界面之間的跳轉關係

源碼

結構框架

這裏寫圖片描述
這裏寫圖片描述

代碼

//  ViewController.h
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
//
//  ViewController.m
//

#import "ViewController.h"
#import "AppView.h"
#import "App.h"

@interface ViewController () <AppViewDelegate>

@property (nonatomic, strong) NSArray *apps;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 添加應用信息

    // 1.總列數(一行最多3列)
    int totalColumns = 3;

    // 2.應用的尺寸
    CGFloat appW = 90;
    CGFloat appH = 90;

    // 3.間隙 = (控制器view的寬度 - 3 * 應用寬度) / 4
    CGFloat marginX = (self.view.frame.size.width - totalColumns * appW) / (totalColumns + 1);
    CGFloat marginY = 40;

    // 4.根據應用個數創建對應的框框(index 0 ~ 11)
    for(int index = 0 ; index < self.apps.count; index++){

        // 3.1 創建view,設置數據
        AppView *appView = [AppView appViewWithApp:self.apps[index]];

        // 3.2 把view的代理設置爲控制器
        appView.delegate = self;

        // 3.3 添加view
        [self.view addSubview:appView];

        // 3.4 設置frame
        int row = index / totalColumns;
        int col = index % totalColumns;
        // 計算x和y
        CGFloat appX = marginX + col * (appW + marginX);
        CGFloat appY = 40 + row * (appH + marginY);
        appView.frame = CGRectMake(appX, appY, appW, appH);
    }

}
// 點擊下載按鈕時就會調用

- (void)appViewClickedDownloadButton:(AppView *)appView{

    // 1.取出模型
    App *app = appView.app;

    // 添加標籤
    UILabel *tipLabel = [[UILabel alloc] init];
    tipLabel.text = [NSString stringWithFormat:@"下載成功%@", app.name];
    tipLabel.font = [UIFont systemFontOfSize:14];
    tipLabel.textAlignment = NSTextAlignmentCenter;
    tipLabel.textColor = [UIColor whiteColor];
    tipLabel.backgroundColor = [UIColor blackColor];
    tipLabel.frame = CGRectMake(0, 0, 170, 40);
    tipLabel.center = CGPointMake(187.5, 300);
    tipLabel.alpha = 0.0;
    tipLabel.layer.cornerRadius = 5;
    tipLabel.clipsToBounds = YES;
    [self.view addSubview:tipLabel];

    // 3.動畫
    [UIView animateWithDuration:1.0 animations:^{
        tipLabel.alpha = 0.5;
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:1.0 delay:1.0 options:UIViewAnimationOptionCurveLinear animations:^{
            tipLabel.alpha = 0.0;
        } completion:^(BOOL finished) {
            [tipLabel removeFromSuperview];
        }];
    }];
}

- (NSArray *)apps
{
    if(_apps == nil){
        // 初始化

        // 1.獲得plist的全路徑
        NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil];

        // 2.加載數組
        NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];

        // 3.將dictArray裏面的所有字典轉成模型對象,放到新的數組中
        NSMutableArray *appArray = [NSMutableArray array];

        for(NSDictionary *dict in dictArray){
            // 3.1 創建模型對象
            App *app = [App appWithDict:dict];

            // 3.2 添加模型對象到數組中
            [appArray addObject:app];
        }

        // 4.賦值
        _apps = appArray;
    }
    return _apps;
}

@end
//
//  App.h
// 模型類:用來存放數據的類

#import <Foundation/Foundation.h>

@interface App : NSObject

// 模型的名稱
@property (nonatomic, copy) NSString *name;

// 模型的圖標
@property (nonatomic, copy) NSString *icon;

// 通過字典來初始化模型對象
- (instancetype) initWithDict:(NSDictionary *)dict;

+ (instancetype) appWithDict:(NSDictionary *)dict;

@end
//
//  App.m
//

#import "App.h"

@implementation App

- (instancetype)initWithDict:(NSDictionary *)dict
{
    if(self = [super init]){
        self.name = dict[@"name"];
        self.icon = dict[@"icon"];
    }
    return self;
}

+ (instancetype)appWithDict:(NSDictionary *)dict
{
    return [[self alloc] initWithDict:dict];
}
@end
//
//  AppView.h
//

#import <UIKit/UIKit.h>

@class App, AppView;

// 聲明一個協議
@protocol AppViewDelegate <NSObject>
@optional
- (void)appViewClickedDownloadButton:(AppView *)appView;

@end



@interface AppView : UIView

// 代理
@property (nonatomic, weak) id<AppViewDelegate> delegate;

// 數據模型
@property (nonatomic, strong) App *app;

// 創建模型
+ (instancetype)appView;

//通過模型數據來創建一個view
+ (instancetype)appViewWithApp:(App *)app;

@end
//
//  AppView.m
//

#import "AppView.h"
#import "App.h"

@interface AppView()
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
- (IBAction)download:(UIButton *)sender;
@end

@implementation AppView

+ (instancetype)appViewWithApp:(App *)app{

    NSBundle *bundle = [NSBundle mainBundle];

    // 讀取xib文件(會創建xib中的描述的所有對象,並且按順序放到數組中返回)
    NSArray *objs = [bundle loadNibNamed:@"AppView" owner:nil options:nil];

    AppView *appView = [objs lastObject];

    appView.app = app;
    return appView;
}

+ (instancetype)appView{
    return [self appViewWithApp:nil];
}
- (void)setApp:(App *)app
{
    _app = app;

    // 1.設置圖標
    self.iconView.image = [UIImage imageNamed:app.icon];

    // 2.設置名稱
    self.nameLabel.text = app.name;
}

// 下載按鈕

- (IBAction)download:(UIButton *)sender {
    // 1.讓按鈕失效(文字變爲“已下載”)
    sender.enabled = NO;
    [sender setTitle:@"已下載" forState:UIControlStateDisabled];

    // 2.通知代理,download按鈕被點擊。
    if([self.delegate respondsToSelector:@selector(appViewClickedDownloadButton:)]){
        [self.delegate appViewClickedDownloadButton:self];
    }
}
@end

代理模式

爲什麼要用代理模式

  • 我們要拿到控制器View,然後讓它顯示tipLabel,但是怎麼拿到控制器View?
    • (1)將控制器View傳到AppView.m裏面,然後在download:方法調用,但這種方法非常不好,耦合性太強。
    • (2) 將按鈕拋出去(放在AppView.h裏面,這樣控制器也可以監聽),讓控制器View監聽,這樣也不好。
    • 正確的方法是用代理模式,控制器作爲AppView的代理。

對代理模式的分析

  • 當點擊下載按鈕的時候,應該通知控制器,控制器得知點擊了下載按鈕,就執行:添加tipLabel到控制器的View
  • 該做的事情應該交給該做的那個類去做(比較適合誰做的事情,就交給誰去做)。比如說我們要拿到控制器的view,添加一個label,誰能直接拿到控制器的view,就是控制器。所以我們得到一個結論,拿到控制器,添加lable的代碼(操作),應該放到控制器裏面。
  • 控制器要監聽【AppView】內部下載按鈕的點擊,當下載按鈕被點擊了,會到download:方法,我們要在download:方法裏面通知控制器(代理)([self.delegate appViewClickedDownloadButton:self];這個方法就是通知代理),下載按鈕被點擊,然後控制器把label添加到它的view上面上去。

達到效果

  • 點擊下載按鈕後變成不能點擊的“已安裝”
  • 中間慢慢彈出提⽰示:已經成功安裝xxx,然後提⽰示會慢慢消失
    這裏寫圖片描述 這裏寫圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章