[iOS基础控件 - 5.5] 代理设计模式 (基于”APP列表"练习)

A.概述
     在"[iOS基础控件 - 4.4] APP列表 进一步封装,初见MVC模式”上进一步改进,给“下载”按钮加上效果、功能
     1.按钮点击后,显示为“已下载”,并且不可以再按
     2.在屏幕中间弹出一个消息框,通知消息“xx已经被安装”,慢慢消失
     3.消息框样式为圆角半透明
 
Image(67)
 
 
B.不使用代理模式,使用app空间组和主View之间的父子View关系
1.在主View中创建一个消息框
 
主View控制器:ViewController.m
   
 1  // 创建下载成功消息框
 2     CGFloat labelWidth = 200;
 3     CGFloat labelHeight = 50;
 4     CGFloat labelX = (self.view.frame.size.width - labelWidth) / 2;
 5     CGFloat labelY = (self.view.frame.size.height - labelHeight) / 2;
 6     UILabel *successMsgLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelX, labelY, labelWidth, labelHeight)];
 7    
 8     // 设置圆角矩形样式
 9     successMsgLabel.layer.cornerRadius = 10.0;
10     successMsgLabel.layer.masksToBounds = YES;
11    
12     // 设置全透明隐藏
13     successMsgLabel.alpha = 0;
14    
15     successMsgLabel.textColor = [UIColor whiteColor];
16     successMsgLabel.backgroundColor = [UIColor grayColor];
17     [successMsgLabel setTextAlignment:NSTextAlignmentCenter];
18     successMsgLabel.tag = 10;
19   
20     [self.view addSubview:successMsgLabel];
 
2.直接在app空间组的控制器中操作消息框,加入到父控件(主View)
 
app控件组View:AppView.m
 1 // 点击下载按钮
 2 - (IBAction)onDownloadButtonClick {
 3     // 更改“下载”按钮样式
 4     [self.downloadButton setTitle:@"已下载" forState:UIControlStateDisabled];
 5     self.downloadButton.enabled = NO;
 6   
 7     // 拿到消息框
 8     UILabel *successMsgLabel = [self.superview viewWithTag:10];
 9     successMsgLabel.text = [NSString stringWithFormat:@"成功安装了%@", self.appData.name];
10     successMsgLabel.alpha = 0.7;
11    
12     // 使用动画
13     [UIView beginAnimations:nil context:nil];
14     [UIView setAnimationDuration:2];
15     successMsgLabel.alpha = 0;
16     [UIView commitAnimations];
17 }
 
缺点:view的父子关系规定了这两个view的层次关系,依赖性、耦合性太强,导致各个view都不能自由修改
 
C.不使用代理模式,在app控件组的view中将主View作为一个成员
1.在控件组AppView中创建主View的引用
控件组 AppView.h
1 // 存储主View的引用
2 @property (nonatomic, weak) UIView *vcView;
 
2.在主View逐个加载控件组的时候,设置主View的引用
ViewController.m
 1  for (int index=0; index<self.apps.count; index++) {
 2         App *appData = self.apps[index];
 3 
 4         // 1.创建View
 5         AppView *appView = [AppView appViewWithApp:appData];
 6        
 7         // 2.定义每个app的位置、尺寸
 8         CGFloat appX = marginX + column * (marginX + APP_WIDTH);
 9         CGFloat appY = marginY + row * (marginY + APP_HEIGHT);
10         appView.frame = CGRectMake(appX, appY, APP_WIDTH, APP_HEIGHT);
11        
12         // 设置每个app控件view的主view引用
13         appView.vcView = self.view;
14        
15         // 3.加入此app信息到总view
16         [self.view addSubview:appView];
17        
18         column++;
19         if (column == appColumnCount) {
20             column = 0;
21             row++;
22         }
 
3.控件组AppView使用主View引用代替父控件引用
AppView.m
 1 // 点击下载按钮
 2 - (IBAction)onDownloadButtonClick {
 3     // 更改“下载”按钮样式
 4     [self.downloadButton setTitle:@"已下载" forState:UIControlStateDisabled];
 5     self.downloadButton.enabled = NO;
 6    
 7     // 创建消息框
 8     UILabel *successMsgLabel = [self.vcView viewWithTag:10];
 9     successMsgLabel.text = [NSString stringWithFormat:@"成功安装了%@", self.appData.name];
10     successMsgLabel.alpha = 0.7;
11    
12     // 使用动画
13     [UIView beginAnimations:nil context:nil];
14     [UIView setAnimationDuration:2];
15     successMsgLabel.alpha = 0;
16     [UIView commitAnimations];
17 }
 
缺点:控件组AppView的下载事件中取得控制器来弹出消息框,还是依赖于主View,耦合性强
 
 
D.暴露控件组AppView的“下载”按钮,在主控制器中编写“下载”事件方法,绑定方法
1.AppView暴露“下载”按钮控件给外部
AppView.h
1 // 将“下载”按钮控件移到 .h 文件中暴露
2 @property (weak, nonatomic) IBOutlet UIButton *downloadButton;
 
2.在控制器中编写“下载”单击事件方法
ViewController.m
 1 // 控制器创建“下载”按钮点击事件
 2 - (IBAction)onAppViewDownloadButtonClick:(UIButton *) downloadButton {
 3     // 更改“下载”按钮样式
 4     [downloadButton setTitle:@"已下载" forState:UIControlStateDisabled];
 5     downloadButton.enabled = NO;
 6    
 7     // 创建消息框
 8     UILabel *successMsgLabel = [self.view viewWithTag:99];
 9    
10     App *app = self.apps[downloadButton.tag];
11     successMsgLabel.text = [NSString stringWithFormat:@"成功安装了%@", app.name];
12     successMsgLabel.alpha = 0.7;
13    
14     // 使用动画
15     [UIView beginAnimations:nil context:nil];
16     [UIView setAnimationDuration:2];
17     successMsgLabel.alpha = 0;
18     [UIView commitAnimations];
19 }
20 
21 @end
 
3.给每个AppView的“下载”按钮绑定方法
   
 1  for (int index=0; index<self.apps.count; index++) {
 2         App *appData = self.apps[index];
 3 
 4         // 1.创建View
 5         AppView *appView = [AppView appViewWithApp:appData];
 6        
 7         // 2.定义每个app的位置、尺寸
 8         CGFloat appX = marginX + column * (marginX + APP_WIDTH);
 9         CGFloat appY = marginY + row * (marginY + APP_HEIGHT);
10         appView.frame = CGRectMake(appX, appY, APP_WIDTH, APP_HEIGHT);
11        
12         // 存储每个AppView对应的AppData数据索引在tag中
13         appView.downloadButton.tag = index;
14        
15         // 绑定每个AppView中的“下载”按钮点击事件
16         [appView.downloadButton addTarget:self action:@selector(onAppViewDownloadButtonClick:) forControlEvents:UIControlEventTouchUpInside];
17  
18         // 3.加入此app信息到总view
19         [self.view addSubview:appView];
20        
21         column++;
22         if (column == appColumnCount) {
23             column = 0;
24             row++;
25         }
26     }
 
缺点:依赖于AppView暴露的“下载”按钮,不能被修改
 
 
E.代理模式
1.原则:谁拥有资源,谁调用
     添加label到控制器的逻辑:控制器来做
     当点击“下载”按钮的时候,控件组AppView的按钮点击事件应该通知控制器,要执行添加label到控制器view的操作
     控制器监听控件组AppView的下载按钮的点击
 
1.声明代理
AppView.h
1 // 定义代理的协议
2 @protocol AppViewDelegate <NSObject>
3 // “下载”按钮被点击事件
4 @optional
5 - (void) appViewClickedDownloadButton:(AppView *) appView;
6 @end
 
2.在AppView中创建代理引用
AppView.h
1 @interface AppView : UIView
2 // 代理
3 @property(nonatomic, weak) id<AppViewDelegate> delegate;
4 ...
5 @end
 
3.控制器遵守AppViewDelegate,使其拥有称为代理的资格
1 ViewController.m
2 @interface ViewController () <AppViewDelegate>
3 ...
4 @end
 
4.实现代理方法
ViewController.m
 1 // “下载”按钮点击的代理方法
 2 - (void)appViewClickedDownloadButton:(AppView *)appView {
 3     // 创建下载成功消息框
 4     CGFloat labelWidth = 200;
 5     CGFloat labelHeight = 50;
 6     CGFloat labelX = (self.view.frame.size.width - labelWidth) / 2;
 7     CGFloat labelY = (self.view.frame.size.height - labelHeight) / 2;
 8     UILabel *successMsgLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelX, labelY, labelWidth, labelHeight)];
 9    
10     // 设置圆角矩形样式
11     successMsgLabel.layer.cornerRadius = 10.0;
12     successMsgLabel.layer.masksToBounds = YES;
13    
14     // 设置全透明隐藏
15     successMsgLabel.alpha = 0;
16    
17     successMsgLabel.textColor = [UIColor whiteColor];
18     successMsgLabel.backgroundColor = [UIColor grayColor];
19     [successMsgLabel setTextAlignment:NSTextAlignmentCenter];
20 
21     successMsgLabel.text = [NSString stringWithFormat:@"成功安装了%@", appView.appData.name];
22     successMsgLabel.alpha = 0.7;
23    
24     // 使用动画
25     [UIView beginAnimations:nil context:nil];
26     [UIView setAnimationDuration:2];
27     successMsgLabel.alpha = 0;
28     [UIView commitAnimations];
29    
30     [self.view addSubview:successMsgLabel];
31 }
 
5.给每一个AppView设置代理
ViewController.m
  
 1   for (int index=0; index<self.apps.count; index++) {
 2         App *appData = self.apps[index];
 3 
 4         // 1.创建View
 5         AppView *appView = [AppView appViewWithApp:appData];
 6        
 7         // 2.定义每个app的位置、尺寸
 8         CGFloat appX = marginX + column * (marginX + APP_WIDTH);
 9         CGFloat appY = marginY + row * (marginY + APP_HEIGHT);
10         appView.frame = CGRectMake(appX, appY, APP_WIDTH, APP_HEIGHT);
11        
12         // 设置代理
13         appView.delegate = self;
14        
15         // 3.加入此app信息到总view
16         [self.view addSubview:appView];
17        
18         column++;
19         if (column == appColumnCount) {
20             column = 0;
21             row++;
22         }
23     }
 
6.点击“下载”按钮的时候,通知代理
AppView.m
 1 // 点击下载按钮
 2 - (IBAction)onDownloadButtonClick {
 3     // 更改“下载”按钮样式
 4     [self.downloadButton setTitle:@"已下载" forState:UIControlStateDisabled];
 5     self.downloadButton.enabled = NO;
 6 
 7     // 通知代理
 8     // 检查是否实现了代理方法
 9     if ([self.delegate respondsToSelector:@selector(appViewClickedDownloadButton:)]) {
10         [self.delegate appViewClickedDownloadButton:self];
11     }
12 }
 
主要代码:
Model:
 1 App.h
 2 //
 3 //  App.h
 4 //  01-应用管理
 5 //
 6 //  Created by hellovoidworld on 14/11/25.
 7 //  Copyright (c) 2014年 hellovoidworld. All rights reserved.
 8 //
 9 
10 #import <Foundation/Foundation.h>
11 
12 @interface App : NSObject
13 
14 /**
15 copy : NSString
16 strong: 一般对象
17 weak: UI控件
18 assign: 基本数据类型
19 */
20 
21 /**
22 名称
23 */
24 @property(nonatomic, copy) NSString *name;
25 
26 /**
27 图标
28 */
29 @property(nonatomic, copy) NSString *icon;
30 
31 /**
32 自定义构造方法
33 通过字典来初始化模型对象
34 */
35 - (instancetype) initWithDictionary:(NSDictionary *) dictionary;
36 
37 + (instancetype) appWithDictionary:(NSDictionary *) dictionary;
38 
39 @end
 
App.m
 1 //
 2 //  App.m
 3 //  01-应用管理
 4 //
 5 //  Created by hellovoidworld on 14/11/25.
 6 //  Copyright (c) 2014年 hellovoidworld. All rights reserved.
 7 //
 8 
 9 #import "App.h"
10 
11 #define ICON_KEY @"icon"
12 #define NAME_KEY @"name"
13 
14 @implementation App
15 
16 - (instancetype) initWithDictionary:(NSDictionary *) dictionary {
17     if (self = [super init]) {
18         self.name = dictionary[NAME_KEY];
19         self.icon = dictionary[ICON_KEY];
20     }
21    
22     return self;
23 }
24 
25 
26 + (instancetype) appWithDictionary:(NSDictionary *) dictionary {
27     // 使用self代表类名代替真实类名,防止子类调用出错
28     return [[self alloc] initWithDictionary:dictionary];
29 }
30 
31 @end
 
View:
 1 AppView.h
 2 //
 3 //  AppView.h
 4 //  01-应用管理
 5 //
 6 //  Created by hellovoidworld on 14/11/25.
 7 //  Copyright (c) 2014年 hellovoidworld. All rights reserved.
 8 //
 9 
10 #import <UIKit/UIKit.h>
11 
12 @class App, AppView;
13 
14 // 定义代理的协议
15 @protocol AppViewDelegate <NSObject>
16 // “下载”按钮被点击事件
17 @optional
18 - (void) appViewClickedDownloadButton:(AppView *) appView;
19 @end
20 
21 @interface AppView : UIView
22 
23 // 代理
24 @property(nonatomic, weak) id<AppViewDelegate> delegate;
25 
26 // 在Controller和View之间传输的Model数据
27 @property(nonatomic, strong) App *appData;
28 
29 
30 // 自定义将Model数据加载到View的构造方法
31 - (instancetype) initWithApp:(App *) appData;
32 // 自定义构造的类方法
33 + (instancetype) appViewWithApp:(App *) appData;
34 // 返回一个不带Model数据的类构造方法
35 + (instancetype) appView;
36 
37 
38 @end
 
AppView.m
 1 //
 2 //  AppView.m
 3 //  01-应用管理
 4 //
 5 //  Created by hellovoidworld on 14/11/25.
 6 //  Copyright (c) 2014年 hellovoidworld. All rights reserved.
 7 //
 8 
 9 #import "AppView.h"
10 #import "App.h"
11 
12 // 封装私有属性
13 @interface AppView()
14 
15 // 封装View中的控件,只允许自己访问
16 @property (weak, nonatomic) IBOutlet UIImageView *iconView;
17 @property (weak, nonatomic) IBOutlet UILabel *nameLabel;
18 @property (weak, nonatomic) IBOutlet UIButton *downloadButton;
19 
20 
21 
22 - (IBAction)onDownloadButtonClick;
23 
24 @end
25 
26 @implementation AppView
27 
28 - (void)setAppData:(App *)appData {
29     // 1.赋值Medel成员
30     _appData = appData;
31    
32     // 2.设置图片
33     self.iconView.image = [UIImage imageNamed:appData.icon];
34     // 3.设置名字
35     self.nameLabel.text = appData.name;
36 }
37 
38 // 自定义将Model数据加载到View的构造方法
39 - (instancetype) initWithApp:(App *) appData {
40     // 1.从NIB取得控件
41     UINib *nib = [UINib nibWithNibName:@"app" bundle:[NSBundle mainBundle]];
42     NSArray *viewArray = [nib instantiateWithOwner:nil options:nil];
43     AppView *appView = [viewArray lastObject];
44    
45     // 2.加载Model
46     appView.appData = appData;
47    
48     return appView;
49 }
50 
51 // 自定义构造的类方法
52 + (instancetype) appViewWithApp:(App *) appData {
53     return [[self alloc] initWithApp:appData];
54 }
55 
56 // 返回一个不带Model数据的类构造方法
57 + (instancetype) appView {
58     return [self appViewWithApp:nil];
59 }
60 
61 // 点击下载按钮
62 - (IBAction)onDownloadButtonClick {
63     // 更改“下载”按钮样式
64     [self.downloadButton setTitle:@"已下载" forState:UIControlStateDisabled];
65     self.downloadButton.enabled = NO;
66 
67     // 通知代理
68     // 检查是否实现了代理方法
69     if ([self.delegate respondsToSelector:@selector(appViewClickedDownloadButton:)]) {
70         [self.delegate appViewClickedDownloadButton:self];
71     }
72 }
73 
74 @end
 
Controller:
ViewController.m
  1 //
  2 //  ViewController.m
  3 //  01-应用管理
  4 //
  5 //  Created by hellovoidworld on 14/11/24.
  6 //  Copyright (c) 2014年 hellovoidworld. All rights reserved.
  7 //
  8 
  9 #import "ViewController.h"
 10 #import "App.h"
 11 #import "AppView.h"
 12 
 13 #define ICON_KEY @"icon"
 14 #define NAME_KEY @"name"
 15 #define APP_WIDTH 85
 16 #define APP_HEIGHT 90
 17 #define MARGIN_HEAD 20
 18 #define ICON_WIDTH 50
 19 #define ICON_HEIGHT 50
 20 #define NAME_WIDTH APP_WIDTH
 21 #define NAME_HEIGHT 20
 22 #define DOWNLOAD_WIDTH (APP_WIDTH - 20)
 23 #define DOWNLOAD_HEIGHT 20
 24 
 25 @interface ViewController () <AppViewDelegate>
 26 
 27 /** 存放应用信息 */
 28 @property(nonatomic, strong) NSArray *apps; // 应用列表
 29 
 30 @end
 31 
 32 @implementation ViewController
 33 
 34 - (void)viewDidLoad {
 35     [super viewDidLoad];
 36     // Do any additional setup after loading the view, typically from a nib.
 37    
 38     [self loadApps];
 39 }
 40 
 41 - (void)didReceiveMemoryWarning {
 42     [super didReceiveMemoryWarning];
 43     // Dispose of any resources that can be recreated.
 44 }
 45 
 46 #pragma mark 取得应用列表
 47 - (NSArray *) apps {
 48     if (nil == _apps) {
 49         // 1.获得plist的全路径
 50         NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil];
 51        
 52         // 2.加载数据
 53         NSArray *dictArray  = [NSArray arrayWithContentsOfFile:path];
 54        
 55         // 3.将dictArray里面的所有字典转成模型,放到新数组中
 56         NSMutableArray *appArray = [NSMutableArray array];
 57         for (NSDictionary *dict in dictArray) {
 58             // 3.1创建模型对象
 59             App *app = [App appWithDictionary:dict];
 60            
 61             // 3.2 添加到app数组中
 62             [appArray addObject:app];
 63         }
 64        
 65         _apps = appArray;
 66     }
 67 
 68     return _apps;
 69 }
 70 
 71 #pragma mark 加载全部应用列表
 72 - (void) loadApps {
 73     int appColumnCount = [self appColumnCount];
 74     int appRowCount = [self appRowCount];
 75    
 76     CGFloat marginX = (self.view.frame.size.width - APP_WIDTH * appColumnCount) / (appColumnCount + 1);
 77     CGFloat marginY = (self.view.frame.size.height - APP_HEIGHT * appRowCount) / (appRowCount + 1) + MARGIN_HEAD;
 78    
 79     int column = 0;
 80     int row = 0;
 81     for (int index=0; index<self.apps.count; index++) {
 82         App *appData = self.apps[index];
 83 
 84         // 1.创建View
 85         AppView *appView = [AppView appViewWithApp:appData];
 86        
 87         // 2.定义每个app的位置、尺寸
 88         CGFloat appX = marginX + column * (marginX + APP_WIDTH);
 89         CGFloat appY = marginY + row * (marginY + APP_HEIGHT);
 90         appView.frame = CGRectMake(appX, appY, APP_WIDTH, APP_HEIGHT);
 91        
 92         // 设置代理
 93         appView.delegate = self;
 94        
 95         // 3.加入此app信息到总view
 96         [self.view addSubview:appView];
 97        
 98         column++;
 99         if (column == appColumnCount) {
100             column = 0;
101             row++;
102         }
103     }
104    
105 }
106 
107 
108 #pragma mark 计算列数
109 - (int) appColumnCount {
110     int count = 0;
111     count = self.view.frame.size.width / APP_WIDTH;
112    
113     if ((int)self.view.frame.size.width % (int)APP_WIDTH == 0) {
114         count--;
115     }
116    
117     return count;
118 }
119 
120 #pragma mark 计算行数
121 - (int) appRowCount {
122     int count = 0;
123     count = (self.view.frame.size.height - MARGIN_HEAD) / APP_HEIGHT;
124    
125     if ((int)(self.view.frame.size.height - MARGIN_HEAD) % (int)APP_HEIGHT == 0) {
126         count--;
127     }
128    
129     return count;
130 }
131 
132 // “下载”按钮点击的代理方法
133 - (void)appViewClickedDownloadButton:(AppView *)appView {
134     // 创建下载成功消息框
135     CGFloat labelWidth = 200;
136     CGFloat labelHeight = 50;
137     CGFloat labelX = (self.view.frame.size.width - labelWidth) / 2;
138     CGFloat labelY = (self.view.frame.size.height - labelHeight) / 2;
139     UILabel *successMsgLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelX, labelY, labelWidth, labelHeight)];
140    
141     // 设置圆角矩形样式
142     successMsgLabel.layer.cornerRadius = 10.0;
143     successMsgLabel.layer.masksToBounds = YES;
144    
145     // 设置全透明隐藏
146     successMsgLabel.alpha = 0;
147    
148     successMsgLabel.textColor = [UIColor whiteColor];
149     successMsgLabel.backgroundColor = [UIColor grayColor];
150     [successMsgLabel setTextAlignment:NSTextAlignmentCenter];
151 
152     successMsgLabel.text = [NSString stringWithFormat:@"成功安装了%@", appView.appData.name];
153     successMsgLabel.alpha = 0.7;
154    
155     // 使用动画
156     [UIView beginAnimations:nil context:nil];
157     [UIView setAnimationDuration:2];
158     successMsgLabel.alpha = 0;
159     [UIView commitAnimations];
160    
161     [self.view addSubview:successMsgLabel];
162 }
163 
164 @end

 

 
posted @ 2014-11-29 17:26  HelloVoidWorld  阅读(527)  评论(0编辑  收藏  举报