封装一个UI控件的范例
来源:互联网 发布:unity3d ugui官方demo 编辑:程序博客网 时间:2024/06/01 14:59
用户需求一览
封装自己的UI空间需要注意哪些地方?
封装第三方ActionSheet中关于委托模式和Block的一点思考
代码地址:https://github.com/zhangyanrui/SimpleSelectionView
先来看一下要实现的功能:
其实很简单,就是自定义一个类似于ActionSheet的控件。
如何使用呢?也很简单:
[WZSelectionView showWithItemsBlock:^(id<WZSelectionItemsProtocol> items) { [items addItemWithLabelText:@"加入乐影单" imageName:@"homeAddList" shouldDismiss:YES]; [items addItemWithLabelText:@"下载" imageName:@"downloaded_icon" shouldDismiss:YES]; } selectedBlock:^(NSInteger selectedTag) { [self showTipsWithSelectedIndex:selectedTag]; }];
众所周知,我们封装View最常用的就是委托模式了。
委托模式可将数据与业务与逻辑解耦。比方说,用户界面有个显示数据用的视图,视图中应该只包含显示数据需要的逻辑,把显示何种数据和数据之间的交互逻辑放到委托类里面实现。就像TableView里面的“数据源(dataSource)”和“委托(delegate)”。
委托模式虽然做到了很好的逻辑解耦,但是需要我们要在自己的实现类里实现若干个委托方法,这样就使得我们的代码逻辑比较分散,我们可以使用Block来做到类似的效果。
对于一个View来说,我们首先需要提供一个数据源的入口,在这里,我们提供了ItemsBlock。ItemsBlock会传递一个实现了WZSelectionItemsProtocol的items容器对象。我们首先来看一下WZSelectionItemsProtocol协议。
@protocol WZSelectionItemsProtocol <NSObject>@required- (void)addItemWithLabelText:(NSString *)labelText imageName:(NSString *)imageName shouldDismiss:(BOOL)shouldDismiss;@end
这个协议很简单,暴露给我们一个添加item的方法,我们可以按照需要把我们的item一一添加到容器对象items里。这就完成了DataSource的传递。
来看一下WZSelectionView的代码:
@interface WZSelectionView () <UITableViewDataSource, UITableViewDelegate>@property (nonatomic, strong)UITableView *selectionTableView;@property (nonatomic, strong)WZSelectionItems *items;@property (nonatomic, copy) void (^seletedBlock)(NSInteger selectedIndex);@end
WZSelectionView内部的数据显示和交互回调我们借用了TableView,用WZSelectionItems来提供Tableview的数据源。传递过来的selectedBlock我们先暂存下来,tableview被点击的时候,我们把对应的index传递给selectedBlock。
WZSelectionItems类代码:
@interface WZSelectionItems : NSObject <WZSelectionItemsProtocol>@property (nonatomic, strong) NSMutableArray *itemsArray;- (NSInteger) count;@end@implementation WZSelectionItems- (instancetype)init{ self = [super init]; if (self) { self.itemsArray = [NSMutableArray array]; } return self;}- (void)addItemWithLabelText:(NSString *)labelText imageName:(NSString *)imageName shouldDismiss:(BOOL)shouldDismiss{ if (labelText && [labelText isKindOfClass:[NSString class]] && imageName && [imageName isKindOfClass:[NSString class]]) { NSDictionary *itemDic = [NSDictionary dictionaryWithObjectsAndKeys: labelText, kSelectionCellNameKey, imageName, kSelectionCellImageNameKey, [NSNumber numberWithBool:shouldDismiss], kSelectionViewShouldDismissKey, nil]; [self.itemsArray addObject:itemDic]; }}- (NSInteger)count{ return self.itemsArray.count;}@end
WZSelectionItems内部有一个可变数组,数组内容纳的是若干个包含item信息的字典。上文中提到过,WZSelectionItems实现了WZSelectionItemsProtocol协议,也就是实现了addItemWithLabelText:imageName:shouldDismiss:方法,该方法会将传递过来的信息封装到item信息字典,并添加到内置的可变数组中。此方法在何时回调呢?看代码:
- (void)showWithItemsBlock:(void (^)(id <WZSelectionItemsProtocol> item))itemsBlock selectedBlock:(void (^)(NSInteger))selectedBlock{ self.seletedBlock = selectedBlock; itemsBlock(self.items); ~~~~}
WZSelectionItems通过我们传入的ItemsBlock完成了数据源的组装,对应的通过把组装好的WZSelectionItems对象传递给tableView的dataSource,就完成了整个数据的显示过程。代码实现如下:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *WZSelectionCellID=@"WZSelectionCell"; static NSString *WZSelectionCancelCellID=@"WZSelectionCancelCell"; UITableViewCell *aCell; switch (indexPath.section) { case 0:{ WZSelectionCell *cell=[tableView dequeueReusableCellWithIdentifier:WZSelectionCellID]; if (cell==nil) { cell=[[WZSelectionCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:WZSelectionCellID]; } if (self.items.count > indexPath.row) { cell.infoDictionary = self.items.itemsArray[indexPath.row]; } aCell = cell; break; } case 1:{ WZSelectionCancelCell *cell=[tableView dequeueReusableCellWithIdentifier:WZSelectionCancelCellID]; if (cell==nil) { cell=[[WZSelectionCancelCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:WZSelectionCancelCellID]; } aCell = cell; } default: break; } if (!aCell) { aCell = [[UITableViewCell alloc] init]; } return aCell;}
相应的,我们如何提供委托的入口呢,对于此业务逻辑来说,我们只需要传递点击的item对应的index就好,所以我们提供了selectedBlock。selectedBlock传递一个index作为我们点击的序列。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ if (tableView == self.selectionTableView) { [tableView deselectRowAtIndexPath:indexPath animated:YES]; if (indexPath.section == 0) { self.seletedBlock(indexPath.row); if (self.items.itemsArray.count > indexPath.row) { BOOL shouldDismiss = [[self.items.itemsArray[indexPath.row] objectForKey:kSelectionViewShouldDismissKey] boolValue]; if (shouldDismiss) { [self hideSelf]; } } } else { [self hideSelf]; } }}
总结:我们在封装一个相对独立的View的时候,可以考虑用block来传递数据源和响应事件,这样封装的view在代码的连贯性上更好一些,可以用最少的方法来实现同样的功能,本文中暴露出来的公共方法只有一个。
- 封装一个UI控件的范例
- RichEdit控件的范例
- ios ui控件-UIScrollView封装
- 一个封装的QQ面板控件(HTC)
- 自己封装的一个GridView控件
- 一个圆角显示的控件封装
- iOS 教你学UI控件的封装
- UI控件笔记(六):UI之UINavigationController、navigationBar和UIView的封装
- UI控件笔记(十四):UI之自定义导航条的封装
- UI控件笔记(十五):UI之自定义搜索框的封装
- UI控件笔记(十六):UI之Uibutton,UIview,UIlbel等初始化的封装
- cocos2d-x自己封装的一个 等待的控件 TTWaitting
- 自己项目用的一个控件,自己封装的
- 简单的封装一个批量上传的控件
- 一个不错的SQL范例
- 抽象工厂的一个范例
- 一个简单的 fwrite 范例
- 一个JinternalFrame的子类范例
- Jenkins+RobotFramework持续集成测试-jenkins环境搭建
- leetcode-4-Median of Two Sorted Arrays
- 51单片机入门之七:按键检测
- 【机房重构】——视图
- 递增数组相同的存入一个数组里面,把最后的结果放到一个大数组里面
- 封装一个UI控件的范例
- 一个小工具完成对memcached/kt/mongodb/redis的性能监测
- Servlet_01_介绍(续)
- 网络---大文件的下载(NSURLSession)
- LeetCode 206. Reverse Linked List
- 收集oracle统计信息
- 【深度学习介绍系列之二】——深度强化学习:卷积神经网络
- hdu acm 2202 最大三角形
- 高手问答,Java多线程编程设计模式篇