UI高级控件入门2——UITableView

UITableView

  • UITableView介绍?

    1. UITableView继承自UIScrollView,支持垂直滚动,而且性能极好。
    2. UITableView的两种样式:
      • 不分组UITableViewStylePlain;
      • 分组UITableViewStyleGrouped。
    3. 如何在UITableView中展示数据:UITableView需要一个数据源(UIDataSource)来展示数据,UITableView会向数据源查询一共有多少行数以及每一行显示什么数据等,没有设置数据源的UITableView只是个空壳。
    4. UITableView中数据只有行的概念,并没有列的概念。
    5. 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简单介绍?

    1. NSIndexPath在UITableView中有个分类,有两个属性,分别对应组号和行号。
@interface NSIndexPath (UITableView)
+ (NSIndexPath *)indexPathForRow:(NSInteger)row inSection:(NSInteger)section;
@property(nonatomic,readonly) NSInteger section;
@property(nonatomic,readonly) NSInteger row;
@end
  • cell的重用?

    1. cell的性能优化:当cell出屏幕(不用之后),把cell对象存储在特定标示的缓存池,当tableView加载下一个数据的时候,从缓存池中取出空闲的cell。
    2. 代码体现:
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?

    1. 因系统自带的UITableViewCell提供的四种cell样式,大多数情况下并不能满足我们的需求,这时我们就需要自定义UITableViewCell。
    2. 实现自定义UITableViewCell的步骤:
      • 提供快速创建可重用的自定义cell的类方法,tableView作为参数。
      • 重写构造方法,初始化设置cell内部的子控件。
      • 传入数据模型,重写数据模型的setter方法,给cell内部的子控件设置frame,以及给内部子控件赋值。
    3. 由于代理设置行高的方法在数据源方法之前执行,如果需要tableView的行高是不固定的,因此应根据tableView每一行内部的数据内容设置行高,而要想提前拿到数据设置行高,应该在控制器中懒加载模型数据时,就已经设置好每一行内部的子控件的frame,因此自定义UITableViewCell时,考虑自定义frame模型。
    4. 在自定义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行滚动到屏幕指定的位置
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • 概述在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似...
    liudhkk阅读 9,034评论 3 38
  • UITableViewCell控件空间构造 cell的子控件是contentView,contentView的子控...
    CoderZXS阅读 689评论 0 1
  • UITableViewCell 父类是UIView UITableView的每一行都是一个UITableViewC...
    翻这个墙阅读 6,596评论 0 1
  • 掌握 设置UITableView的dataSource、delegate UITableView多组数据和单组数据...
    JonesCxy阅读 1,127评论 0 2
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,093评论 4 62