UITableView简介
UITableViewCell简介以及重用原理介绍
UITableViewCell的几种循环利用方式介绍
自定义Cell的几种方式(StoryBoard Xib 纯代码等)
键盘通知
tableView设置下拉背景色(CGRectOffset
和CGRectInset
的使用区别)
在众多移动应用中,能看到各式各样的表格数据,而且都是如下两种样式,如:
UITableViewStylePlain 样式

UITableViewStyleGrouped 样式

UITableViewDataSource
tableView需要一个数据源(dataSource)来显示数据,它会向数据源查询一共有多少行数据以及每一行显示什么数据等,没有设置数据源的tableView只是一个空壳
// 可选方法,一共有多少组数据,默认是1
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
// 每一组有多少行数据
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
// 每一行显示什么内容,调用时刻:每当有一个cell进入视野范围内就会调用
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
UITableViewDelegate
注意一:UILabel结合Autolayout计算高度有时候不精确的问题

通过上图我们可以看到UILabel在使用Autolayout设置约束的时候,由于多行UILabel不知道自己要显示多少内容,不知道自己的真实尺寸,我们需要为他设置preferredMaxLayoutWidth
,告诉它布局时最大的参考宽度。
+ (instancetype)cellWithTableView:(UITableView *)tableView
{
static NSString *ID = @"status";
XMGStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
}
return cell;
}
- (void)awakeFromNib
{
[super awakeFromNib];
// 设置label每一行文字的最大宽度
// 为了保证计算出来的数值 跟 真正显示出来的效果 一致
self.contentLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;
}
- (void)setStatus:(XMGStatus *)status
{
_status = status;
if (status.isVip) {
self.nameLabel.textColor = [UIColor orangeColor];
self.vipView.hidden = NO;
} else {
self.nameLabel.textColor = [UIColor blackColor];
self.vipView.hidden = YES;
}
self.nameLabel.text = status.name;
self.iconView.image = [UIImage imageNamed:status.icon];
if (status.picture) {
self.pictureView.hidden = NO;
self.pictureView.image = [UIImage imageNamed:status.picture];
} else {
self.pictureView.hidden = YES;
}
self.contentLabel.text = status.text;
// 强制布局
[self layoutIfNeeded];
// 计算cell的高度
if (self.pictureView.hidden) { // 没有配图
status.cellHeight = CGRectGetMaxY(self.contentLabel.frame) + 10;
} else { // 有配图
status.cellHeight = CGRectGetMaxY(self.pictureView.frame) + 10;
}
}
注意二:区分 tableHeaderView和sectionHeaderView


注意三:局部刷新
// 局部刷新
NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:0];
//推荐这种做法
[self.tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationBottom];
// 全部刷新,会重新刷新屏幕上的所有Cell,一般不推荐这么粗暴解决,因为比较浪费性能
[self.tableView reloadData];
注意四:tableView编辑模式

// 让tableView进入编辑模式
[self.tableView setEditing:!self.tableView.isEditing animated:YES];
#pragma mark - TableView代理方法
/**
* 只要实现这个方法,左划cell出现删除按钮的功能就有了(默认是删除操作)
* 用户提交了添加(点击了添加按钮)\删除(点击了删除按钮)操作时会调用
*/
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) { // 点击了“删除”
// 删除模型
[self.deals removeObjectAtIndex:indexPath.row];
// 刷新表格
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
} else if (editingStyle == UITableViewCellEditingStyleInsert) { // 点击了+
NSLog(@"+++++ %zd", indexPath.row);
}
}
/**
* 这个方法决定了tableView进入编辑模式时,每一行的编辑类型:insert(+按钮)、delete(-按钮)
*/
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return indexPath.row % 2 == 0? UITableViewCellEditingStyleInsert : UITableViewCellEditingStyleDelete;
}
注意五:批量操作

上图是系统自带的批量操作样式:
- (void)viewDidLoad {
[super viewDidLoad];
// 允许在编辑模式进行多选操作
self.tableView.allowsMultipleSelectionDuringEditing = YES;
}
//让tableView进入编辑模式
- (IBAction)multiOperation:(id)sender {
[self.tableView setEditing:!self.tableView.isEditing animated:YES];
}
- (IBAction)remove {
// 获得所有被选中的行
NSArray *indexPaths = [self.tableView indexPathsForSelectedRows];
NSMutableArray *deletedDeals = [NSMutableArray array];
for (NSIndexPath *path in indexPaths) {
[deletedDeals addObject:self.deals[path.row]];
}
[self.deals removeObjectsInArray:deletedDeals];
[self.tableView reloadData];
}
当然系统自带的这种样式可能不符合我们的需求,所以此时我们需要自己自定义批量操作
我们可以在Cell中增加一张打钩的图片,默认是隐藏状态 , 同时在模型中增加一个属性,用来标志用户的选中状态
/** 状态量标识有无被打钩 */
@property (assign, nonatomic, getter=isChecked) BOOL checked;
在用户点击Cell的时候操作checked
属性
#pragma mark - TableView代理方法
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// 取消选中这一行
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// 模型的打钩属性取反
XMGDeal *deal = self.deals[indexPath.row];
deal.checked = !deal.isChecked;
// 刷新表格
[tableView reloadData];
}
同时在Cell中来操作打钩图片的显示和隐藏即可
- (void)setDeal:(XMGDeal *)deal
{
_deal = deal;
// 设置数据
self.iconView.image = [UIImage imageNamed:deal.icon];
self.titleLabel.text = deal.title;
self.priceLabel.text = [NSString stringWithFormat:@"¥%@", deal.price];
self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已购买", deal.buyCount];
// 设置打钩控件的显示和隐藏
self.checkView.hidden = !deal.isChecked;
}
UITableViewCell简介
UITableView的每一行都是一个UITableViewCell
,通过tableView:cellForRowAtIndexPath:
方法来初始化每一行
UITableViewCell
内部有个默认的子视图:contentView
,contentView
是UITableViewCell
所显示内容的父视图,可显示一些辅助指示视图
辅助指示视图的作用是显示一个表示动作的图标,可以通过设置UITableViewCell的accessoryType
来显示,默认是UITableViewCellAccessoryNone(不显示辅助指示视图),其他值如下:
UITableViewCellAccessoryDisclosureIndicator

UITableViewCellAccessoryDetailDisclosureButton

UITableViewCellAccessoryCheckmark

还可以通过cell的accessoryView属性来自定义辅助指示视图,比如往右边放一个开关等;
UITableViewCell结构
contentView下默认有3个子视图
textLabel
detailTextLabel
UIImageView
UITableViewCell可以通过UITableViewCellStyle
属性,来决定使用contentView的哪些子视图,以及这些子视图在contentView中的位置
UITableViewCellStyleDefault

UITableViewCellStyleValue1

UITableViewCellStyleValue2

UITableViewCellStyleSubtitle

UITableView的常见设置
// 分割线颜色
self.tableView.separatorColor = [UIColor redColor];
// 隐藏分割线
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.allowsSelection = NO; // 不允许选中
// tableView有数据的时候才需要分割线
self.tableView.tableFooterView = [[UIView alloc] init];
// 当 cell、 header 、footer 的高度是一样的时候,可以 使用以下操作来统一设置高度。
self.tableView.rowHeight = 40;
self.tableView.sectionHeaderHeight = 44;
self.tableView.sectionFooterHeight = 44;
UITableViewCell的常见设置
// 取消选中的样式(常用) 让当前 cell 按下无反应
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// 设置选中的背景色
UIView *selectedBackgroundView = [[UIView alloc] init];
selectedBackgroundView.backgroundColor = [UIColor redColor];
cell.selectedBackgroundView = selectedBackgroundView;
// 设置默认的背景色
cell.backgroundColor = [UIColor blueColor];
// 设置默认的背景色
UIView *backgroundView = [[UIView alloc] init];
backgroundView.backgroundColor = [UIColor greenColor];
cell.backgroundView = backgroundView;
// backgroundView的优先级 > backgroundColor
// 设置指示器
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.accessoryView = [[UISwitch alloc] init];
UITableViewCell的重用原理
因为iOS设备的内存有限,如果用UITableView显示成千上万条数据,那么就需要成千上万个UITableViewCell对象的话,那将会耗尽iOS设备的内存。要解决该问题,需要重用UITableViewCell对象
重用原理:当滚动列表时,部分Cell会被移出窗口,tableView会将窗口外的Cell放入一个缓存池中,等待重用。当tableView要求dataSource返回 Cell时,dataSource会先查看这个缓存池,如果池中有未使用的 Cell,dataSource则会用新的数据来配置这个Cell,然后返回给tableView重新显示到窗口中,从而避免创建新对象
问题 因为每⼀行⽤的不一定是同一种类型的Cell,所以缓存池中也会有很多不同类型的 Cell,那么tableView在重⽤用Cell时可能会得到错误类型的 Cell
解决方案 Cell有个 reuseIdentifier 属性,当tableView 要求dataSource返回Cell时,先 通过一个字符串标识到对象池中查找对应类型的Cell对象,如果有就重用,如果没有就传入这个字符串标识来初始化一个Cell对象。
缓存优化的思路:
(1)先去缓存池中查找是否有满足条件的cell,若有那就直接拿来
(2)若没有,就自己创建一个cell
(3)创建cell,并且设置一个唯一的标记
(4)给cell设置数据
cell的循环利用方式1
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 0.重用标识
// 被static修饰的局部变量:只会初始化一次,在整个程序运行过程中,只有一份内存
static NSString *ID = @"cell";
// 1.先根据cell的标识去缓存池中查找可循环利用的cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
// 2.如果cell为nil(缓存池找不到对应的cell)
if (cell == nil) {
//代码创建
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
//加载xib中的Cell
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([XMGDealCell class]) owner:nil options:nil] lastObject];
}
// 3.覆盖数据
cell.textLabel.text = [NSString stringWithFormat:@"testdata - %zd", indexPath.row];
return cell;
}
cell的循环利用方式2
- 定义一个全局变量
// 定义重用标识
NSString *ID = @"cell";
- 注册某个标识对应的cell类型
// 在这个方法中注册cell
- (void)viewDidLoad {
[super viewDidLoad];
// 注册某个标识对应的cell类型
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:ID];
[self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([XMGDealCell class]) bundle:nil] forCellReuseIdentifier:ID];
}
- 在数据源方法中返回cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1.去缓存池中查找cell,如果找不到则根据上面注册的Cell类型创建一个
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID forIndexPath:indexPath];
// 2.覆盖数据
cell.textLabel.text = [NSString stringWithFormat:@"testdata - %zd", indexPath.row];
return cell;
}
cell的循环利用方式3
-
在storyboard中设置UITableView的Dynamic Prototypes Cell
image
-
设置cell的重用标识
image
-
在代码中利用重用标识获取cell
static NSString *ID = @"cell";
// 1.先根据cell的标识去缓存池中查找可循环利用的cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
// 2.覆盖数据
cell.textLabel.text = [NSString stringWithFormat:@"cell - %zd", indexPath.row];
return cell;
自定义Cell的步骤
-
storyboard自定义cell
-
1.创建一个继承自UITableViewCell的子类,比如XMGDealCell
image
-
2.在storyboard中
-
往cell里面增加需要用到的子控件
image
-
设置cell的重用标识
image
-
设置cell的class为XMGDealCell
image
-
往cell里面增加需要用到的子控件
-
3.在控制器中
- 利用重用标识找到cell
-
给cell传递模型数据
image
-
4.在XMGDealCell中
-
将storyboard中的子控件连线到类扩展中
image
-
需要提供一个模型属性,重写模型的set方法,在这个方法中设置模型数据到子控件上
image
image
-
将storyboard中的子控件连线到类扩展中
-
1.创建一个继承自UITableViewCell的子类,比如XMGDealCell
-
xib自定义cell
- 1.创建一个继承自UITableViewCell的子类,比如XMGDealCell
- 2.创建一个xib文件(文件名建议跟cell的类名一样),比如XMGDealCell.xib
- 拖拽一个UITableViewCell出来
- 修改cell的class为XMGDealCell
- 设置cell的重用标识
- 往cell中添加需要用到的子控件
- 3.在控制器中
- 利用registerNib...方法注册xib文件
- 利用重用标识找到cell(如果没有注册xib文件,就需要手动去加载xib文件)
- 给cell传递模型数据
- 4.在XMGDealCell中
- 将xib中的子控件连线到类扩展中
- 需要提供一个模型属性,重写模型的set方法,在这个方法中设置模型数据到子控件上
- 也可以将创建获得cell的代码封装起来(比如cellWithTableView:方法)
-
代码自定义cell(使用frame)
- 1.创建一个继承自UITableViewCell的子类,比如XMGDealCell
- 在initWithStyle:reuseIdentifier:方法中
- 添加子控件
- 设置子控件的初始化属性(比如文字颜色、字体)
- 在layoutSubviews方法中设置子控件的frame
- 需要提供一个模型属性,重写模型的set方法,在这个方法中设置模型数据到子控件
- 在initWithStyle:reuseIdentifier:方法中
- 2.在控制器中
- 利用registerClass...方法注册XMGDealCell类
- 利用重用标识找到cell(如果没有注册类,就需要手动创建cell)
- 给cell传递模型数据
- 也可以将创建获得cell的代码封装起来(比如cellWithTableView:方法)
- 1.创建一个继承自UITableViewCell的子类,比如XMGDealCell
-
代码自定义cell(使用autolayout)
- 1.创建一个继承自UITableViewCell的子类,比如XMGDealCell
- 在initWithStyle:reuseIdentifier:方法中
- 添加子控件
- 添加子控件的约束(建议使用Masonry)
- 设置子控件的初始化属性(比如文字颜色、字体)
- 需要提供一个模型属性,重写模型的set方法,在这个方法中设置模型数据到子控件
- 在initWithStyle:reuseIdentifier:方法中
- 1.创建一个继承自UITableViewCell的子类,比如XMGDealCell
-
2.在控制器中
- 利用registerClass...方法注册XMGDealCell类
- 利用重用标识找到cell(如果没有注册类,就需要手动创建cell)
- 给cell传递模型数据
- 也可以将创建获得cell的代码封装起来(比如cellWithTableView:方法)
两种重用Cell的区别
在UITableView
中有两种重用Cell
的方法:
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier;
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);
在iOS 6
中 dequeueReusableCellWithIdentifier:
被 dequeueReusableCellWithIdentifier:forIndexPath:
所取代。如此一来,在tableView
中创建并添加UITableViewCell
对象会变得更为精简而流畅。而且使用dequeueReusableCellWithIdentifier:forIndexPath:
一定会返回cell,系统在默认没有cell可复用的时候会自动创建一个新的cell出来。
使用dequeueReusableCellWithIdentifier:forIndexPath:
的话,必须和下面的两个配套方法或者是 StoryBoard 配合起来使用:
[self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([GSDealCell class]) bundle:nil] forCellReuseIdentifier:cellID];
//或者
[self.tableView registerClass:[GSDealCell class] forCellReuseIdentifier:cellID];
这样在tableView:cellForRowAtIndexPath:
方法中就可以省掉下面这些代码:
static NSString *CellIdentifier = @"Cell";
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
//或者
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([GSDealCell class]) owner:nil options:nil] lastObject];
取而代之的是下面这句代码:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
一、使用NIB
1、xib中指定cell的Class为自定义cell的类型
2、调用registerNib:forCellReuseIdentifier:
向数据源注册cell
[_tableView registerNib:[UINib nibWithNibName:@"CustomCell" bundle:nil] forCellReuseIdentifier:kCellIdentify];
3、在tableView:cellForRowAtIndexPath:
中使用dequeueReusableCellWithIdentifier:forIndexPath:
获取重用的cell,如果没有重用的cell,将自动使用提供的nib文件创建cell并返回(如果使用dequeueReusableCellWithIdentifier:
需要判断返回的是否为空(当然如果使用 StoryBoard除外))
CustomCell *cell = [_tableView dequeueReusableCellWithIdentifier:kCellIdentify forIndexPath:indexPath];
二、使用Class
1、重写Cell的 initWithStyle:withReuseableCellIdentifier:
方法进行界面布局
2、在Controller中注册Cell
[_tableView registerClass:[CustomCell class] forCellReuseIdentifier:kCellIdentify];
3、在tableView:cellForRowAtIndexPath:
中使用dequeueReusableCellWithIdentifier:forIndexPath:
获取重用的cell,如果没有重用的cell,将自动使用提供的class类创建cell并返回
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentify forIndexPath:indexPath];
4、获取cell时如果没有可重用的cell,将调用cell中的initWithStyle:withReuseableCellIdentifier:
方法创建新的cell
贴一段Cell中使用代码结合Masonry来布局的例子:
+ (instancetype)cellWithTableView:(UITableView *)tableView
{
static NSString *ID = @"deal";
// 创建cell
XMGDealCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
//如果已经注册此时这里就不用再进行判断了
if (cell == nil) {
cell = [[XMGDealCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
return cell;
}
// 1.在initWithStyle:reuseIdentifier:方法中添加子控件
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
CGFloat margin = 10;
UIImageView *iconView = [[UIImageView alloc] init];
[self.contentView addSubview:iconView];
self.iconView = iconView;
[iconView makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(100);
make.left.top.offset(margin);
make.bottom.offset(-margin);
}];
UILabel *titleLabel = [[UILabel alloc] init];
[self.contentView addSubview:titleLabel];
self.titleLabel = titleLabel;
[titleLabel makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(iconView);
make.left.equalTo(iconView.right).offset(margin);
make.right.offset(-margin);
}];
UILabel *priceLabel = [[UILabel alloc] init];
priceLabel.textColor = [UIColor orangeColor];
[self.contentView addSubview:priceLabel];
self.priceLabel = priceLabel;
[priceLabel makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(titleLabel);
make.bottom.equalTo(iconView);
make.width.equalTo(70);
}];
UILabel *buyCountLabel = [[UILabel alloc] init];
buyCountLabel.textAlignment = NSTextAlignmentRight;
buyCountLabel.font = [UIFont systemFontOfSize:14];
buyCountLabel.textColor = [UIColor lightGrayColor];
[self.contentView addSubview:buyCountLabel];
self.buyCountLabel = buyCountLabel;
[buyCountLabel makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(priceLabel);
make.right.equalTo(titleLabel);
make.left.equalTo(priceLabel.right).offset(margin);
}];
}
return self;
}
// 3.重写模型的set方法
- (void)setDeal:(XMGDeal *)deal
{
_deal = deal;
// 设置数据
self.iconView.image = [UIImage imageNamed:deal.icon];
self.titleLabel.text = deal.title;
self.priceLabel.text = [NSString stringWithFormat:@"¥%@", deal.price];
self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已购买", deal.buyCount];
}
TableView滚动到顶部的两种方法
方法一:
[self.tableView setContentOffset:CGPointMake(0,0) animated:YES];
方法二:
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
iOS11中heightForHeaderInSection
高度无效的问题
更新到iOS11之后,使用XCode9运行项目,发现-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
方法不走,所以页面也华丽丽的变成了一排的cell,通过查看文档和资料,原来是iOS11默认开启self-sizing
,把这个属性关掉即可:
self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;
把上面这几句代码加到初始化tableview的地方即可,其他的设置不用变!加完后,再运行,原来的设置就起效了!
工具条跟随键盘变化


方式一:

- (void)viewDidLoad {
[super viewDidLoad];
// 监听键盘通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
}
#pragma mark - 键盘处理
- (void)keyboardWillChangeFrame:(NSNotification *)note {
// 取出键盘最终的frame
CGRect rect = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
// 取出键盘弹出需要花费的时间
double duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 修改约束
self.bottomSpacing.constant = [UIScreen mainScreen].bounds.size.height - rect.origin.y;
[UIView animateWithDuration:duration animations:^{
[self.view layoutIfNeeded];
}];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
方式二:
抛开控制约束,我们可以直接通过transform
来实现
#pragma mark - 键盘处理
- (void)keyboardWillChangeFrame:(NSNotification *)note {
// 取出键盘最终的frame
CGRect rect = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
// 取出键盘弹出需要花费的时间
double duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 修改transform
[UIView animateWithDuration:duration animations:^{
CGFloat ty = [UIScreen mainScreen].bounds.size.height - rect.origin.y;
self.view.transform = CGAffineTransformMakeTranslation(0, - ty);
}];
}
tableView设置下拉背景色
在开发中为了程序的美观,tableView在下拉刷新的时候经常会让下拉之后的背景色与主色调保持一致,如下图所示

那么该如何实现这种效果呢? 不要想当然的以为 只要设置了tableView的背景色就可以了哦
CGRect rect= CGRectOffset(self.tableView.bounds, 0, -self.tableView.bounds.size.height);
UIView *bgView = [[UIView alloc] initWithFrame:rect];
bgView.backgroundColor = [UIColor blueColor];
[self.tableView insertSubview:bgView atIndex:0];
这里使用到了CGRectOffset
那么我们就来讨论下 CGRectOffset
和 CGRectInset
的区别:
CGRectInset:
CGRect CGRectInset(CGRect rect, CGFloat dx, CGFloat dy):
通过第二个参数 dx 和第三个参数 dy 重置第一个参数 rect 并且作为结果返回。
重置的方式为,首先将 rect 的坐标(origin)按照(dx,dy) 进行平移,然后将 rect 的大小(size) 宽度缩小2倍的 dx,高度缩小2倍的 dy 如果参数为负数 则对应放大
UIView *grayView = [[UIView alloc] initWithFrame:CGRectMake(50, 200, 200, 200)];
grayView.backgroundColor = [UIColor grayColor];
[self.view addSubview:grayView];
//根据grayView的大小变换后创建redView;
CGRect rect=CGRectInset(grayView.frame, -10, 10);
UIView *redView=[[UIView alloc]initWithFrame:rect];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
效果图如下:

CGRectOffset
CGRect CGRectOffset(CGRect rect, CGFloat dx, CGFloat dy);
rect 按照(dx,dy)进行平移 没有缩放
UIView *grayView = [[UIView alloc] initWithFrame:CGRectMake(50, 200, 200, 200)];
grayView.backgroundColor = [UIColor grayColor];
[self.view addSubview:grayView];
//根据grayView的大小变换后创建redView;
CGRect rect=CGRectOffset(grayView.frame, 0, -grayView.frame.size.height);
UIView *redView=[[UIView alloc]initWithFrame:rect];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
效果图如下:

头部可拉伸效果

网友评论