iOS小技能: cell类型及Frame模型的应用

1,074 阅读4分钟

「这是我参与2022首次更文挑战的第36天,活动详情查看:2022首次更文挑战」。

引言

iOS开发中,要实现表格数据展示,做常用的做法就是使用UITableView。

I cell类型&右侧视图及代理方法

1.1 设置右侧视图

  1. UITableViewCellAccessoryDisclosureIndicator 箭头,可以提示用户们当前行是可以点击的,通常选择行会跳到新的页面
  2. UITableViewCellAccessoryDetailButton 按钮,通常点击按钮可以做独立的操作,例如进行alertView;点击按钮并不会选中行
  3. UITableViewCellAccessoryDetailDisclosureButton 箭头+按钮,它们是各自工作的控件
  4. UITableViewCellAccessoryCheckmark 对号,通常提示用户该行数据设置完毕,使用的比较少

    /**
      
     */
    [cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];

1.2 自定义右侧控件

setAccessoryType 不满足实现的时候,才使用setAccessoryView进行自定义控件

setAccessoryView需要自行添加监听方法,应用场景通常是自定义cell,监听方法不要写在视图控制器中。

    /**
     */
    UISwitch *switcher = [[UISwitch alloc] init];
    [switcher addTarget:self action:@selector(updateSwitcher) forControlEvents:UIControlEventValueChanged];
    [cell setAccessoryView: switcher];

1.3 相关代理方法


#pragma mark - 代理方法通常没有返回值
/**
 点击右侧按钮的监听方法,此协议方法只为setAccessoryType服务,对于自定义控件setAccessoryView不会进行响应
 */
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath{
    NSLog(@"%d,%s",indexPath.row,__func__);
}
//取消选择某一行,有箭头的,极少使用此协议方法,极容易出错
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath{
//    NSLog(@"%d,%s",indexPath.row,__func__);
}
//选中某一行,有箭头的
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//    NSLog(@"%d,%s",indexPath.row,__func__);
}

II 例子:新浪微博


获取demo源码,请关注#公众号:iOS逆向

2.1 cellHeight 可以通过Status计算

  • 利用数据模型计算控件位置及计算cell的行高
/** 利用数据模型计算控件位置-》计算cell的行高/
 
- (void)setCellHeight{
 
    CGFloat picHeight = (self.status.picture.length >0) ? KPading+CGRectGetHeight(self.pictureViewFrame): 0;
 
    _cellHeight = CGRectGetHeight(self.iconViewFrame)+CGRectGetHeight(self.textViewFrame)+picHeight+2*KPading;
 
}

2.2 Frame模型替代数据模型

  • 数据模型用于存放文字、图片数据。
  • frame模型存放数据模型、所有子控件的frame、cell的高度。
  1. 目前存在什么问题? 所有的单元格控件的计算都是重复的,而且每次表格滚动,设置内容都会重新计算。

  2. 解决这个问题: 目前控制器中的数组保存的是status模型,将status模型替换为statusFrame模型{status,所有控件的位置},同样具有status模型

  3. 解决步骤:

  1. 只是修改视图控制器中的代码,暂时不要动cell。 --(将原有的status模型替换为statusFrame模型)

  2. 重写statusFrame set方法--设置子控件的显示数frame

  3. 重构代码一定要小步来

//
 

 
#import "HSStatusFrame.h"
 
#define KPading 10
 
#define KnameFont [UIFont systemFontOfSize:14]
 
#define KtextFont [UIFont systemFontOfSize:16]
 
 
 
 
 
 
@interface HSStatusFrame ()
 
 
 
 
//重写frame模型的setter方法(设置子控件的显示数frame);对frame的对象实例化采用懒加载方法,即进行getter方法的重写
 
 
 
 
@end
 
 
 
 
@implementation HSStatusFrame
 
 
 
 
-(void)setStatus:(HSStatus *)status{
 
    _status = status;
 
    [self setIconViewFrame];
 
    [self setNameViewFrame];
 
    [self setVipViewFrame];
 
    [self setTextViewFrame];
 
    [self setPictureViewFrame];
 
    [self setCellHeight];
 
     
 
     
 
}
 
/** 计算头像控件的frame*/
 
- (CGRect)setIconViewFrame{
 
    //定义间距
 
    CGFloat iconX = KPading;
 
    CGFloat iconY = KPading;
 
    CGFloat iconWidth = 30;
 
    CGFloat iconHeiht = 30;
 
   _iconViewFrame= CGRectMake(iconX, iconY, iconWidth, iconHeiht);
 
    return _iconViewFrame;
 
}
 
 
 
 
- (void)setNameViewFrame {
 
    //设置昵称,昵称的大小由文字的长度决定
 
    /**
 
     1.boundingRectWithSize 方法计算给定文本所占用的区域
 
     2.options: 计算多行的的准确高度需要传入NSStringDrawingUsesLineFragmentOrigin
 
     3、attributes 指定字体相关属性;UIKit框架中的第一个头文件   NSAttributedString.h
 
     */
 
    NSDictionary *nameDict = @{NSFontAttributeName:KnameFont};
 
    CGRect nameFrame = [self.status.name boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:nameDict context:nil];//返回一个x,y,都为0 的CGRect
 
    nameFrame.origin.x = CGRectGetMaxX(self.iconViewFrame)+KPading;
 
    nameFrame.origin.y = KPading+(CGRectGetHeight(self.iconViewFrame)-CGRectGetHeight(nameFrame))*0.5;
 
    _nameViewFrame =nameFrame;
 
}
 
 
 
 
- (void)setVipViewFrame{
 
      _VipViewFrame = CGRectMake(CGRectGetMaxX(self.nameViewFrame)+KPading, CGRectGetHeight(self.nameViewFrame), 14, 14);
 
}
 
 
 
 
- (void)setTextViewFrame{
 
    NSDictionary *textDict = @{NSFontAttributeName:KtextFont};
 
    CGRect textFrame = [self.status.text boundingRectWithSize:CGSizeMake(300, MAXFLOAT) options: NSStringDrawingUsesLineFragmentOrigin attributes:textDict context:nil];
 
    textFrame.origin.x = KPading;
 
    textFrame.origin.y = CGRectGetMaxY(self.iconViewFrame)+KPading ;
 
    _textViewFrame = textFrame;
 
}
 
/** 计算配图的frame*/
 
- (void)setPictureViewFrame{
 
    self.pictureViewFrame = CGRectMake(KPading,CGRectGetMaxY(self.textViewFrame)+KPading, 100, 100);
 
}
 
 
 
 
/** 计算cell的行高*/
 
 
 
 
- (void)setCellHeight{
 
    CGFloat picHeight = (self.status.picture.length >0) ? KPading+CGRectGetHeight(self.pictureViewFrame): 0;
 
    _cellHeight = CGRectGetHeight(self.iconViewFrame)+CGRectGetHeight(self.textViewFrame)+picHeight+2*KPading;
 
}
 
 
 
 
 
 
 
//实例化frame模型
 
- (instancetype)initWithStatus:(HSStatus *)status{
 
    //KVC
 
    self = [super init];//初始化父类属性
 
    if (self) {
 
        //初始化自身属性
 
        [self setStatus:status];
 
    }
 
    return self;
 
}
 
//实例化frame模型
 
+ (instancetype)statusFrameWithStatus:(HSStatus *)status{
 
    return [[self alloc]initWithStatus:status];
 
}
 
//返回frame模型数组
 
+ (NSArray *)statusFrames{
 
    NSMutableArray *tmpArrayM = [NSMutableArray array];
 
    //解析plist
 
    NSArray *arrayDict = [HSStatus statuses];
 
    for (HSStatus *status in  arrayDict) {
 
        [tmpArrayM addObject:[self statusFrameWithStatus:status]];
 
    }
 
    return tmpArrayM;
 
}
 
 
 
 
 
 
 
@end

III 其他

3.1 Xcode编辑技巧

  • 自定义视图的分割线:使用UIView,高度为1,设置下背景颜色即可)

  • 自定义的代码块文件 ~/Library/Developer/Xcode/UserData/CodeSnippets--换新电脑,直接替换文件夹中的内容即可。

3.2 错误分析

1.CUICatalog: Invalid asset name supplied: (null)

_pictureImage = [UIImage imageNamed:self.picture];//self.picture为nil,将会报错CUICatalog: Invalid asset name supplied: (null)

see also

🍅 联系作者: iOS逆向(公号:iosrev)


🍅 作者简介:CSDN 博客专家认证🏆丨全站 Top 50、华为云云享专家认证🏆、iOS逆向公号号主


🍅 简历模板、技术互助。关注我,都给你。