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

二、UITableViewDelegate
通常都要为UITableView设置代理对象(delegate),以便在UITableView触发一下事件时做出相应的处理,比如选中了某一行。凡是遵守了UITableViewDelegate协议的OC对象,都可以是UITableView的代理对象。一般会让控制器充当UITableView的dataSource和delegate。查看源码:

@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 0;
}- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{return 0.000001;
}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{return 0;
}- (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(0, -64, KS_Width, KS_Heigth+64) style:UITableViewStyleGrouped];_tableView.delegate = (id)self;_tableView.dataSource = (id)self;[_tableView setSectionHeaderHeight:0];_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)cellHeightbuildCell:(buildCellInfo)buildCellclickCell:(clickBlock)clickCell;// 复杂初始化 - 不可刷新
- (ZTCoolTableCellList *)initComplexCellNoRefresh:(CGFloat)headHeigthbuildHead:(buildCell)buildHeadfootHeight:(CGFloat)footHeightbuildFoot:(buildCell)buildFootcellHeight:(CGFloat)cellHeightbuildCell:(buildCellInfo)buildCellclickCell:(clickBlock)clickCellcellCount:(NSInteger)cellCountidentifier:(NSString *)identifierxibName:(NSString *)xibName;// 复杂初始化 - 可刷新
- (ZTCoolTableCellList *)initComplexCellHasRefresh:(CGFloat)headHeigthbuildHead:(buildCell)buildHeadfootHeight:(CGFloat)footHeightbuildFoot:(buildCell)buildFootcellHeight:(CGFloat)cellHeightbuildCell:(buildCellInfo)buildCellclickCell:(clickBlock)clickCellrefreshCell:(refreshBlock)refreshCellcellCount:(NSInteger)cellCountidentifier:(NSString *)identifierxibName:(NSString *)xibName;
@end

.h文件

@implementation ZTCoolTableViewCell@end@implementation ZTCoolTableCellList// 简单初始化
- (ZTCoolTableCellList *)initSimpleCell:(CGFloat)cellHeightbuildCell:(buildCellInfo)buildCellclickCell:(clickBlock)clickCell{return [self initComplexCellNoRefresh:0 buildHead:nil footHeight:0 buildFoot:nil cellHeight:cellHeight buildCell:buildCell clickCell:clickCell cellCount:1 identifier:nil xibName:nil];
}// 复杂初始化 - 不可刷新
- (ZTCoolTableCellList *)initComplexCellNoRefresh:(CGFloat)headHeigthbuildHead:(buildCell)buildHeadfootHeight:(CGFloat)footHeightbuildFoot:(buildCell)buildFootcellHeight:(CGFloat)cellHeightbuildCell:(buildCellInfo)buildCellclickCell:(clickBlock)clickCellcellCount:(NSInteger)cellCountidentifier:(NSString *)identifierxibName:(NSString *)xibName{if(headHeigth >0){self.headCell = [[ZTCoolTableViewCell alloc] init];self.headCell.height = headHeigth;self.headCell.buildCell = buildHead;}if(footHeight >0){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)headHeigthbuildHead:(buildCell)buildHeadfootHeight:(CGFloat)footHeightbuildFoot:(buildCell)buildFootcellHeight:(CGFloat)cellHeightbuildCell:(buildCellInfo)buildCellclickCell:(clickBlock)clickCellrefreshCell:(refreshBlock)refreshCellcellCount:(NSInteger)cellCountidentifier:(NSString *)identifierxibName:(NSString *)xibName{if(headHeigth >0){self.headCell = [[ZTCoolTableViewCell alloc] init];self.headCell.height = headHeigth;self.headCell.buildCell = buildHead;}if(footHeight >0){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 == 0){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 = 0;_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:0 buildHead:nil footHeight:10 buildFoot:nil cellHeight:44 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 == 0){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:2 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:0 buildHead:nil footHeight:10 buildFoot:nil cellHeight:44 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 == 0){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:2 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:0 buildHead:nil footHeight:0 buildFoot:nil cellHeight:44 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 == 0){cell.imageView.image = [UIImage imageNamed:@"ic_my_quotebill"];cell.textLabel.text = @"交易记录";}else if(indexPath.row == 1){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:1];[selfWeak.tableView reloadData];} cellCount:3 identifier:identifier xibName:nil];}return _thirdCell;
}

thirdCell

其中第三个cell可刷新(为了给第二个cell指定新增时的入口)这里是个block:

refreshCell:^{[selfWeak.tableView.arrayTableViewCellList insertObject:selfWeak.secondCell atIndex:1];[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^

转载于:https://www.cnblogs.com/beckwang0912/p/7082826.html

玩转UITableView相关推荐

  1. mysql分组后组内排名_SQL实现group by 分组后组内排序

    在一个月黑风高的夜晚,自己无聊学习的SQL的时候,练习,突发奇想的想实现一个功能查询,一张成绩表有如下字段,班级ID,英语成绩,数据成绩,语文成绩如下图 实现 查询出 每个班级英语成绩最高的前两名的记 ...

  2. 为什么在iOS7中,UITableView顶部的UITableViewStyleGrouped样式具有额外的填充

    本文翻译自:Why is there extra padding at the top of my UITableView with style UITableViewStyleGrouped in ...

  3. 玩转iOS开发:NSURLSession讲解(三)

    文章分享至我的个人技术博客: https://cainluo.github.io/14986211698053.html 前言 虽然前面两讲都是说了NSURLSession的一些理论上的知识, 但我们 ...

  4. UITableViewCell中嵌套UITableView,用UITextView加载HTML数据

    UIWebView可以加载HTML数据毋庸置疑,但是我们也都知道webView的性能不高,单个webView的话可能还好,但是如果我的tableviewcell中的内容是HTML数据,这时候再用web ...

  5. 玩转iOS开发:iOS 11 新特性《UIKit新特性的基本认识》

    文章分享至我的个人技术博客: https://cainluo.github.io/15099354591154.html 前两篇, 我们讲了Xcode 9的一些新特性, 可以更加方便我们去写" ...

  6. iOS UITableView+FDTemplateLayoutCell 配合AutoLayout分分钟教你实现类似微信朋友圈的动态高度自适应

    11.30日更新,实现了简单的微信朋友圈,点赞,评论,图片,高度自适应,下拉展开等各种效果Demo 点击打开链接 11.10更新 这种高度自适应的Label切记一定要加上这个属性 preferredM ...

  7. 初试linux编译(ubuntu+vim)+玩转智能蛇

    一.初试linux编译(ubuntu+vim) 步骤: ①下载vmware15+ubuntu桌面版映像 ②安装ubuntu ③下载vim+gcc 在ubuntu终端输入: sudo apt-get i ...

  8. 一步步玩pcDuino3--mmc下的裸机流水灯

    第一部分是玩pcduino3下的裸机.这个过程能够让我们更好的理解嵌入式系统,熟悉我们使用的这个平台. 首先介绍下开发环境: 虚拟机:VMware® Workstation 10.0.2 build- ...

  9. 玩转Spring Cloud之配置中心(config server config client)

    玩转Spring Cloud之配置中心(config server &config client)  本文内容导航: 一.搭建配置服务中心(config server) 1.1.git方式 1 ...

最新文章

  1. python【蓝桥杯vip练习题库】ADV-172身份证排序
  2. [Part 3]API对接,这些坑你一定掉过!
  3. MacBook 如何通过命令终端进入 U 盘内的目录
  4. 工作135:引用当前组件下面的方法是混入
  5. ios label文字行间距_iOS- 设置label的行间距字体间距
  6. 运维与节能:数据中心绿色运维技术研讨会召开
  7. NuGet镜像上线试运行
  8. mybatis传多个参数实例
  9. 计算机round是什么函数,round函数怎么用例子
  10. 【202209秋招软开银行面试C++】
  11. 围观知乎真福利话题,放松一下。
  12. IE和Firefox浏览器CSS网页布局不同点
  13. linux设备驱动七(时间、延迟及延缓操作)
  14. 2. Switch能否用String做参数?
  15. 什么是大数据(个人理解)
  16. Android 11 强制分区存储
  17. ADS-WK11-Review of Programming Contest Rules-回溯剪枝
  18. devops 三十六计_要避免的6个DevOps错误
  19. 提升搜索引擎的友好度只要五个步骤
  20. eclipse photon 的汉化

热门文章

  1. SpringBoot中访问Thymeleaf提示:元素类型 meta 必须由匹配的结束标记终止。
  2. 根可达算法的根_GC垃圾回收算法
  3. 获取mysql所有用户权限_python 获取mysql数据库列表以及用户权限
  4. 科普丨营销人,还不知道行为触发的话你就 OUT 了!
  5. 新认知,新力量!神策 2021 数据驱动大会来了
  6. 神策数据张涛:微信生态数字化运营解决方案
  7. 独家专访 | 红布林(Plum​)庞博:万亿元二手时尚交易蓝海的生存法则
  8. CentOs7下lnmp环境安装
  9. OpenGL环境下的射线选择
  10. BREW应用的分发流程