UITableView
-
UITableView介绍?
- UITableView继承自UIScrollView,支持垂直滚动,而且性能极好。
- UITableView的两种样式:
- 不分组UITableViewStylePlain;
- 分组UITableViewStyleGrouped。
- 如何在UITableView中展示数据:UITableView需要一个数据源(UIDataSource)来展示数据,UITableView会向数据源查询一共有多少行数以及每一行显示什么数据等,没有设置数据源的UITableView只是个空壳。
- UITableView中数据只有行的概念,并没有列的概念。
- UITableView中每行数据都是一个UITableViewCell,UITableViewCell内部有一个UIView控件(contentView,作为其他元素的父控件)、两个UILabel控件(textLabel、detailTextLabel)、一个UIImageView控件(imageView),分别用于容器、显示内容、详情和图片。
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault, // 左侧显示textLabel(不显示detailTextLabel),imageView可选(显示在最左边)
UITableViewCellStyleValue1, // 左侧显示textLabel、右侧显示detailTextLabel(默认蓝色),imageView可选(显示在最左边)
UITableViewCellStyleValue2, // 左侧依次显示textLabel(默认蓝色)和detailTextLabel,imageView可选(显示在最左边)
UITableViewCellStyleSubtitle // 左上方显示textLabel,左下方显示detailTextLabel(默认灰色),imageView可选(显示在最左边)
};
- UITableView的数据源的常用方法?
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; // 设置tableView的组数 可以不实现 默认是1
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; // 设置tableView的行数 必须实现
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; // 设置tableView每一行的cell 必须实现
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section; // 组头标题
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section; // 组尾标题
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView; // tableView右侧的索引
- UITableView的代理的常用方法?
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath; // 设置tableView的行高
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section; // 设置tableView的组头的高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section; // 设置tableView的组尾的高度
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; // 选择了某组中的某行
-
NSIndexPath简单介绍?
- NSIndexPath在UITableView中有个分类,有两个属性,分别对应组号和行号。
@interface NSIndexPath (UITableView)
+ (NSIndexPath *)indexPathForRow:(NSInteger)row inSection:(NSInteger)section;
@property(nonatomic,readonly) NSInteger section;
@property(nonatomic,readonly) NSInteger row;
@end
-
cell的重用?
- cell的性能优化:当cell出屏幕(不用之后),把cell对象存储在特定标示的缓存池,当tableView加载下一个数据的时候,从缓存池中取出空闲的cell。
- 代码体现:
static NSString *reuseID = @"hero";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseID];
// 不能在此设置cell的属性
}
NSLog(@"===%d", indexPath.row);
NSLog(@"%p", cell);
-
自定义UITableViewCell?
- 因系统自带的UITableViewCell提供的四种cell样式,大多数情况下并不能满足我们的需求,这时我们就需要自定义UITableViewCell。
- 实现自定义UITableViewCell的步骤:
- 提供快速创建可重用的自定义cell的类方法,tableView作为参数。
- 重写构造方法,初始化设置cell内部的子控件。
- 传入数据模型,重写数据模型的setter方法,给cell内部的子控件设置frame,以及给内部子控件赋值。
- 由于代理设置行高的方法在数据源方法之前执行,如果需要tableView的行高是不固定的,因此应根据tableView每一行内部的数据内容设置行高,而要想提前拿到数据设置行高,应该在控制器中懒加载模型数据时,就已经设置好每一行内部的子控件的frame,因此自定义UITableViewCell时,考虑自定义frame模型。
- 在自定义frame模型中,拥有数据模型属性、行高属性,以及每一个cell内部各个子控件的frame属性,重写数据模型的setter方法,在setter方法中根据当前数据模型设置frame模型,进行各个控件的布局(大小、位置),同时计算行高。还应提供快速创建所有数据模型对应的frame模型数组,方便在控制器中加载frame模型。而加载好的frame模型中,已经有了每一行cell对应的高度,只需在返回行高的代理方法中返回对应frame模型的行高属性。
微博自定义cell示例
frame模型
#import <UIKit/UIKit.h>
#define KNAMEFONT [UIFont systemFontOfSize:15]
#define KTEXTFONT [UIFont systemFontOfSize:13]
@class LNMicroBlog;
@interface LNMicroBlogFrame : NSObject
// blogFrame模型
@property (nonatomic, strong) LNMicroBlog *microBlog;
@property (nonatomic, assign, readonly) CGRect iconF;
@property (nonatomic, assign, readonly) CGRect nameF;
@property (nonatomic, assign, readonly) CGRect vipF;
@property (nonatomic, assign, readonly) CGRect textF;
@property (nonatomic, assign, readonly) CGRect pictureF;
@property (nonatomic, assign, readonly) CGFloat rowHeight;
+ (NSArray *)microBlogFrames;
@end
#import "LNMicroBlogFrame.h"
#import "LNMicroBlog.h"
@implementation LNMicroBlogFrame
+ (NSArray *)microBlogFrames {
NSArray *microBlogs = [LNMicroBlog microBlogs];
NSMutableArray *tmpArray = [NSMutableArray array];
for (LNMicroBlog *microBlog in microBlogs) {
LNMicroBlogFrame *microBlogFrame = [[LNMicroBlogFrame alloc] init];
microBlogFrame.microBlog = microBlog;
[tmpArray addObject:microBlogFrame];
}
return tmpArray;
}
// 重写setter方法
- (void)setMicroBlog:(LNMicroBlog *)microBlog {
_microBlog = microBlog;
[self setBlogFrame:microBlog]; // 给每个属性设置frame
}
// 依据数据模型,设置frame布局
- (void)setBlogFrame:(LNMicroBlog *)microBlog {
CGFloat margin = 10;
// 头像
CGFloat iconW = 35;
CGFloat iconH = iconW;
CGFloat iconX = margin;
CGFloat iconY = margin;
_iconF = CGRectMake(iconX, iconY, iconW, iconH);
// 昵称
CGFloat nameX = CGRectGetMaxX(_iconF) + margin;
CGSize nameSize = [self sizeOfText:microBlog.name font:KNAMEFONT maxSize:CGSizeMake(MAXFLOAT, MAXFLOAT)];
CGFloat nameY = margin + (iconH - nameSize.height) / 2;
_nameF = (CGRect){{nameX, nameY}, nameSize};
// vip
CGFloat vipW = 14;
CGFloat vipH = vipW;
CGFloat vipX = CGRectGetMaxX(_nameF) + margin;
CGFloat vipY = nameY;
_vipF = CGRectMake(vipX, vipY, vipW, vipH);
// 内容
CGSize textSize = [self sizeOfText:microBlog.text font:KTEXTFONT maxSize:CGSizeMake(355, MAXFLOAT)];
CGFloat textX = margin;
CGFloat textY = CGRectGetMaxY(_iconF) + margin;
_textF = (CGRect){{textX, textY}, textSize};
// 图片
if (microBlog.picture) {
CGFloat pictureW = 200;
CGFloat pictureH = pictureW;
CGFloat pictureX = margin;
CGFloat pictureY = CGRectGetMaxY(_textF) + margin;
_pictureF = CGRectMake(pictureX, pictureY, pictureW, pictureH);
_rowHeight = margin + CGRectGetMaxY(_pictureF);
} else {
_rowHeight = margin + CGRectGetMaxY(_textF);
}
}
// 封装计算多行文本数据size方法
- (CGSize)sizeOfText:(NSString *)text font:(UIFont *)font maxSize:(CGSize)maxSize {
NSDictionary *attrs = @{NSFontAttributeName:font};
CGSize size = [text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;
return size;
}
@end
自定义cell
#import <UIKit/UIKit.h>
@class LNMicroBlogFrame;
@interface LNMicroBlogCell : UITableViewCell
// 创建可重用的cell
+ (instancetype)microBlogWithTableView:(UITableView *)tableView;
// 拥有一个blog属性的frame
@property (nonatomic, strong) LNMicroBlogFrame *microBlogFrame;
@end
#import "LNMicroBlogCell.h"
#import "LNMicroBlogFrame.h"
#import "LNMicroBlog.h"
@interface LNMicroBlogCell ()
@property (nonatomic, weak) UIImageView *iconView;
@property (nonatomic, weak) UILabel *nameView;
@property (nonatomic, weak) UIImageView *vipView;
@property (nonatomic, weak) UILabel *textView;
@property (nonatomic, weak) UIImageView *pictureView;
@end
@implementation LNMicroBlogCell
// 快速创建cell的方法
+ (instancetype)microBlogWithTableView:(UITableView *)tableView {
static NSString *reuseId = @"blogCache";
LNMicroBlogCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId];
if (!cell) {
cell = [[self alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseId];
}
return cell;
}
// 重写构造方法 设置cell内部的子控件
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
// 头像
UIImageView *iconView = [[UIImageView alloc] init];
[self.contentView addSubview:iconView];
self.iconView = iconView;
// 昵称
UILabel *nameView = [[UILabel alloc] init];
nameView.font = KNAMEFONT;
[self.contentView addSubview:nameView];
self.nameView = nameView;
// vip
UIImageView *vipView = [[UIImageView alloc] init];
vipView.image = [UIImage imageNamed:@"vip"];
[self.contentView addSubview:vipView];
self.vipView = vipView;
// 内容
UILabel *textView = [[UILabel alloc] init];
textView.font = KTEXTFONT;
textView.numberOfLines = 0;
[self.contentView addSubview:textView];
self.textView = textView;
// 图片
UIImageView *pictureView = [[UIImageView alloc] init];
[self.contentView addSubview:pictureView];
self.pictureView = pictureView;
}
return self;
}
// 重写microBlogFrame的setter方法 给cell内部的子控件赋值 设置frame
- (void)setMicroBlogFrame:(LNMicroBlogFrame *)microBlogFrame {
_microBlogFrame = microBlogFrame;
// 给子控件赋值
[self setSubviewsData:microBlogFrame];
// 给子控件设置frame
[self setSubviewsFrame:microBlogFrame];
}
// 给cell内部子控件赋值
- (void)setSubviewsData:(LNMicroBlogFrame *)microBlogFrame {
LNMicroBlog *microBlog = microBlogFrame.microBlog;
// 头像
self.iconView.image = [UIImage imageNamed:microBlog.icon];
// 姓名
self.nameView.text = microBlog.name;
// vip
if (microBlog.vip) {
self.vipView.hidden = NO;
self.nameView.textColor = [UIColor redColor];
} else {
self.vipView.hidden = YES;
self.nameView.textColor = [UIColor blackColor];
}
// 内容
self.textView.text = microBlog.text;
// 图片
if (microBlog.picture) {
self.pictureView.image = [UIImage imageNamed:microBlog.picture];
} else {
self.pictureView.image = nil;
}
}
// 给子控件设置frame
- (void)setSubviewsFrame:(LNMicroBlogFrame *)microBlogFrame {
self.iconView.frame = microBlogFrame.iconF;
self.nameView.frame = microBlogFrame.nameF;
self.vipView.frame = microBlogFrame.vipF;
self.textView.frame = microBlogFrame.textF;
self.pictureView.frame = microBlogFrame.pictureF;
}
- 补充?
- (CGSize)sizeWithAttributes:(NSDictionary *)attrs; // 计算单行文本数据文本宽度和高度
// 计算多行文本数据文本宽度和高度,在此之前应设置文本的numberOfLines属性为0
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context;
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; // 在指定indexPaths插入单\多行
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; // 在指定indexPaths删除单\多行
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; // 在指定indexPaths删除之前的行,然后加载刷新数据
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated; // 让指定indexPath行滚动到屏幕指定的位置