百思不得姐之自定义cell(八)

一 运行图和解析顺序

1 运行图:

2 解析顺序:

—-> 2.1 新增的刷新功能(点击各自标题刷新和点击tabBar中的按钮刷新对应的内容)
—-> 2.2 论cell的2种做法
—-> 2.3 自定义cell
—-> 2.4 设置cell的数据
—-> 2.5 计算cell的高度
—-> 2.6 处理热门评论

二 新增的数据刷新功能(接上一篇)

1 新增原因: 目前市面上很多app都有一样的功能,就是当程序启动进入主界面的时候,数据会刷新.当用户点击该页面对应的title标题的时候又会刷新页面;同时当用户点击该页面对应的控制器中的按钮的时候,也会刷新数据.这就是现在很对app都具有的功能.

三 底部tabBar按钮刷新功能

1 监听按钮的点击事件—>两种方法

—-> 1.1 用addTag监听: 由于tabBarButton是继承UIControl,那么可以用addTag来监听.
—-> 1.2 用代理方法: 这个tabBarButton比较特别,其内部已经准守了协议,只需要我们重写代理的方法就可以.

2 步骤:

—-> 2.1 由于是底部的tabBar按钮,那么我们来到有关tabBar按钮设置的文件

—-> 2.2 对按钮的监听
//对按钮的监听
        [button addTarget:self action:@selector(ButtonClickRefresh:) forControlEvents:UIControlEventTouchUpInside];
—-> 2.3 实现监听方法(内部采用发布通知的方法实现对按钮的监听,只要有点击按钮就会发布通知)
#pragma mark - 实现监听的方法
- (void)ButtonClickRefresh:(UIControl *)button
{
    //判断当前按钮是不是上一个按钮
    if (self.previousClickedTabBarButton == button) {
        //发布通知
        [[NSNotificationCenter defaultCenter] postNotificationName:XFJTabBarButtonDidRepeatClickNotification object:nil userInfo:nil];
    }
    //将当前的按钮赋值作为上一个按钮
    self.previousClickedTabBarButton = button;
}
—-> 2.4 监听通知(注意监听通知是在发布通知之前)
#pragma mark - 监听通知
- (void)addNoticeoberver
{
    //监听tabBar重复点击通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(repeatedButtonClick) name:XFJTabBarButtonDidRepeatClickNotification object:nil];
}
—-> 2.5 移除通知
#pragma mark - 移除通知
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self forKeyPath:XFJTabBarButtonDidRepeatClickNotification];
}    
—-> 2.6 在监听通知的方法中我们实现方法(在方法中仅仅是调用开始头部刷新是不能达到效果的,需要作出判断才能运行的合理)
#pragma mark - 实现tabBar按钮重复点击的监听方法
- (void)repeatedButtonClick
{
    //判断当前控制器是否为窗口控制器
    if (self.tableView.window == nil) {
        return;
    }
    //判断当前控制器是否为可视控制器
    if (self.tableView.scrollsToTop == NO) {
        return;
    }
    [self headerBeginRefresh];
}

3 采用重写代理的方法实现监听

—-> 3.1 说明: 1.1> 不能直接来到创建tabBar对象的方法中直接将tabBar的代理设置为控制器.(设置了,没有准守协议是不包警告的,这和其他对象设置代理有区别)
tabBar.delegate = self;//不要这么设置代理,不会给警告,因为tabBar的内部已经准守了协议
1.2> 代码:下面代码这样设置代理的话,会报错.
#pragma mark - 自定义tabBar
- (void)setupTabBar
{
    XMGTabBar *tabBar = [[XMGTabBar alloc] init];
    // 替换系统的tabBar KVC:设置readonly属性
    [self setValue:tabBar forKey:@"tabBar"];
    tabBar.delegate = self;
}
1.3> 错误提示.与原因
/*
 Changing the delegate of 【a tab bar managed by a tab bar controller】 is not allowed.
 被UITabBarController所管理的UITabBar,它的delegate不允许被修改
 */
—> 说明: 2 > 我们只需要重写代理的方法就可以了
#pragma mark - tabbar的代理方法
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
    // 这里不需要调用super,因为父类没有实现这个代理方法
    NSLog(@"%s",__func__);
}

四 顶部标题刷新

1 注意点:

—-> 1.1 顶部标题的复按刷新功能和底部的一样做法
—-> 1.2 注意发布通知的标题最好用不一样的
—-> 1.3 监听通知的方法中同样需要作出判断

五 刷新部分会出现的BUG

1 屏幕旋转后会出现点击按钮后不再刷新的功能(BUG)

—-> 1.1 出现BUG原因: 当屏幕旋转的时候,又会调用layoutSubviews重新赋值,所以会监听不到刷新的按钮.(因为旋转后按钮的尺寸明显的会增大,那么自然而然就会调用layoutSubviews).
—-> 1.2 解决思路: 1> 既然旋转屏幕就调用layoutSubviews,那么我们可以用一次性代码(dispatch_once)控制该方法调用的次数; 2> 加一个判断
//判断(角标为0并且上一个按钮状态为空,表示是第0个按钮,并且将值作为上一个按钮的状态)
        if (i == 0 && self.previousClickedTabBarButton == nil) {
            self.previousClickedTabBarButton = button;
        }
—-> 1.3 开发中需要考虑到的后果: 有些方法会重复的执行,就需要考虑到重复执行的后果.

2 如果我们仅仅是直接用通知,不在监听通知的方法中做出判断的话,那么当主屏幕离开视线的时候,点击tabBar中的其他按钮,列表又会刷新,那么我们就需要做出判断.并且两句判断是不能缺少的.(这也是开发中会遇见的BUG 2.6中两个判断方法).

3 我们一侧滑页面,就会立刻刷新(BUG)

—-> 3.1 产生原因: 当我们侧滑的时候,机会来到下面的方法.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    //计算拖动索引
    NSInteger index = scrollView.contentOffset.x / XFJ_screenW;
    //根据索引取出对应的按钮
    XFJTitleButton *titleButton = self.titleView.subviews[index];
    //方法
    [self titleButtonClick:titleButton];
}
—-> 说明: 方法内部有一个调用点击按钮的方法,就意味着点击了按钮,所以就会刷新.
—-> 3.2 解决办法: 我们在一拖动UIScrollView就会调用的方法中加入下面代码.

    //判断如果上一次的按钮和这次的按钮相同就直接返回
    if (self.previousButton == titleButton) return;

六 自定义cell

1 通过分析采取方案: 1> 一个父类负责cell的顶部和底部控件,五个子类继承父类负责各自的cell的情况(通过xib) 2> 一个父类负责cell的顶部和底部控件,五个子类继承父类负责各自的cell的情况(用纯代码做,父类的上部控件和底部控件也要写) 3> 只创建一个类,其他类需要的就动态添加.

2 结论: 方法(1)和方法(2)不推荐使用,代码量太大,并且方法(1)是不可能实现的,因为xib是不存在继承的关系,所以我们采用第(3)种方法.

3 模型(通过查看完整的app和plist文件我们需要在模型中定义以下属性)

/**
 *  踩(hate)
 */
@property (nonatomic, assign) NSInteger cai;
/**
 *  顶(love)
 */
@property (nonatomic, assign) NSInteger love;
/**
 *  帖子描述
 */
@property (nonatomic, strong) NSString *text;
/**
 *  发帖时间
 */
@property (nonatomic, strong) NSString *passtime;
/**
 *  用户名
 */
@property (nonatomic, strong) NSString *name;
/**
 *  转发
 */
@property (nonatomic, assign) NSInteger repost;
/**
 *  评论
 */
@property (nonatomic, assign) NSInteger comment;
/**
 *  头像
 */
@property (nonatomic, strong) NSString *profile_image;

/**
 *  在模型中定义一个记录cell高度的属性
 */
@property (nonatomic, assign) CGFloat cellHeight;
/**
 *  最热评论(从请求的数据转为plist文件中可以看出,最热评论是数组)
 */
@property (nonatomic, strong) NSArray *top_cmt;
/**
 *  帖子类型
 */
@property (nonatomic, assign) NSInteger type;

4 自定义cell(文件和xib)

—-> 4.1 文件

—-> 4.2 xib(一律都采用自动布局)

5 在设置cell内容的方法中调用模型的set方法,直接来到自定义cell的文件中重写模型属性的set方法

#pragma mark - cell的内容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    XFJTopicesViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    cell.topices = self.item[indexPath.row];

    return cell;
}

6 通过xib连线设置自定义cell文件中的属性

/**
 *  头像
 */
@property (weak, nonatomic) IBOutlet UIImageView *profileImageView;
/**
 *  用户名
 */
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
/**
 *  正文
 */
@property (weak, nonatomic) IBOutlet UILabel *text_Label;
/**
 *  发帖时间
 */
@property (weak, nonatomic) IBOutlet UILabel *passtimeLabel;
/**
 *  顶帖
 */
@property (weak, nonatomic) IBOutlet UIButton *loveButton;
/**
 *  踩帖
 */
@property (weak, nonatomic) IBOutlet UIButton *caiButton;
/**
 *  转发数
 */
@property (weak, nonatomic) IBOutlet UIButton *repostButton;
/**
 *  评论
 */
@property (weak, nonatomic) IBOutlet UIButton *commentButton;
/**
 *  最热评论的父控件UIView
 */
@property (weak, nonatomic) IBOutlet UIView *topCmtView;
/**
 *  最热评论文字
 */
@property (weak, nonatomic) IBOutlet UILabel *topCmtLabel;

7 在自定义cell 的文件中用模型给xib中的属性赋值

#pragma mark - 用于给模型赋值
- (void)setTopices:(XFJItem *)topices
{
    _topices = topices;
    //头像
    [self.profileImageView sd_setImageWithURL:[NSURL URLWithString:topices.profile_image] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"]];
    //用户名
    self.nameLabel.text = topices.name;
    //正文
    self.text_Label.text = topices.text;
    //发帖时间
    self.passtimeLabel.text = topices.passtime;

    //对帖子的顶
    [self setUpButton:self.loveButton number:topices.love placeholder:@"顶"];
    //对帖子的踩
    [self setUpButton:self.caiButton number:topices.cai placeholder:@"踩"];
    //对帖子的转发数
    [self setUpButton:self.repostButton number:topices.repost placeholder:@"分享"];
    //对帖子的评论
    [self setUpButton:self.commentButton number:topices.comment placeholder:@"评论"];
}

8 判断是否有人们评论帖子(有些帖子并没有热门评论,这样的话我们需要控制好尺寸,该方法也是在给xib中的属性赋值的方法中进行的)

 //判断
    if (topices.top_cmt.count) {//如果有最热评论
        //不需要将view隐藏
        self.topCmtView.hidden = NO;
        //并且取出用户名和内容
        NSString *userName = topices.top_cmt.firstObject[@"user"][@"username"];
        NSString *content = topices.top_cmt.firstObject[@"content"];
        //如果没有文字是一个语音评论
        if (content.length == 0) {
            content = @"[语音评论]";
        }
        //将值付给最热评论的label
        self.topCmtLabel.text = [NSString stringWithFormat:@"%@ : %@",userName,content];
    }else{
        //如果都不满足就隐藏
        self.topCmtView.hidden = YES;
    }

9 对底部四个控件中的文字的设置(设计的方法)

- (void)setUpButton:(UIButton *)button number:(NSInteger)number placeholder:(NSString *)placeholder
{
    //判断
    if (number > 10000.0) {
        [button setTitle:[NSString stringWithFormat:@"%.1f万",number / 10000.0] forState:UIControlStateNormal];
    }else if (number > 0){
        [button setTitle:[NSString stringWithFormat:@"%zd",number] forState:UIControlStateNormal];
    }else{
        [button setTitle:placeholder forState:UIControlStateNormal];
    }
}

七 计算cell的高度

1 我们通过在模型中取出对应的行直接点出cell的高度(下面代码)

#pragma mark - cell的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return self.item[indexPath.row].cellHeight;
}

2 我们在模型中自定义一个属性,用来记录cell的高度(第3点说明了),很明显上面的代码中是直接调用cellHeight的get方法,那么我们就重写模型中cellHeight属性的get方法,将计算好的cell直接返回给地阿莱方法,直接根据每个模型中的数据返回cell的高度.(说明:下面采用的做法是xib中每一个控件高度的累加)

#pragma makr - 重写cellHeight的get方法
- (CGFloat)cellHeight
{
    //判断如果cell的高度设置了就直接返回
    if (_cellHeight) return _cellHeight;

    //正文的y值
    _cellHeight += 55;

    //正文的宽度
    CGSize cellSize = CGSizeMake(XFJ_screenW - 2 * XFJ_margin, MAXFLOAT);
    _cellHeight += [self.text boundingRectWithSize:cellSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName :[UIFont systemFontOfSize:15]} context:nil].size.height + XFJ_margin;

    //判断如果不是段子那么就是声音,图片和视频
    if (self.type != XFJTopicTypeWord) {
        _cellHeight += 100;
    }
    //如果有最热评论
    if (self.top_cmt.count) {
        _cellHeight += 21;

        //取出用户名
        NSString *userName = self.top_cmt.firstObject[@"user"][@"username"];
        //取出评论内容
        NSString *content = self.top_cmt.firstObject[@"content"];
        //判断没有文字评论,是语音评论
        if (content.length == 0) {
            content = @"[语音评论]";
        }
        //拼接评论的文字
        NSString *topCmtText = [NSString stringWithFormat:@"%@ : %@",userName,content];
        //计算文字评论的高度
        _cellHeight += [topCmtText boundingRectWithSize:cellSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:16]} context:nil].size.height + XFJ_margin;
    }
    //加上工具条
    _cellHeight += 35 + XFJ_margin;

    return _cellHeight;
}

八 cell的估算高度

1 如果我们不直接采用在模型中设置cellHeight计算cell的高度,而是采用估算高度来设置会对heightForRowAtIndexPath这个方法有怎么样的影响呢?

2 估算高度代码设置

self.tableView.estimatedRowHeight = 170;

3 估算高度的优点与缺点

—-> 3.1 优点一: 会减少heightForRowAtIndexPath方法的调用次数
—-> 3.2 优点二: 可以暂时看不见cell的高度的延迟计算
—-> 3.3 缺点一: contentSize是不太准确的
—-> 3.4 缺点而: 滑动过程中,滚动条的长度会变来变去(可能会有跳跃效果)

4 解析heightForRowAtIndexPath方法的调用时刻

—-> 4.1 如果没有设置估算高度estimatedRowHeight
——–> 4.1.1 每当reloadData时,有多少条数据,就会调用多少次这个方法(比如一共有100条数据,就会调用100次这个方法)
——–> 4.1.2 每当有cell出现时,就会调用一次这个方法
—-> 4.2 如果设置了估算高度estimatedRowHeight
——–> 4.2.1 每当有cell出现时,就会调用一次这个方法

九 知识点和BUG补充

1 知识点: 除了前面所说的刷新数据方式,苹果还自带了刷新类(只要实现了下面代码,什么事情都不需要做)

UIRefreshControl *control = [[UIRefreshControl alloc] init];
    [control addTarget:self action:@selector(loadNewTopics) forControlEvents:UIControlEventValueChanged];
    [control beginRefreshing];
    [self.tableView addSubview:control];

2 注意: 苹果自带的刷新控件会有很多的问题,在开发中一般不会使用苹果自带的,因为会出现一系列的问题.

3 BUG: 很多人刷新数据的时候,特别喜欢在请求数据的方法中加入下面代码,然后就会出现重重的下拉刷新,程序会崩溃,轻微的下拉刷新是没有问题的.

//清空模型
    [self.item removeAllObjects];
—-> 3.1 下拉刷新的目的: 旧的数据抛弃,用请求到的新的数据将旧的数据覆盖住,所以很多的初学者会这样处理数据.但是要考虑到,如果请求失败怎么办,而又将原来的数据清空了,那么不就什么都没有了么,所以这句是不应该加上去的.
—-> 3.2 错误提示:
//错误提示:-[__NSArrayM objectAtIndex:]: index 10 beyond bounds for empty array‘
    //*** First throw call stac
—-> 3.3 错误解析: 当用户往下一拉,整个tableView处于下拉刷新状态,意味着有一部分数据离开屏幕进入缓存池,但是之后如果写了清空模型的方法,那么当用户一松手的时候,由于tableView需要回到顶部,那么就会调用cellForRow的数据源方法,从缓存池中取,但是又因为清空过了数组,所以找不到缓存池中的cell,所以就会报数组越界.但是当用户轻拖的时候就不会有问题,因为轻拖的时候,cell并没有进入缓存池,所以就不存在数组越界.这是很多初学者会犯的错误.

十 总结

1 该模块是在补充了上一篇刷新部分缺少的地方,也可以说是完善吧,里面说明了现有的app中所有的刷新方式,所以现在大家只要能搞明白这些,那么刷新数据发这部分就不会有问题了.

2 里面还讲到了一些在开发中常出现的BUG,我也附上了BUG解决的办法,希望能给你们一些帮助,最后如果大家觉得我写的还可以的话,那么请关注我的官方博客,我会定期的为大家写上我最近在做的一些项目,有什么问题可以及时给我留言,谢谢!!!!

时间: 2024-10-18 07:01:33

百思不得姐之自定义cell(八)的相关文章

百思不得姐之自定义cell的完善部分(九)

一 效果图和五个模块的分析 1 效果图: 2 五个模块分析 --> 2.1 模块图: 1>视频模块 2>声音模块 3> 图片模块 4> 段子模块 5> 包含前面四种类型的模块 --> 2.2 视频模块 --> 2.3 图片模块 --> 2.4 声音模块 --> 2.5 段子模块 --> 2.6 全部模块(包括上面这些都有) 3 上一篇已经写了思路: 将顶部和底部用一个类来管理,其它部分交给对应的类来管理,根据从服务器中请求到的数据往空出来的

iOS开发-通过代码自定义cell

一.添加子控件和传递模型数据注意:子控件位置不固定,不能写死时就用代码自定义cell步骤一:将控制器改为继承UITableViewController,然后删除故事板上原来的View,新拖一个TableView,并将Class改为控制器步骤二:新建一个类WeiboCell,继承自UITableViewCell步骤三:在控制器实现文件中导入WeiboCell.h第三个数据源方法 static NSString *ID = @“weibo”: weiboCell *cell = [tableView

IOS xib在tableview上的简单应用(通过xib自定义cell)

UITableView是一种常用的UI控件,在实际开发中,由于原生api的局限,自定义UITableViewCell十分重要,自定义cell可以通过代码,也可以通过xib. 这篇随笔介绍的是通过xib自定义cell. 首先通过gif介绍如何创建xib. 然后实现代码部分,要注意的是实现代码的同时要使代码与xib相关联.-如图 下面便是代码,一些解释我在代码中注释了. ViewController.m // // ViewController.m // CX-Xib在tableView中的简单应用

自定义cell的左侧滑动

效果如上图,中间那个白线是一个UIview. 如果不添加中间那根白线,用系统的方法就可以实现,方法如下 1 -(NSArray<UITableViewRowAction*>*)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath{ 2     UITableViewRowAction *rowAction = [UITableViewRowAction rowAction

iOS 获取自定义cell上按钮所对应cell的indexPath.row的方法

在UITableView或UICollectionView的自定义cell中创建一button,在点击该按钮时知道该按钮所在的cell在UITableView或UICollectionView中的行数.就是cell的 indexPath.row,下面以UITableView为例: 有两种方法: -(IBAction):(id)sender { 1. 第一种方法,这个方便一点点,不用设置tag. NSLog(@"MyRow:%d",[self.table indexPathForCell

蓝懿教育 自定义cell

自定义cell的三种方式: 1.纯代码创建(布局内容经常改变的) 2.通过storyboard(显示固定,不涉及复用) 3.通过Xib(显示固定,但涉及复用) // dequeueRe...方法 如果是纯代码创建 就是一个参数 如果是storyboard创建 就是两个参数 通过Xib创建 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ M

iOS 代码自定义cell示例

底色标黄为代码自定义cell重点处,入手从这几点即可. MyCell.h #import <UIKit/UIKit.h> @interface MyCell :UITableViewCell @property(nonatomic,strong)UILabel *ageLabel; @property(nonatomic,strong)UILabel *nameLabel; @property(nonatomic,strong)UILabel *additionLabel; - (instan

猫猫学IOS(十六)UI之XIB自定义Cell实现团购UI

猫猫分享,必须精品 素材代码地址:http://blog.csdn.net/u013357243/article/details/44926809 原文地址:http://blog.csdn.net/u013357243?viewmode=contents 先看效果图 自定义Cell 本次主要是自定义Cell的学习 实现自定义Cell主要有三种方法:按照使用的频繁度排序: XIB > 纯代码 > StoryBoard XIB的定义步骤 1> 新建HMTgCell.xib 2> 拽一

(素材源码)猫猫学IOS(十六)UI之XIB自定义Cell实现团购UI

猫猫分享,必须精品 素材代码地址:http://download.csdn.net/detail/u013357243/8572001 原文地址:http://blog.csdn.net/u013357243?viewmode=contents 先看效果图 自定义Cell 本次主要是自定义Cell的学习 实现自定义Cell主要有三种方法:按照使用的频繁度排序: XIB > 纯代码 > StoryBoard XIB的定义步骤 1> 新建HMTgCell.xib 2> 拽一个需要自定义