玩转UITableView系列(一)--- 解耦封装、简化代码、适者生存!
UITableView这个iOS开发中永远绕不开的UIView,那么就不可避免的要在多个页面多种场景下反复摩擦UITableView,就算是刚跳进火坑不久的iOS Developer也知道实现UITableView的数据源dataSource和代理delegate,写出一个UITableView也就基本OK了,但是这仅仅是写出一个UITableView而已,作为一个有想法的程序猿,要做的还有很多,如何利用UITableViewCell的重用机制,如何提高性能等,这些留在后面的系列中一一讲述,那么本文要解决的痛点又是什么呢?回答这个问题之前,我们先来看看上面提到的UITableView的两大核心:UITableViewDataSource、UITableViewDelegate!
一、UITableViewDataSource
UITableView需要一个数据源(dataSource)来显示数据,UITableView会向数据源查询一共有多少行数据以及每一行显示什么数据等。没有设置数据源的UITableView只是个空壳。凡是遵守UITableViewDataSource协议的OC对象,都可以是UITableView的数据源。查看源码:
@required // 必须实现 // 每个section的行数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; // 第section分区第row行的UITableViewCell对象
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; @optional // 可选实现 // section个数,默认是1
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; // 第section分区的头部标题
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section; // 第section分区的底部标题
- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section; // 某一行是否可以编辑(删除)
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath; // 某一行是否可以移动来进行重新排序
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath; // UITableView右边的索引栏的内容
// return list of section titles to display in section index view (e.g. "ABCD...Z#")
- (nullable NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView;
UITableViewDataSourc
@protocol UITableViewDelegate<NSObject, UIScrollViewDelegate> @optional // 每行高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath; // 每个section头部高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section; // 每个section底部高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section; // 每个section头部自定义UIView
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section; // 每个section底部自定义UIView
- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section; // 是否允许高亮
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0); // 选中某行
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
UITableViewDelegate
到这里已经很明确了,在需要实现UITableView的控制器对象里,就不可避免的要设置数据源和设置代理,那么就不可避免的需要实现以上提到的那些代理方法,试想一下,如果不进行有效的封装,那极有可能每个需要UITableView的Controller里都有如下重复的代码行:
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 0.000001;
} - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
return 0.000001;
} - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return ;
} - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 0.000001;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return ;
} - (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
return nil;
} - (UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
return nil;
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"defaultType"];
return cell;
} - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
return;
} // lazy load
- (UITableView*)tableView{
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(, -, KS_Width, KS_Heigth+) style:UITableViewStyleGrouped];
_tableView.delegate = (id)self;
_tableView.dataSource = (id)self;
[_tableView setSectionHeaderHeight:];
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableView.showsVerticalScrollIndicator = NO;
_tableView.showsHorizontalScrollIndicator = NO;
}
return _tableView;
}
重复代码块
这已经是够灾难的了,如果在项目周期中再遇到某个或者多个页面设计UI设计频繁的变动,那简直不敢想象,哪怕每次只是一点小小的改动,也可能需要修改上面重复代码块中UITableViewDelegate的多个地方,如新插入一行row或者一个section,所有涉及到section或者row的地方或许都需要更改!!!
OK,我现在可以回答上面的问题了,这边文章到底是做什么的?解决的痛点在那里?--- 解耦封装、简化代码、适者生存!
从重复代码块我们可以看出,一般会让控制器充当UITableView的dataSource和delegate,那么既然要解耦,那么就要打破思维定式,让UITableView自己做自己的dataSource和delegate!毕竟我的地盘我做主嘛!其次将UITableViewCell进行block封装对象化,让其所有的属性都自我集成。
一、首先来看UITableViewCell的封装 -- ZTCoolTableViewCell
@class UIView;
@class UITableViewCell;
@class UITableView;
@class NSIndexPath; // 创建section头部 Or section底部的block
typedef UIView *(^buildCell)(UITableView *tableView, NSInteger section);
// 创建section对应的row数据源的block
typedef UITableViewCell *(^buildCellInfo)(UITableView *tableView, NSIndexPath *indexPath);
// 点击section对应row的事件block
typedef void (^clickBlock)(UITableView *tableView, NSIndexPath *indexPath);
// ZTCoolTableCellList刷新block
typedef void (^refreshBlock)(); @interface ZTCoolTableViewCell : NSObject // 行高度
@property (nonatomic,assign) CGFloat height;
// 构造行
@property (nonatomic, copy) buildCell buildCell; @end @interface ZTCoolTableCellList : NSObject // 头部
@property (nonatomic,strong) ZTCoolTableViewCell * headCell;
// 底部
@property (nonatomic,strong) ZTCoolTableViewCell * footCell;
// 构造行
@property (nonatomic,copy) buildCellInfo buildCellInfo;
// 列高(等于0表示自适应)
@property (nonatomic,assign) CGFloat cellHeigth;
// 行数量
@property (nonatomic,assign) NSInteger cellCount;
// 行点击事件
@property(nonatomic,copy) clickBlock clickBlock;
// 刷新事件(适用于需要动态更新tableview布局:新增或者删减section/row)
@property(nonatomic,copy) refreshBlock refreshBlock;
// 行标识
@property (nonatomic,copy) NSString *identifier;
@property (nonatomic,copy) NSString *xibName; // 简单初始化 (单行cell)
- (ZTCoolTableCellList *)initSimpleCell:(CGFloat)cellHeight
buildCell:(buildCellInfo)buildCell
clickCell:(clickBlock)clickCell; // 复杂初始化 - 不可刷新
- (ZTCoolTableCellList *)initComplexCellNoRefresh:(CGFloat)headHeigth
buildHead:(buildCell)buildHead
footHeight:(CGFloat)footHeight
buildFoot:(buildCell)buildFoot
cellHeight:(CGFloat)cellHeight
buildCell:(buildCellInfo)buildCell
clickCell:(clickBlock)clickCell
cellCount:(NSInteger)cellCount
identifier:(NSString *)identifier
xibName:(NSString *)xibName; // 复杂初始化 - 可刷新
- (ZTCoolTableCellList *)initComplexCellHasRefresh:(CGFloat)headHeigth
buildHead:(buildCell)buildHead
footHeight:(CGFloat)footHeight
buildFoot:(buildCell)buildFoot
cellHeight:(CGFloat)cellHeight
buildCell:(buildCellInfo)buildCell
clickCell:(clickBlock)clickCell
refreshCell:(refreshBlock)refreshCell
cellCount:(NSInteger)cellCount
identifier:(NSString *)identifier
xibName:(NSString *)xibName;
@end
.h文件
@implementation ZTCoolTableViewCell @end @implementation ZTCoolTableCellList // 简单初始化
- (ZTCoolTableCellList *)initSimpleCell:(CGFloat)cellHeight
buildCell:(buildCellInfo)buildCell
clickCell:(clickBlock)clickCell{ return [self initComplexCellNoRefresh: buildHead:nil footHeight: buildFoot:nil cellHeight:cellHeight buildCell:buildCell clickCell:clickCell cellCount: identifier:nil xibName:nil];
} // 复杂初始化 - 不可刷新
- (ZTCoolTableCellList *)initComplexCellNoRefresh:(CGFloat)headHeigth
buildHead:(buildCell)buildHead
footHeight:(CGFloat)footHeight
buildFoot:(buildCell)buildFoot
cellHeight:(CGFloat)cellHeight
buildCell:(buildCellInfo)buildCell
clickCell:(clickBlock)clickCell
cellCount:(NSInteger)cellCount
identifier:(NSString *)identifier
xibName:(NSString *)xibName{ if(headHeigth >){
self.headCell = [[ZTCoolTableViewCell alloc] init];
self.headCell.height = headHeigth;
self.headCell.buildCell = buildHead;
} if(footHeight >){
self.footCell = [[ZTCoolTableViewCell alloc] init];
self.footCell.height = footHeight;
self.footCell.buildCell = buildFoot;
} self.cellHeigth = cellHeight;
self.buildCellInfo = buildCell;
self.clickBlock = clickCell;
self.cellCount = cellCount;
self.identifier = identifier;
self.xibName = xibName; return self;
} // 复杂初始化 - 可刷新
- (ZTCoolTableCellList *)initComplexCellHasRefresh:(CGFloat)headHeigth
buildHead:(buildCell)buildHead
footHeight:(CGFloat)footHeight
buildFoot:(buildCell)buildFoot
cellHeight:(CGFloat)cellHeight
buildCell:(buildCellInfo)buildCell
clickCell:(clickBlock)clickCell
refreshCell:(refreshBlock)refreshCell
cellCount:(NSInteger)cellCount
identifier:(NSString *)identifier
xibName:(NSString *)xibName{ if(headHeigth >){
self.headCell = [[ZTCoolTableViewCell alloc] init];
self.headCell.height = headHeigth;
self.headCell.buildCell = buildHead;
} if(footHeight >){
self.footCell = [[ZTCoolTableViewCell alloc] init];
self.footCell.height = footHeight;
self.footCell.buildCell = buildFoot;
} self.cellHeigth = cellHeight;
self.buildCellInfo = buildCell;
self.clickBlock = clickCell; if(refreshCell){
self.refreshBlock = refreshCell;
} self.cellCount = cellCount;
self.identifier = identifier;
self.xibName = xibName; return self;
}
.m文件
二、让UITableView自己做自己的dataSource和delegate -- ZTCoolTableViewBase
@class ZTCoolTableCellList; @interface ZTCoolTableViewBase : UITableView <UITableViewDataSource, UITableViewDelegate> // UITableView的数据集合
@property (nonatomic,strong) NSMutableArray<ZTCoolTableCellList*> *arrayTableViewCellList; @end
.h文件
@implementation ZTCoolTableViewBase #pragma mark-hitTest
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
id view = [super hitTest:point withEvent:event];
if(![view isKindOfClass:[UITextField class]]){
[self endEditing:YES];
}
return view;
} #pragma mark - TableViewDelegate
// section头部高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:section];
if(cellList.headCell){
return cellList.headCell.height;
}else{
return 0.00001;
}
} // section底部高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:section];
if(cellList.footCell){
return cellList.footCell.height;
}else{
return 0.00001;
}
} // 有多少section
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [self.arrayTableViewCellList count];
} // 改变行的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:[indexPath section]];
if(cellList.cellHeigth == ){
UITableViewCell *cell = [self tableView:self cellForRowAtIndexPath:indexPath];
return cell.frame.size.height;
}else{
return cellList.cellHeigth;
}
} // 每个section有多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:section];
return cellList.cellCount;
} // 头部
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:section];
if(cellList.headCell.buildCell){
return cellList.headCell.buildCell(tableView,section);
}else{
return nil;
}
} // cell数据构造
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:[indexPath section]];
return cellList.buildCellInfo(tableView,indexPath);
} // 底部
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:section];
if(cellList.footCell.buildCell){
return cellList.footCell.buildCell(tableView,section);
}else{
return nil;
}
} // 选中某个项
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:[indexPath section]];
if(cellList.clickBlock){
return cellList.clickBlock(tableView,indexPath);
}
} - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0){
return YES;
}
.m文件
如此,我们便实现了UITableViewCell的对象化封装和Controller于UITableView数据源及代理的耦合。
那么如何实际运用呢?我们来举个例子,如下图,实现这样一个页面:


按照以前的思维,将控制器充当UITableView的dataSource和delegate,那么就会出现
_tableView.delegate = (id)self;
_tableView.dataSource = (id)self;
而且每个Controller页面都是实现的协议代理方法,一长串的重复代码!!!
那么现在有了新需求,需要动态的再第一个section和第二个section之间新增一个section,包括两行row,这就需要重新代码布局,涉及到了所有 row点击事件极有可能需要重新绑定section与row值,对于能躺着绝对不站着的懒程序猿来说,这简直不要太扎心!如果使用上面封装的设计去实现,简直不要太舒服!
一、声明对象
// 主界面容器UITableView
@property (nonatomic,strong) ZTCoolTableViewBase *tableView;
// 第一个section(个人资料、我的钱包)
@property (nonatomic,strong) ZTCoolTableCellList *firstCell;
// 第二个section(交易记录、联系客服、设置)
@property (nonatomic,strong) ZTCoolTableCellList *secondCell;
// 第三个section(私人日记、统计面板)
@property (nonatomic,strong) ZTCoolTableCellList *thirdCell;
二、设置UITableView数据源和代理
- (ZTCoolTableViewBase *)tableView{
if (!_tableView) {
CGRect rect = [UIScreen mainScreen].bounds;
_tableView = [[ZTCoolTableViewBase alloc] initWithFrame:rect style:UITableViewStyleGrouped];
_tableView.arrayTableViewCellList = [[NSMutableArray alloc] initWithObjects:
self.firstCell,
self.thirdCell,
nil];
_tableView.delegate = _tableView;
_tableView.dataSource = _tableView;
_tableView.sectionHeaderHeight = ;
_tableView.separatorColor = [UIColor groupTableViewBackgroundColor];
}
return _tableView;
}
其中:
// 设置UITableView的代理为自己
_tableView.delegate = _tableView;
// 设置UITableView的数据源为自己
_tableView.dataSource = _tableView; // 初始化UITableView的数据对象集合
_tableView.arrayTableViewCellList = [[NSMutableArray alloc] initWithObjects:
self.firstCell,
self.thirdCell,
nil];
三、懒加载数据集合
#pragma mark - firstCell
- (ZTCoolTableCellList *)firstCell{
if (!_firstCell) {
BIWeakObj(self)
static NSString *identifier = @"firstCell";
_firstCell = [[ZTCoolTableCellList alloc] init];
_firstCell = [_firstCell initComplexCellNoRefresh: buildHead:nil footHeight: buildFoot:nil cellHeight: buildCell:^UITableViewCell *(UITableView *tableView, NSIndexPath *indexPath) { UITableViewCell *cell = [selfWeak.tableView dequeueReusableCellWithIdentifier:identifier]; if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.font = [UIFont systemFontOfSize:14.0f]; if(indexPath.row == ){
cell.imageView.image = [UIImage imageNamed:@"ic_my_info"];
cell.textLabel.text = @"个人资料";
}
else{
cell.imageView.image = [UIImage imageNamed:@"ic_my_money"];
cell.textLabel.text = @"我的钱包";
}
} return cell; } clickCell:^(UITableView *tableView, NSIndexPath *indexPath) { [selfWeak clickCell:indexPath]; } cellCount: identifier:identifier xibName:nil];
}
return _firstCell;
}
firstCell
#pragma mark - secondCell
- (ZTCoolTableCellList *)secondCell{
if (!_secondCell) {
BIWeakObj(self)
static NSString *identifier = @"secondCell";
_secondCell = [[ZTCoolTableCellList alloc] init];
_secondCell = [_secondCell initComplexCellNoRefresh: buildHead:nil footHeight: buildFoot:nil cellHeight: buildCell:^UITableViewCell *(UITableView *tableView, NSIndexPath *indexPath) { UITableViewCell *cell = [selfWeak.tableView dequeueReusableCellWithIdentifier:identifier]; if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.font = [UIFont systemFontOfSize:14.0f]; if(indexPath.row == ){
cell.imageView.image = [UIImage imageNamed:@"ic_my_log"];
cell.textLabel.text = @"私人日记";
}
else{
cell.imageView.image = [UIImage imageNamed:@"ic_my_statistic"];
cell.textLabel.text = @"统计面板";
}
} return cell; } clickCell:^(UITableView *tableView, NSIndexPath *indexPath) { [selfWeak clickCell:indexPath]; } cellCount: identifier:identifier xibName:nil];
}
return _secondCell;
}
secondCell
#pragma mark - thirdCell
- (ZTCoolTableCellList *)thirdCell{
if (!_thirdCell) {
BIWeakObj(self)
static NSString *identifier = @"thirdCell";
_thirdCell = [[ZTCoolTableCellList alloc] init]; _thirdCell = [_thirdCell initComplexCellHasRefresh: buildHead:nil footHeight: buildFoot:nil cellHeight: buildCell:^UITableViewCell *(UITableView *tableView, NSIndexPath *indexPath) { UITableViewCell *cell = [selfWeak.tableView dequeueReusableCellWithIdentifier:identifier]; if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.font = [UIFont systemFontOfSize:14.0f]; if(indexPath.row == ){
cell.imageView.image = [UIImage imageNamed:@"ic_my_quotebill"];
cell.textLabel.text = @"交易记录";
}
else if(indexPath.row == ){
cell.imageView.image = [UIImage imageNamed:@"ic_my_service"];
cell.textLabel.text = @"联系客服";
}
else{
cell.imageView.image = [UIImage imageNamed:@"ic_my_setup"];
cell.textLabel.text = @"设置";
}
} return cell; } clickCell:^(UITableView *tableView, NSIndexPath *indexPath) { [selfWeak clickCell:indexPath]; } refreshCell:^{ [selfWeak.tableView.arrayTableViewCellList insertObject:selfWeak.secondCell atIndex:];
[selfWeak.tableView reloadData]; } cellCount: identifier:identifier xibName:nil];
}
return _thirdCell;
}
thirdCell
其中第三个cell可刷新(为了给第二个cell指定新增时的入口)这里是个block:
refreshCell:^{
[selfWeak.tableView.arrayTableViewCellList insertObject:selfWeak.secondCell atIndex:];
[selfWeak.tableView reloadData];
}
新增按钮点击事件:
- (void)addTableviewSection:(id)sender{
if(self.thirdCell.refreshBlock){
self.thirdCell.refreshBlock();
}
}
如此实现,在解耦的同时还能简化重复代码量,并且可以最小的代价cost适应频繁变化的UI设计!
PS:目前的封装只支持每个section块的每行row高度是一样的,如果存在不一致的需求,可在我的基础上进行二次封装变化,如果我的文章对您有些许帮助,帮忙点赞标星,如需转载,请说明出处,谢谢!
demo Github地址:https://github.com/BeckWang0912/ZTCoolTableView 喜欢就标个星星吧✨✨~~~✨✨^o^
玩转UITableView系列(一)--- 解耦封装、简化代码、适者生存!的更多相关文章
- 玩转UITableView
UITableView这个iOS开发中永远绕不开的UIView,那么就不可避免的要在多个页面多种场景下反复摩擦UITableView,就算是刚跳进火坑不久的iOS Developer也知道实现UITa ...
- 第40章 CAN—通讯实验—零死角玩转STM32-F429系列
第40章 CAN—通讯实验—零死角玩转STM32-F429系列 第40章 CAN—通讯实验 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视 ...
- 第48章 MDK的编译过程及文件类型全解—零死角玩转STM32-F429系列
第48章 MDK的编译过程及文件类型全解 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.co ...
- 第25章 串行FLASH文件系统FatFs—零死角玩转STM32-F429系列
第25章 串行FLASH文件系统FatFs 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.c ...
- Windows玩转Kubernetes系列3-Centos安装K8S
以往文章参考: Windows玩转Kubernetes系列1-VirtualBox安装Centos Windows玩转Kubernetes系列2-Centos安装Docker 安装K8S yum in ...
- SpringBoot系列——MyBatis-Plus整合封装
前言 MyBatis-Plus是一款MyBatis的增强工具(简称MP),为简化开发.提高效率,但我们并没有直接使用MP的CRUD接口,而是在原来的基础上封装一层通用代码,单表继承我们的通用代码,实现 ...
- 循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码
VUE+Element 前端应用,比较不错的一点就是界面组件化,我们可以根据重用的指导方针,把界面内容拆分为各个不同的组合,每一个模块可以是一个组件,也可以是多个组件的综合体,而且这一个过程非常方便. ...
- iOS深入学习(UITableView系列4:使用xib自定义cell)
可以通过继承UITableViewCell重新自定义cell,可以像下面一样通过代码来自定义cell,但是手写代码总是很浪费时间, ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...
- 微软云平台媒体服务实践系列 2- 使用动态封装为iOS, Android , Windows 等多平台提供视频点播(VoD)方案
文章微软云平台媒体服务实践系列 1- 使用静态封装为iOS, Android 设备实现点播(VoD)方案 介绍了如何针对少数iOS, Android 客户端的场景,出于节约成本的目的使用媒体服务的静 ...
随机推荐
- hdu1213 How Many Tables 并查集的简单应用
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1213 简单的并查集 代码: #include<iostream> #include< ...
- 搭建腾讯云Linux服务器(Centos6)入门教程
搭建腾讯云我们需要准备WinSCP,支持文件上传和下载的客户端,界面操作,很方便快捷,有这个可以不用搭建SVN哦! SecureCRT 7.3,这个是很不错的Linux远程客户端哦,可以去CSDN下载 ...
- 【2017-05-22】WebForm内置对象:Application和ViewState、Repeater的Command用法
一.内置对象 1.Application 存贮在服务器端,占用服务器内存生命周期:永久 所有人访问的都是这一个对象 传值:传的是object类型可以传对象. string s =TextBox1.Te ...
- MYBATIS 简单整理与回顾
这两天简单整理了一下MyBatis 相关api和jar包这里提供一个下载地址,免得找了 链接:http://pan.baidu.com/s/1jIl1KaE 密码:d2yl A.简单搭建跑项目 2.进 ...
- 你会python不?当你听到这个问题要谨慎回答!!!
问:你会python不? 答:python啊,略微有点小研究,虽然不精通,但是写写网络小爬虫,搜集搜集网络资源,学习视频什么的,还是手到擒来的...(for循环一小时中) 旁白:然而你没有明白人家的真 ...
- [原创]MongoDB_Sharding
Mongo Sharding:本示例搭建了三个副本集作为三个分片的sharding集群,其中master,slave,factershi三台同网段的内网主机.前期规划和原理分析省略,可根据具体配置推导 ...
- R语言学习路线和常用数据挖掘包(转)
对于初学R语言的人,最常见的方式是:遇到不会的地方,就跑到论坛上吼一嗓子,然后欣然or悲伤的离去,一直到遇到下一个问题再回来.当然,这不是最好的学习方式,最好的方式是——看书.目前,市面上介绍R语言的 ...
- 挂载mount
mount 1 挂载mount 基本概念 挂载:将额外文件系统与根文件系统现存的目录建立起关联关系,进而使得此目录做为其它文件访问入库的行为 卸载:为解除关联关系的过程 注意:挂载点下原有的文件在挂载 ...
- 从零开始——PowerShell应用入门(全例子入门讲解)
学习一门技术,不止要会,还要善用,例子就是带你快速入门的最佳利器.本文就是要用例子,不,大量的例子来带你走进PowerShell应用世界. 本文主要介绍一些PowerShell入门的基础知识,对技术小 ...
- DOUAudioStreamer 中kqueue的应用
DOUAudioStreamer是一个基于Core Audio的流式音频播放器,其中的DOUAudioEventLoop通过kqueue来控制音频的各种状态. kqueue简介(详情请看官方manua ...