iOS:UITableView相关(18-10-20更)
UITableView用得较多,遇到的情况也较多,单独记录一篇。
一、零散的技巧
二、取cell
三、cell高度
四、导航栏、TableView常见问题相关
五、自定义左滑删除按钮图片
六、仅做了解
一、零散的技巧
1、 cell的选中效果是cell的属性,可以有的有,无的无。
// 自定义cell
self.selectionStyle = UITableViewCellSelectionStyleNone;
// 取cell
cell.selectionStyle = UITableViewCellSelectionStyleNone;
2、cell的下划线是Table的属性,全部有,或全部无。
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
3、cell下划线左边顶住屏幕左边。
cell.preservesSuperviewLayoutMargins = NO;
cell.layoutMargins = UIEdgeInsetsZero;
cell.separatorInset = UIEdgeInsetsZero;
后续补充:也可以隐藏掉系统的下划线,自定义LineView,要多宽就多宽,且可以实现不同cell不同下划线样式。
4、cell的重用ID,可以用类名
NSStringFromClass([MyCell class])
5、根据 indexPath 获取 cell
[self.mTableView cellForRowAtIndexPath:indexPath];
6、根据 cell 获取 indexPath
[self.mTableView indexPathForCell:cell];
7、superView
// 第一个superview 是contentView,第二个就cell
UITableViewCell *cell = (UITableViewCell*)button.superview.superview;
8、UIScrollView 和 UITableView 的 内边距差别
// 自动偏移 contentOffset = CGPointMake(0, -200);
tableView.contentInset = UIEdgeInsetsMake(200, 0, 200, 0); // 需要设置偏移量。否则停留在偏移量(0.0)。需要再下拉一下,
scrollView.contentInset = UIEdgeInsetsMake(200, 0, 200, 0);
scrollView.contentOffset = CGPointMake(0, -200);
9、监听 contentOffset ,可以得到类似拖动代理的效果。如写第三方给别人用。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{ }
10、取消cell的左滑 编辑、删除 状态。如,按了其他位置的按钮,cell不会自动复原。
[self.mTableView setEditing:NO animated:YES];
11、style ,风格样式
// 分组 风格
// 1、自动间隔开每组
// 2、如需设置间隔需要注意,组头组尾都要处理。(坑过一次,只设置组尾高度,结果发现怎么还很高,而且不显示不能为0,要 = CGFLOAT_MIN)
// 3、滑动,组头不会悬停
self.mTableView = [[UITableView alloc]initWithFrame:CGRectZero style:UITableViewStyleGrouped]; // 扁平化 风格
// 1、每组的间隙可通过组头、组尾,自行调整。(相对上面风格,组头组尾高度默认为0)
// 2、滑动,组头组尾会悬停
self.mTableView = [[UITableView alloc]initWithFrame:CGRectZero style:UITableViewStylePlain];
12、获取当前显示的cells
// 直接得到 cells
self.mTableView.visibleCells
// 得到 indexPath ,看需求通过 cellForRowAtIndexPath: 转换。
self.mTableView.indexPathsForVisibleRows
13、滑动时,使用低分辨率图片,停止时再加载高分辨率图片。(利用 代理 和上面 “12、获取当前显示的cells” )
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
14、UIScrollView、UITableView 实时 位置 相关
参照 《iOS:手势与矩形、点运算相关》 -> “1、矩形、点运算” -> “4、UIScrollView、UITableView 实时 位置 相关”
15、拖动状态。比如判断当前滚动是否拖动引起。拖动的代理只有开始和结束,拖动中没有。
mScrollView.dragging
16、滚动到具体的cell位置。如,1、外卖,左右tableView联动。2、聊天,滚动到最新信息。
if (self.dataSource.count > 0) {
[self.mTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.dataSource.count-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom
animated:YES];
}
17、选中效果,动画
// 用途可参考外卖、电商类APP(左边tbV的分类,随着右边商品的拖动,跟新cell选中位置)
[self.leftTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:section inSection:0] animated:YES scrollPosition:UITableViewScrollPositionTop];
N、如果一个 tableView 对应多个 dataSource 。通过按钮切换,那么要考虑,点击/滑动 切换时,请求返回的数据,是否是当前 “功能选中”的位置,比如:
判断对比请求前后的字段 parameterDics、状态。若不是,
1)、可丢弃。
2)、可刷新该状态对应的 dataSource 数组(有的话),下次切换,可先刷出数据,再请求。界面友好(防止网络请求,一片空)。
边输入边搜索,同理,避免,如快速删除完后,又刷出删除前的请求数据。
二、取cell
1、cell初始化的一些区别
1)、TableViewCell
1-1)、没注册
没注册的(一开始会取不到):
cell = 从队列取
if(cell取不到)
{
创建cell
创建子视图,加tag
}
cell从tag取子视图,刷新 tag 或 属性
1-2)、注册
注册的(100%取得到):
cell = 从队列取(有indexPath的方法)
刷新 tag 或 属性 (
系统取不到,会走自定义的initWithStyle:reuseIdentifier:
if(cell创建成功)
{
创建子视图,加tag
}
)
2)、CollectionViewCell
2-1)、没注册
2-2)、注册
注册的(100%取得到):
cell = 从队列取(有indexPath的方法)
if(cell取得到)
{
(判断是否有子视图)创建子视图
}
刷新 tag 或 属性 collectionViewCell 流程有点不同
1、没 TableViewCell 的 initWithStyle:reuseIdentifier:
2、但 每次都能从队列取到
3、所以 需要判断取到的cell是否有子视图,不然会不断创建
2、加载XIB
1)、从多个cell样式的XIB加载。只有1个cell样式,可直接lastObject加载。(先根据不同的ID取,取不到再加载。)
1-1)、获取XIB里的所有对象
NSArray *cellArry = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([MyTableCell class]) owner:self options:nil];
1-2)、读取对应的Cell样式,此时的参数type为枚举,或基本数据类型。
cell = [cellArry objectAtIndex:type];
2)、在 UIView + xxx 的类别文件里,可以添加这个类。方便加载单种Cell样式的XIB。
+ (instancetype)viewFromXib
{
return [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
}
三、cell高度
0、不固定内容的cell,可弄数组、模型存高度,以免每次计算。
貌似系统计算的(下面的4、5、),耗时都长?复杂的cell滑动不流畅?所以还是能手动就手动咯(下面的2、3、)?
还有,如果是富文本,记得要把font加进去计算,经常算行距的时候,忘了字体大小。
1、全部固定高度
self.tableView.rowHeight = 44;
2、自定义cell类方法
+ (CGFloat)getCellHeight
{
return 44;
} + (CGFloat)getCellHeightWithData:(id)data
{
// 手动计算label的高度
return 计算高度;
}
后续补充:对于固定高度,没问题,好用。对于根据Data计算的,根据情况保存计算高度。
3、模型(只有属性的特殊类)
后续补充:通过get方法读取。需要才计算(懒加载),可能还要判断是否计算过,否则每次都要计算?
4、系统自动计算(iOS6后,使用 UIView 的 类别 UIConstraintBasedLayoutFittingSize 的方法,控件需要全是 Autolayout 约束?)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 取出不带 indexPath 的
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([MyCell class])]; // 填充数据
//cell.model = model[indexPath.row];
[cell initData:data[indexPath.row]]; // 计算高度
// UILayoutFittingCompressedSize 返回最小可能的值
// UILayoutFittingExpandedSize 返回最大可能的值
cellHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 0.5f; return cellHeight;
}
后续补充:1、根据情况保存计算高度。
2、普通View非Cell,的高度计算也可用,但同样要 Autolayout 约束。
3、注册cell,一般是取出带indexPath的。不带indexPath一般是在自写cell重用机制的用的。
但是,注册cell 还可以取出普通的cell样式,不带 indexPath。来填充数据,计算高度。
4、对3、补充,如果是xib可以用 NSBundle。
5、对3、再补充,可以弄个局部变量,用懒加载获取普通cell,不用每次都获取。
6、label类,多行,除了 label.numberOfLines = 0。
好像还需要设置 label.preferredMaxLayoutWidth = SCREEN_WIDTH - 20 ;
5、系统自动计算(iOS8后,UITableViewAutomaticDimension,控件需要全是 Autolayout 约束?)
1)、先给cell高度一个估算值,好让TableView,知道contentSize有多大
tableView.estimatedRowHeight = 80.0f;
2)、设置为自动计算
tableView.rowHeight = UITableViewAutomaticDimension;
iOS8后,UITableViewAutomaticDimension自动计算,不用实现 heightForRowAtIndexPath 了,不过为了兼容ios8前,可能需要再写、判断
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ( [[[UIDevice currentDevice] systemVersion ] integerValue] >= 8)
{
return UITableViewAutomaticDimension;
}
else
{ }
}
四、导航栏、TableView常见问题相关
1、导航栏、TableView
//调整contentInset。
//NO:不调整,按设定的frame、contentInset的显示
//YES:会调整contentInset.top的高,让显示的顶在导航栏下面,【有滑过半透明效果】
self.automaticallyAdjustsScrollViewInsets =NO; //调整frame
// UIRectEdgeNone //会顶在导航栏下面【没有滑过半透明效果】
// UIRectEdgeTop //对齐原点
// UIRectEdgeLeft //对齐左边
// UIRectEdgeBottom //对齐顶部
// UIRectEdgeRight //对齐右边
// UIRectEdgeAll //对齐所有
self.edgesForExtendedLayout = UIRectEdgeNone; //导航栏半透明
self.navigationController.navigationBar.translucent = YES; //隐藏navigationBar(1、它推过的所有的VC共用1个Bar;2、用继承View的hidden属性,隐藏不了!)
self.navigationController.navigationBarHidden=YES;
2、iOS11
此处参考自简书 “iOS 11 安全区域适配总结 ” -- sonialiu
1)、TableView 默认开启Cell高度估算,关掉。
[UITableView appearance].estimatedRowHeight = 0;
[UITableView appearance].estimatedSectionHeaderHeight = 0;
[UITableView appearance].estimatedSectionFooterHeight = 0;
2)、ScrollView新增安全区域。
2-1)、如果之前让TabelView顶住屏幕,然后设置顶部内边距 = 20+44,刚好在导航栏下面的话,
会被系统向下偏移64的 SafeAreaInsets,再加上自己设置的64,就出现下移64问题。
2-2)、同理,没导航栏的时候,也会下移20 -> 状态栏的高度。
2-3)、以前若设置 automaticallyAdjustsScrollViewInsets = YES 让系统自动调整,不会有问题
解决方案:添加下面,相当于 automaticallyAdjustsScrollViewInsets = NO
#ifdef __IPHONE_11_0
if ([tableView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
[UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
#endif
2-4)、contentInsetAdjustmentBehavior 其他类型
UIScrollViewContentInsetAdjustmentScrollableAxes: adjustedContentInset = ( 可滚动方向 ? safeAreaInset + contentInset : contentInset );
UIScrollViewContentInsetAdjustmentNever: adjustedContentInset = contentInset;
UIScrollViewContentInsetAdjustmentAlways: adjustedContentInset = safeAreaInset + contentInset;
UIScrollViewContentInsetAdjustmentAutomatic: (controller里automaticallyAdjustsScrollViewInsets = YES) && (controller被navigation包含) == Always,否则 == Axes
五、自定义左滑删除按钮图片
参考自简书 《【支持iOS11】UITableView左滑删除自定义 - 实现多选项并使用自定义图片 》 -- pika11
0、写在前面
尽管iOS11已经支持自定义删除图片了,但还是要兼容以前的。
1、进入编辑模式,标记view为需要layout。
- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
self.editingIndexPath = indexPath;
[vc.view setNeedsLayout];
} - (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
self.editingIndexPath = nil;
}
2、在VC的,layout子View完成的时候,判断是否需要改变cell删除样式
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews]; if (self.dataSource.editingIndexPath != nil)
{
// 改变cell 删除文字 为 删除图片
[self resetCellDeleteButton];
}
}
3、配置,不同系统
- (void)resetCellDeleteButton
{
// 获取选项按钮的reference
if ( [[[UIDevice currentDevice] systemVersion] integerValue] >= 11 )
{
for (UIView *subview in self.mTableView.subviews)
{
//iOS11(Xcode 8编译): UITableView -> UITableViewWrapperView -> UISwipeActionPullView
if ([subview isKindOfClass:NSClassFromString(@"UITableViewWrapperView")])
{
for (UIView *subsubview in subview.subviews)
{
if ([subsubview isKindOfClass:NSClassFromString(@"UISwipeActionPullView")] && [subsubview.subviews count] >= 1)
{
#warning - 可能需要判断类型再去改,会比较好,暂时没去试。
UIButton *deleteButton = subsubview.subviews.lastObject;
[self configDeleteButton:deleteButton];
}
}
}
//iOS11(Xcode 9编译): UITableView -> UISwipeActionPullView
else if ([subview isKindOfClass:NSClassFromString(@"UISwipeActionPullView")] && [subview.subviews count] >= 1)
{
#warning - 可能需要判断类型再去改,会比较好,暂时没去试。
UIButton *deleteButton = subview.subviews.lastObject;
[self configDeleteButton:deleteButton];
}
}
}
else
{
// iOS8-10: UITableView -> UITableViewCell -> UITableViewCellDeleteConfirmationView
SignCell *tableCell = [self.mTableView cellForRowAtIndexPath:self.dataSource.editingIndexPath];
for (UIView *subview in tableCell.subviews)
{
if ( [subview isKindOfClass:NSClassFromString(@"UITableViewCellDeleteConfirmationView")] && [subview.subviews count] >= 1)
{
UIButton *deleteButton = subview.subviews.lastObject;
[self configDeleteButton:deleteButton];
}
}
}
}
4、实现删除样式
- (void)configDeleteButton:(UIButton*)deleteButton
{
deleteButton.backgroundColor = kBgColor;
[deleteButton setTitle:@"" forState:UIControlStateNormal];
[deleteButton setImage:[UIImage imageNamed:@"delete"] forState:UIControlStateNormal];
}
六、仅做了解
1、cell 异步加载网络图片,主线程更新UI。
1)、现在有了 SDWebImage ,只做为一种思路了解
2)、重用机制。
在取 cell 的同时刷新 imageView 的 tag ,当 imageView 异步获取到图片,判断自己的 tag 还是不是请求前传进来的 index + basetag。
如果不加这样的判断,当网络差,会出现图片错位的情况。
3)、cacheDic。
目前写法,只是一个可变字典。
往后考虑,1)、获取图片前,先判断自定义缓存NSCache(或者字典)是否有相应url名的图片。
1)、有 -> 直接调用
2)、没有 -> 去本地查找,url名的图片
1)、有,提取到缓存NSCache。key = url,object = image。调用
2)、没有?请求,以url命名保存到本地、缓存,调用。
2)、NSCache设置一定大小,会自动删除旧。如缓存读不到,又会去本地读取,并刷新到NSCache里。
缓存缺陷,如果后台更新图片,且名字用原来的,就不会被刷新。
0、宏定义
#define kImageBaseTag 2000 1、判断数据
// 更新imageView的标签
imgView.tag = indexPath.row + kImageBaseTag;
// 在单元格显示的时候,先清掉
imgView.image = nil;
// 判断是否加载过,有就用,没有就请求
if ([[self.imageCacheDic allKeys] containsObject:self.dataSource[indexPath.row]]) {
imgView.image = [self.imageCacheDic objectForKey:indexPath]; }else{
dispatch_async(_queue, ^{
NSURL *url = [NSURL URLWithString:self.dataSource[indexPath.row]];
[imgView requestImgFromUrl:url cache:self.imageCacheDic index:indexPath];
});
} 2、请求数据
#import "UIImageView+MyWebCache.h"
-(void)requestImgFromUrl:(NSURL*)url cache:(NSMutableDictionary*)cache indexPath:(NSIndexPath*)indexPath
{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:@"GET"];
[request setTimeoutInterval:3]; [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { UIImage *image = [UIImage imageWithData:data]; dispatch_async(dispatch_get_main_queue(), ^{
if (image != nil) {
// 添加到缓存
[cache setObject:image forKey:indexpath];
// 判断是否需要刷新
if (self.tag == indexPath.row + kBaseImageTag) {
self.image = image;
}
}
});
}] resume];
}
2、自定义循环池
NSMutableSet *recyclePool;
MyCell *cell = [self.recyclePool anyObject];
if (cell)
{
// 从循环池内取出
[self.recyclePool removeObject:cell];
}
else
{
// 创建
cell = [MyCell cell];
} // 超出屏幕再 add 进循环池
3、图片缓存相关
参照《iOS:图片相关》
iOS:UITableView相关(18-10-20更)的更多相关文章
- ios UITableView 相关
1.tableView 实现的方法 无分组的cell #pragma mark - Table view data source - (NSInteger)tableView:(UITableView ...
- iOS,视图相关
1.移除视图的所以子视图 2.自定义视图(UIView) 3.处理悬浮窗口(类似微信视频),等比缩放 4.自定义前面视图(可以手写字) 5.图片拉伸的几种方式,计算文本占用空间大小 6.UILable ...
- iOS开发多线程篇 10 —NSOperation基本操作
iOS开发多线程篇—NSOperation基本操作 一.并发数 (1)并发数:同时执⾏行的任务数.比如,同时开3个线程执行3个任务,并发数就是3 (2)最大并发数:同一时间最多只能执行的任务的个数. ...
- (转自http://www.blogjava.net/moxie/archive/2006/10/20/76375.html)WebWork深入浅出
(转自http://www.blogjava.net/moxie/archive/2006/10/20/76375.html) WebWork深入浅出 本文发表于<开源大本营> 作者:钱安 ...
- iOS移动开发周报-第20期
iOS移动开发周报-第20期iOS移动开发周报-第20期 [摘要]:本期iOS移动开发周报带来如下内容:iOS 通知中心扩展制作入门,iOS APP可执行文件的组成,objc非主流代码技巧等. 教程 ...
- 从Ubuntu 18.04 LTS升级到Ubuntu 18.10版本的方法
从Ubuntu 18.04 LTS升级到Ubuntu 18.10版本的方法 2018-10-18 21:08:39作者:ywnz稿源:云网牛站 本文提供从Ubuntu 18.04 LTS(Bionic ...
- iOS UITableView优化
一.Cell 复用 在可见的页面会重复绘制页面,每次刷新显示都会去创建新的 Cell,非常耗费性能. 解决方案:创建一个静态变量 reuseID,防止重复创建(提高性能),使用系统的缓存池功能. s ...
- 背水一战 Windows 10 (20) - 绑定: DataContextChanged, UpdateSourceTrigger, 对绑定的数据做自定义转换
[源码下载] 背水一战 Windows 10 (20) - 绑定: DataContextChanged, UpdateSourceTrigger, 对绑定的数据做自定义转换 作者:webabcd 介 ...
- UITableView相关知识点
//*****UITableView相关知识点*****// 1 #import "ViewController.h" // step1 要实现UITableViewDataSou ...
- iOS网络相关零散知识总结
iOS网络相关零散知识总结 1. URL和HTTP知识 (1) URL的全称是Uniform Resource Locator(统一资源定位符). URL的基本格式 = 协议://主机地址/路径 ...
随机推荐
- Java中int与Integer的区别
转自https://www.cnblogs.com/guodongdidi/p/6953217.html import java.lang.Integer; public class intDemo{ ...
- 使用WICleanup清理Windows Installer 冗余文件
使用WICleanup清理Windows Installer 冗余文件 | 浏览:816 | 更新:2015-11-02 10:43 | 标签:Win7 Win10 1 2 3 4 5 6 7 分步阅 ...
- Windows server 2008 Tips
Tips for remote server in domain. Some Definition user [user] group workgroup domain Local account d ...
- Android MVC模式和MVP模式的区别
MVC模式: 1. MVC的所有通信都是单向的. 2. view传送指令到controller(用户也可以直接将指令传到controller). 3. controller完成业务逻辑后要求model ...
- Tinker + Bugly + Jenkins 爬坑之路
前阵子 Android 端的线上崩溃比较多,热修复被提上日程.实现方案是 Tinker,Jenkins 打包,最后补丁包上传到 Bugly 进行分发.主要在 Jenkins 打包这一块爬了不少坑,现记 ...
- 数据预处理(Python scikit-learn)
在机器学习任务中,经常会对数据进行预处理.如尺度变换,标准化,二值化,正规化.至于采用哪种方法更有效,则与数据分布和采用算法有关.不同算法对数据的假设不同,可能需要不同的变换,而且有时无需进行变换,也 ...
- String类型的学习
一 :关于两个string类型变量是否相等: 请运行以下示例代码StringPool.java,查看其输出结果.如何解释这样的输出结果?从中你能总结出什么? 分析: 首先为s0开辟空间,然后给s1开辟 ...
- 使用公钥和私钥实现LINUX下免密登录
linux公钥私钥实现无密码登录 首先本地主机生成公约和私钥 # ssh-keygen /生成公钥和私钥 不要更改默认路径,中途不要输入密码,直接两次回车. 2. 将生成 ...
- 沉淀再出发:在python3中导入自定义的包
沉淀再出发:在python3中导入自定义的包 一.前言 在python中如果要使用自己的定义的包,还是有一些需要注意的事项的,这里简单记录一下. 二.在python3中导入自定义的包 2.1.什么是模 ...
- python基础语法1
一.基础语法 1.常量 python语言没有真正的常量,它只是字面常量. 2.变量 变量是一个指针,它指向一块内存. 变量的命名规则: 1)只能包含字母.数字和下划线: 2)只能以字母或者下划线开始: ...