iOS 实现多个可变 cell 复杂界面的制作
来源:飘游人
www.jianshu.com/p/9fc838d46f5e
如有好文章投稿,请点击 → 这里了解详情
在日常的开发中,有时会遇到内容块比较多,且又可变的界面:
这个界面中有些内容块是固定出现的,比如最上面的商品详情图片、商品名称、价格等。而有些内容块则是不一定出现的,比如促销(显然不是每个商品都有促销)、已选规格(有的商品没有规格)、店铺信息(有的商品属于自营,就没有店铺)等。还有些内容要根据情况进行变化,比如评论,这里最多列出4条评论,如果没有评论,则显示“暂无评论”且不显示“查看所有评论”按钮。
对于这样的界面,相信很多人第一感觉会用TableView来做,因为中间要列出评论内容,这个用TableView的cell来填充比较合适。但如何处理评论内容之外的其他内容呢?我之前的做法是,评论内容之上的用HeaderView做,下面的用FooterView做,虽然最终实现了功能,但做起来十分麻烦。布局我是用Auto Layout来做的,由于Auto Layout本身的特点,代码中就涉及很多判断处理。比如“已选规格”块,最开始的是有一个和“促销”内容块的顶部间距约束,但“促销”内容块不一定会有,就得根据情况,调整“已选规格”块本身的约束(例如让其顶部间距约束指向“价格”内容块)。同样,“促销”内容块本身也需要类似的处理,如果没有“促销”时,要隐藏自己,而隐藏自己最简单的办法,就是将自己的高度约束设置为0(因为它还有底部到“已选规格”的间距约束,不能随意将自身移除,否则“已选规格”相关的约束得再进行调整)。
此外,还有一个麻烦的问题。界面刚进来的时候,是需要请求网络数据,这时界面就要显示成一个初始状态,而显然初始状态有些内容块是不应该显示的,比如促销,只有完成了数据请求,才能知道是否有促销,有的话才显示促销内容;比如评论,初始时应该显示成“暂无评论”,数据请求完成后,才显示相应的内容。这样,我们需要处理初始进入和数据请求完成两种状态下各个内容块的显示,十分复杂繁琐。
总结来说,用TableView的 HeaderView + 评论内容cell + FooterView + Auto Layout 的方式会带来如下问题:
约束本身需要依赖其他View的,而所依赖的View又是可变的内容块,会导致约束需要繁琐的判断修改增删
需要处理初始进入和数据请求完成两种状态的界面展示,使代码更加复杂繁琐
需要额外计算相应内容的高度,以更新HeaderView、FooterView的高度
可见,这种方式并不是理想的解决方案。可能有人会说,那不要用Auto Layout,直接操作frame来布局就好,这样或许能减少一些麻烦,但总体上并没有减少复杂度。也有人说,直接用ScrollView来做,这样的话,所有的内容包括评论内容的cell,都得自己手动拼接,可以想象这种做法也是比较麻烦的。所以,我们得另辟蹊径,使用其他方法来达到目的。下面就为大家介绍一种比较简便的做法,这种做法也是一个前同事分享给我的,我就借花献佛,分享给大家。
我们还是用TableView来做这个界面,和之前不同的是,我们把每一个可变内容块做成一个独立的cell,cell的粒度可以自行控制,比如可以用一个cell囊括商品图片、标题、副标题、价格,也可以拆得更细,图片、标题、副标题、价格都各自对应一个cell。这里我们选择后者,因为图片内容块,我们需要按屏幕宽度等比例拉伸;标题、副标题的文字内容可能是一行,也可能是两行,高度可变,用单独的cell来控制会更简单明了。
下面先定义好各种类型的cell:
//基础cell,这里为了演示简便,定义这个cell,其他cell继承自这个cell
@interface MultipleVariantBasicTableViewCell : UITableViewCell
@property (nonatomic, weak) UILabel *titleTextLabel;
@end
//滚动图片
@interface CycleImagesTableViewCell : MultipleVariantBasicTableViewCell
@end
//正标题
@interface MainTitleTableViewCell : MultipleVariantBasicTableViewCell
@end
//副标题
@interface SubTitleTableViewCell : MultipleVariantBasicTableViewCell
@end
//价格
@interface PriceTableViewCell : MultipleVariantBasicTableViewCell
@end
// ...其他内容块的cell声明
// 各种内容块cell的实现,这里为了演示简便,cell中就只放了一个Label
@implementation MultipleVariantBasicTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
label.numberOfLines = 0;
[self.contentView addSubview:label];
self.titleTextLabel = label;
}
return self;
}
@end
@implementation CycleImagesTableViewCell
@end
@implementation MainTitleTableViewCell
@end
// ...其他内容块的cell实现
// 评论内容cell使用Auto Layout,配合iOS 8 TableView的自动算高,实现内容自适应
@implementation CommentContentTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.titleTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.titleTextLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 8;
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:self.titleTextLabelattribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.contentViewattribute:NSLayoutAttributeLeading multiplier:1.0f constant:4.0f];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:self.titleTextLabelattribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.contentViewattribute:NSLayoutAttributeTrailing multiplier:1.0f constant:-4.0f];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.titleTextLabelattribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTopmultiplier:1.0f constant:4.0f];
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:self.titleTextLabelattribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.contentViewattribute:NSLayoutAttributeBottom multiplier:1.0f constant:-4.0f];
[self.contentView addConstraints:@[leftConstraint, rightConstraint, topConstraint, bottomConstraint]];
}
return self;
}
@end
接下来就是重点,就是如何来控制显示哪些cell及cell显示的数量。这一步如果处理不好,也会使开发变得复杂。如下面的方式:
// 加载完数据
self.cellCount = 0;
if (存在促销) {
self.cellCount++;
}
if (存在规格) {
self.cellCount++;
}
......
如果以这种方式来记录cell的数量,那么后续cell的展示、点击判断等都会很麻烦。这里我们采用的方式是,使用单独的类(作为一种数据结构)来保存所要展示的cell信息。
// SKRow.h
@interface SKRow : NSObject
@property (nonatomic, copy) NSString *cellIdentifier;
@property (nonatomic, strong) id data;
@property (nonatomic, assign) float rowHeight;
- (instancetype)initWithCellIdentifier:(NSString *)cellIdentifier
data:(id)data
rowHeight:(float)rowHeight;
@end
// SKRow.m
#import "SKRow.h"
@implementation SKRow
- (instancetype)initWithCellIdentifier:(NSString *)cellIdentifier data:(id)data rowHeight:(float)rowHeight {
if (self = [super init]) {
self.cellIdentifier = cellIdentifier;
self.data = data;
self.rowHeight = rowHeight;
}
return self;
}
@end
SKRow用来存储每个cell所需的信息,包括重用标识、数据项、高度。接下来,我们就开始拼接cell信息。
@interface ViewController ()
@property (nonatomic, strong) NSMutableArray *> *tableSections;
@end
self.tableSections = [NSMutableArray array];
/* 初始加载数据
* 初始化时,只显示滚动图片、价格、评论头、无评论
*/
// 滚动图片(宽高保持比例)
SKRow *cycleImagesRow = [[SKRow alloc] initWithCellIdentifier:@"CycleImagesCellIdentifier" data:@[@"滚动图片地址"]rowHeight:120*[UIScreen mainScreen].bounds.size.width / 320.f];
// 价格
SKRow *priceRow = [[SKRow alloc] initWithCellIdentifier:@"PriceCellIdentifier" data:@"0" rowHeight:44];
[self.tableSections addObject:@[cycleImagesRow, priceRow]];
// 评论头
SKRow *commentSummaryRow = [[SKRow alloc] initWithCellIdentifier:@"CommentSummaryCellIdentifier"data:@{@"title":@"商品评价", @"count":@"0"} rowHeight:44];
// 无评论
SKRow *noCommentRow = [[SKRow alloc] initWithCellIdentifier:@"NoCommentCellIdentifier" data:@"暂无评论"rowHeight:44];
[self.tableSections addObject:@[commentSummaryRow, noCommentRow]];
以上是初始状态时要显示的cell,我们在ViewController中声明一个数组,用来存储TableView各个section要显示的cell信息。这里我们将cell分成不同的section,实际中,要不要分,分成几个section都可以自行决定。初始状态我们有两个section,第一个section用于显示基本信息,第二个section用于显示评论信息,这样就完成了cell信息的拼接,接下来就是显示:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// 这里可以通过判断cellIdentifier来区分处理各种不同的cell,cell所需的数据从row.data上获取
SKRow *row = self.tableSections[indexPath.section][indexPath.row];
if ([row.cellIdentifier isEqualToString:@"CycleImagesCellIdentifier"]) {
CycleImagesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifierforIndexPath:indexPath];
NSArray *urlStringArray = row.data;
cell.titleTextLabel.text = [urlStringArray componentsJoinedByString:@"\n"];
return cell;
} else if ([row.cellIdentifier isEqualToString:@"MainTitleCellIdentifier"]) {
MainTitleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifierforIndexPath:indexPath];
cell.titleTextLabel.text = row.data;
return cell;
} else if ([row.cellIdentifier isEqualToString:@"PriceCellIdentifier"]) {
PriceTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifier forIndexPath:indexPath];
cell.titleTextLabel.text = [NSString stringWithFormat:@"¥%@", row.data];
return cell;
} else if ([row.cellIdentifier isEqualToString:@"SalePromotionCellIdentifier"]) {
SalePromotionTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifierforIndexPath:indexPath];
NSArray *salePromotionStringArray = row.data;
cell.titleTextLabel.text = [salePromotionStringArray componentsJoinedByString:@"\n"];
return cell;
} else if ([row.cellIdentifier isEqualToString:@"SpecificationCellIdentifier"]) {
SpecificationTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifierforIndexPath:indexPath];
cell.titleTextLabel.text = [NSString stringWithFormat:@"已选:%@", row.data];
return cell;
} else if ([row.cellIdentifier isEqualToString:@"CommentSummaryCellIdentifier"]) {
CommentSummaryTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifierforIndexPath:indexPath];
NSDictionary *commentSummary = row.data;
cell.titleTextLabel.text = [NSString stringWithFormat:@"%@(%@)", commentSummary[@"title"],commentSummary[@"count"]];
return cell;
} else if ([row.cellIdentifier isEqualToString:@"CommentContentCellIdentifier"]) {
CommentContentTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifierforIndexPath:indexPath];
cell.titleTextLabel.text = row.data;
return cell;
} else if ([row.cellIdentifier isEqualToString:@"AllCommentCellIdentifier"]) {
AllCommentTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifierforIndexPath:indexPath];
cell.titleTextLabel.text = row.data;
return cell;
} else if ([row.cellIdentifier isEqualToString:@"NoCommentCellIdentifier"]) {
NoCommentTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifierforIndexPath:indexPath];
cell.titleTextLabel.text = row.data;
return cell;
}
return nil;
}
上面的代码进行了删减,没有处理所有类型。虽然稍嫌冗长,但是逻辑非常简单,就是获取cell信息,根据重用标识来区分不同类型的内容块,将数据处理后放到cell中展示。
例如,对于商品图片,因为是滚动图片,滚动图片可以有多张,前面我们传入的数据就是数组data:@[@"滚动图片地址"]。后面获取到数据后,cell.titleTextLabel.text = [urlStringArray componentsJoinedByString:@"\n"];,出于演示,商品图片cell我们只放了一个Label,所以只是简单的将地址信息分行显示出来。在实际的开发中,可以放入一个图片滚动显示控件,并将图片地址的数组数据传给控件展示。
其他类型的cell处理也是大同小异,出于演示的原因,都只是简单的数据处理展示。当然,别忘了,设置一下TableView相关的dataSource和delegate:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
SKRow *row = self.tableSections[indexPath.section][indexPath.row];
return row.rowHeight;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.tableSections.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.tableSections[section].count;
}
这样我们就完成了初始状态时界面的展示
完成了cell的显示处理,接下来我们来模拟一下网络请求数据后,界面如何显示所需的cell
self.tableSections = [NSMutableArray array];
NSMutableArray *section1 = [NSMutableArray array];
// 滚动图片(宽高保持比例)
SKRow *cycleImagesRow = [[SKRow alloc] initWithCellIdentifier:@"CycleImagesCellIdentifier" data:@[@"滚动图片地址1",@"滚动图片地址2", @"滚动图片地址3"] rowHeight:120*[UIScreen mainScreen].bounds.size.width / 320.f];
// 主标题
SKRow *mainTitleRow = [[SKRow alloc] initWithCellIdentifier:@"MainTitleCellIdentifier" data:@"商品名称" rowHeight:44];
// 副标题
SKRow *subTitleRow = [[SKRow alloc] initWithCellIdentifier:@"SubTitleCellIdentifier" data:@"节日促销,快来买啊"rowHeight:44];
// 价格
SKRow *priceRow = [[SKRow alloc] initWithCellIdentifier:@"PriceCellIdentifier" data:@(arc4random()) rowHeight:44];
[section1 addObjectsFromArray:@[cycleImagesRow, mainTitleRow, subTitleRow, priceRow]];
// 促销(随机出现)
if (arc4random() % 2 == 0) {
SKRow *salePromotionRow = [[SKRow alloc] initWithCellIdentifier:@"SalePromotionCellIdentifier" data:@[@"促销信息1",@"促销信息2", @"促销信息3"] rowHeight:44];
[section1 addObject:salePromotionRow];
}
[self.tableSections addObject:section1];
NSMutableArray *section2 = [NSMutableArray array];
// 规格(随机出现)
if (arc4random() % 2 == 0) {
SKRow *specificationRow = [[SKRow alloc] initWithCellIdentifier:@"SpecificationCellIdentifier" data:@"银色,13.3英寸"rowHeight:44];
[section2 addObject:specificationRow];
}
if (section2.count > 0) {
[self.tableSections addObject:section2];
}
NSMutableArray *section3 = [NSMutableArray array];
NSArray *commentArray = [NSMutableArray array];
// 评论内容数据(随机出现)
if (arc4random() % 2 == 0) {
commentArray = @[@"评论内容1", @"评论内容2", @"2016年6月,苹果系统iOS 10正式亮相,苹果为iOS 10带来了十大项更新。2016年6月13日,苹果开发者大会WWDC在旧金山召开,会议宣布iOS 10的测试版在2016年夏天推出,正式版将在秋季发布。2016年9月7日,苹果发布iOS 10。iOS10正式版于9月13日(北京时间9月14日凌晨一点)全面推送。", @"评论内容4"];
}
// 评论头
SKRow *commentSummaryRow = [[SKRow alloc] initWithCellIdentifier:@"CommentSummaryCellIdentifier"data:@{@"title":@"商品评价", @"count":@(commentArray.count)} rowHeight:44];
[section3 addObject:commentSummaryRow];
if (commentArray.count > 0) {
for (NSString *commentString in commentArray) {
// 评论内容需要自适应高度,高度值指定为UITableViewAutomaticDimension
SKRow *commentContentRow = [[SKRow alloc] initWithCellIdentifier:@"CommentContentCellIdentifier"data:commentString rowHeight:UITableViewAutomaticDimension];
[section3 addObject:commentContentRow];
}
// 查看所有评论
SKRow *allCommentRow = [[SKRow alloc] initWithCellIdentifier:@"AllCommentCellIdentifier" data:@"查看所有评论"rowHeight:44];
[section3 addObject:allCommentRow];
} else {
// 无评论
SKRow *noCommentRow = [[SKRow alloc] initWithCellIdentifier:@"NoCommentCellIdentifier" data:@"暂无评论"rowHeight:44];
[section3 addObject:noCommentRow];
}
[self.tableSections addObject:section3];
[self.tableView reloadData];
上面的代码同样比较冗长,但逻辑也同样十分简单。按显示顺序拼凑cell数据,有些不一定显示的内容块,如促销,则随机判断,如果显示,将数据加入到section数组中[section1 addObject:salePromotionRow];。其他类型的cell也是类似的,不再赘述。要注意的是,评论内容的文本可能有多行,我们将它的cell高设置为UITableViewAutomaticDimension:
[[SKRow alloc] initWithCellIdentifier:@"CommentContentCellIdentifier" data:commentString rowHeight:UITableViewAutomaticDimension];
由于评论内容cell我们使用了Auto Layout,这样就可以利用iOS 8 TableView的新特性,自动计算cell的高度。拼接完数据后,只要调用[self.tableView reloadData];让TableView重新加载即可。
好了,这样就大功告成
最终效果
使用上述方式制作这种内容块可变的界面虽然写起来较为啰嗦,但有如下优点:
逻辑清晰简单,易于理解,视图间不存在像先前HeaderView + Auto Layout + FooterView那种复杂的依赖,内容块的显示与否处理非常简便
易于调整。例如调换内容块的顺序,只要移动下拼凑cell数据的代码顺序即可
易于扩展增加新的内容块。要增加新的内容块,只需创建新的cell,在数据拼接时,增加拼接新cell类型的数据代码,同样在显示的地方增加显示新cell类型的代码即可,几乎不需要修改原有的逻辑
最后,附上Demo工程代码(https://github.com/kelystor/MultipleVariantCell)。注意,这个工程是用XCode 8创建的,低版本的XCode可能运行会有问题(XCode 8的storyboard默认好像不兼容老版本),示例是基于iOS 8,如果要兼容老版本,请自行修改(主要是涉及cell自动算高的部分)。
相关文章:

做销售如何跟单,逼单!共20招!(转)
逼单是整个销售业务过程中最重要的一个环节。如果逼单失败你的整个业务就会失败,其实整个业务过程就是一个“逼”的过程,逼要掌握技巧,不要太操之过急,也不要慢条斯理,应该张弛有度,步步为营,也…

how to write Makefile
http://www.cnblogs.com/aoyihuashao/archive/2010/01/18/1650865.html转载于:https://www.cnblogs.com/hSheng/archive/2013/03/27/2985145.html

职称计算机word2007难吗,职称计算机word2007原题
全国职称计算机考试题库——word2007模块word2007模块第1套试题1.修改当前文本窗体类型为"数字",默认数字为"5"。2.设置用户在键入“中兴瑞典”时自动替换为“中星睿典”。3.将当前图片位置改为四周型环绕。4.用WORD的插入技术,插入如下字符:④…
iOS 9 通用链接(Universal Links)
来源:iOS_小松哥 www.jianshu.com/p/734c3eff8feb 如有好文章投稿,请点击 → 这里了解详情 什么是Universal Links? 在iOS9之前,对于从各种从浏览器、Safari中唤醒APP的需求,我们通常只能使用scheme。但是这种方式需要提前判断系…
为什么你的工作经验不值钱
前言 每年的三月到六月,都是招聘高峰,除了大量的应届毕业生涌入社会之外,还有一些工作了一两年尚未找到稳定归属感的人,也会开始投递简历,是的,基本都是在拿了年终奖之后。作为前端技术主管,有幸…

http://bbs.phome.net/showthread-13-45519-0.html
适用于想使用EmpireCMS建站的用户;EmpireCMS零基础的用户;会使用Dreamweaver设计简单表格式模板的用户。教程在线观看: 安装实例教程: http://www.phome.net/doc/ecmsedu/rm/install.htm入门实例教程: http://www.phom…

计算机32位操作系统最大识别到内存,win7 32位系统可以支持多大的内存_win7 的32位系统最大支持多少g的内存...
大家都知道win7系统有32位和64位之分,而两者的安装配置是不一样的,一般4G内存的我们安装64位的系统,但是很多用户不知道win7 32位系统可以支持多大的内存,这就给大家分享一下win7 的32位系统最大支持多少g的内存吧。一、内存和操作…
33个2017年必须了解的iOS开源库
原文 本文翻译自Medium,原作者为 Paweł Białecki 照片版权:(Unsplash/Markus Pe) 你好,iOS 开发者们!我的名字叫 Paweł,我是一个独立 iOS 开发者,并且是 Enter Universe 的作者。…

codeforces 610D D. Vika and Segments(离散化+线段树+扫描线算法)
题目链接: D. Vika and Segments time limit per test2 secondsmemory limit per test256 megabytesinputstandard inputoutputstandard outputVika has an infinite sheet of squared paper. Initially all squares are white. She introduced a two-dimensional c…

ubuntu下安装redis
安装reids服务器 apt-get install redis-server 测试是否安装成功 redis-cli 安装phpredis扩展 #wgethttps://github.com/nicolasff/phpredis/downloads #tar -zxvf nicolasff-phpredis-2.1.3-124-gd4ad907.tar.gz # mv nicolasff-phpredis-d4ad907 php-5.3.8/ext/phpredis/ # …

往往存储与计算机硬盘或其他,硬盘是计算机系统中信息资源最重要的存储设备其所存放信息-Read.DOC...
硬盘是计算机系统中信息资源最重要的存储设备其所存放信息-ReadPAGEPAGE 2摘要关键字:磁盘、硬盘、中断13、扩展中断13、分区表、MBR、DBR、DPT、Boot、CMOS、FAT、柱面、磁道、磁头、扇区随着科学技术的不断发展和社会信息化程度的不断提高,电脑已逐渐深…
【Ghost Blog】如何给Ghost Blog添加背景音乐
昨天闲着无聊,就给自己的电脑装了一个Ghost的博客,打开博客的第一眼就被震撼到了,我们可以发现界面十分的简介。。。。上面的都是废话 我们来看一看我我选择的音乐播放器——网易云音乐,这个播放器就是在一个歌曲上点开之后有一个…
AE 动画直接变原生代码:Airbnb 发布开源动画库 Lottie
原文 Airbnb 发布的 Lottie 是一个面向 iOS、Android 和 React Native 的开源动画库。 简单来说,就是可以直接利用 AE 导出的 JSON 动画文件,将其解析为原生代码,并跨平台运行在设备上。 根据身边朋友的试用,通过 Canvas 绘制动画…

纹理贴图的模式设置
1 要对纹理进行任何的操作,必须先使该纹理问当前的active纹理 glGenTextures( 1, &reflectionTexObj );glBindTexture( GL_TEXTURE_2D, reflectionTexObj );glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );glTexParameteri( GL_TEXTURE_2D, G…

人社局计算机考试报名时间,内蒙古人社局:2016年下半年计算机软件水平考试报名时间通知...
关于做好2016年度下半年计算机技术与软件专业技术资格、翻译专业资格(水平)考试笔译考试报名工作的通知各旗县区人力资源和社会保障局、市直有关单位:根据内蒙古自治区人事考试中心《关于做好2016年度下半年计算机技术与软件专业技术资格(水平)考试报名工作的通知》…
即时通讯下数据粘包、断包处理实例(基于CocoaAsyncSocket)
来源:涂耀辉 www.jianshu.com/p/2e16572c9ddc 如有好文章投稿,请点击 → 这里了解详情 前言 本文旨以实例的方式,使用CocoaAsyncSocket这个框架进行数据封包和拆包。来解决频繁的数据发送下,导致的数据粘包、以及较大数据&#x…

linux下 为自己编写的程序 添加tab自动补全 功能
linux下 为自己编写的程序 添加tab自动补全功能 入门 complete 在我的tmp下随便写了一个a.sh, 为他补全edit /etc/bash_completion.d/foo_foo() {local cur prev optsCOMPREPLY()cur"${COMP_WORDS[COMP_CWORD]}"prev"${COMP_WORDS[COMP_CWORD-1]}"opts&quo…

笔记本电脑(Windows7)实现无线AP
使用环境:出差两个同事住一个房间、网线不够用、没有路由器 1、在windows命令窗口中运行以下命令 netsh wlan set hostednetwork modeallow netsh wlan set hostednetwork ssidOPEN key1234567890 netsh wlan start hostednetwork 命令解释:在笔记本插有…

华北电力大学计算机图形学实验报告,华北电力大学计算机图形学实验报告分析.doc...
华北电力大学计算机图形学实验报告分析科 技 学 院课程设计(综合实验)报告( 2013 -- 2014 年度第 2 学期)实验名称 OpenGL基本图元绘制实验课程名称 计算机图形学||专业班级:计算机11K1 学生姓名:曲强学 号:111909010118 成 绩:指…
Fastlane 入门实战教程从打包到上传iTunes connect
有关神器 Fastlane 持续集成\部署的文章网上挺多,本文定位是入门教程,针对 iOS 应用的持续部署,只需一条命令就可实现从 Xcode 项目到 编译\打包\构建\提交审核 文章稍微有点长,涵盖内容为:fastlane 简介\安装\配置 Snapshot 截图 XCTest 一键上传App Store 说明:本文将 App…

double int char 数据类型
贴心的limits... 测试代码: #include <iostream> #include <stdio.h> #include <limits> #include <math.h> using namespace std;int main() {//double 有效数字16位double test3 1.2345678912345678e17;printf("%.17lf\n", te…

开发工具Drawscript
在Mac App Store上有一款iOS开发工具PaintCode(MAC App Store地址)。它可以通过矢量绘图来绘出你想要生成的用户控件界面,然后由PaintCode来动态生成iOS & OSX绘制代码。这样,你在drawRect函数中就只要粘贴拷贝就能生成自己想要的图案了。奈何&#…

悉尼大学计算机研究生学制,悉尼大学研究生学制
澳大利亚悉尼大学具有丰富的研究生专业课程,学制安排一般在1-2年时间。悉尼大学硕士申请要求要求非211大学申请者,暂不需清华认证 (毕业证、学位证、成绩单)入学要求:工程类专业(Engineering,IT)Master of Professional Engineering985/211学…

2016.04.09 使用Powerdesigner进行创建数据库的概念模型并转为物理模型
2016.04.09 使用Powerdesigner进行创建数据库的概念模型并转为物理模型 2016-04-09 21:10:24 本文原创受版权保护,严禁转载。 请大家不要用于商业用途,支持正版,大家都是做软件的,知道开发一套软件实属不易啊! 今天看到了一个很有趣并且很有用的辅助…

ESTabBarController
为什么要使用? 在开发工作中,我们可能会遇到需要自定义UITabBar的情况。例如:改变文字样式、添加一些动画效果、设置一个比默认更大的样式等等,以上需求如果只通过UITabBarItem往往很难实现。 有了ESTabBarController,你可以轻松…

iPhone App开发导航条(Navigation Bar)素材PSD下载
不管是iPhone还是Android的应用App界面基本上最上方都会有个导航条(Navigation Bar)。于是我决定创建此页面整理收集所有好看的适合在iPhone App应用开发中使用的导航条素材PSD文件,并附有下载链接供需要在自己的iPhone App应用开发中需要使用…

点歌服务器工作原理,KTV点歌系统方案概述
《KTV点歌系统方案概述》由会员分享,可在线阅读,更多相关《KTV点歌系统方案概述(7页珍藏版)》请在人人文库网上搜索。1、一)目前点歌系统的主流方式目前,可以实现的KTV系统的点歌方式很多,但是可以主要归类为以下两大方式…
Xcode快捷键及代码块
2017-02-16 吴白 CocoaChina手指在键盘上飞速跳跃,终端上的代码也随着飞舞,是的这确实很酷。优秀的程序员总是这么一群人,他们不拘于现状,不固步自封,他们喜欢新奇的事,他们把自己发挥到极致。 指法攻略 放下您钟爱的鼠标吧&#…

使用logrotate管理nginx日志文件
本文转载自:http://linux008.blog.51cto.com/2837805/555829 描述:linux日志文件如果不定期清理,会填满整个磁盘。这样会很危险,因此日志管理是系统管理员日常工作之一。我们可以使用"logrotate"来管理linux日志文件&am…

c 异步中断服务器连接,异步连接和断开与epoll(Linux)
我有一个“完整”的答案在这里以防别人正在寻找这样的:#include #include ........int retVal -1;socklen_t retValLen sizeof (retVal);int status connect(socketFD, ...);if (status 0){// OK -- socket is ready for IO}else if (errno EINPROGRESS){struc…