IOS-自定义cell与控件布局

本文详细介绍了一个iOS应用中TableView的布局实现方法,包括如何计算不同元素的尺寸,如何封装数据模型和视图展示类,以及如何利用setter方法进行属性计算。


需要解决的问题:

1. 根据文字判断CGsize.

2. 根据最后一个控件的高度判断cell的高度.

3. 对整体模块化实现.

==============================

关于尺寸问题,补充下.  控件的位置描述是一个frame控制的,它包含两个结构体, 一个origin控制水平位置和垂直位置; 一个size控制宽度,和高度;

当我们计算文字的CGsize时候, 就是计算文字的宽度, 高度.

他们都是结构体,但是我们一般 不直接使用结构的语法进行赋值, 而是使用已有的方法, 比如对frame赋值使用CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height);

对CGsize赋值使用CGSizeMake(w, h);

计算文字的CGsize方法

 - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context

===============================


当我们做一个tableView的时候, 只需要按照步骤走即可.

1. tableView的组数,默认是1.

2.tableView分组中的行数(cell).

3.每一个cell的具体显示, 包括内容和位置两部分

4.一些附加的控制属性, 行高, 响应事件等等....

我们只要按照这个思路去完成就可以了...


================================================


关于数据模型方面, 以前我们使用的是xib直接布局的,不存在太多的布局.这次我们没有使用xib, 每一个cell都需要我们布局, 我们可以把cell所需要的信息放到一个类中.

这个类包括cell的内容信息和frame信息.

其他关于数据转模型的就不细说了 ,但是要说一点,这次模拟放到内存中的数组就不仅仅是数据了,还需要frame,所以应该是一个描述cell类的类型数组.

====================================


类封装:

cell内容类

@interface AMMicroBlog : NSObject
@property (nonatomic, copy) NSString * text;
@property (nonatomic, copy) NSString * icon;
@property (nonatomic, copy) NSString * name;
@property (nonatomic, copy) NSString * picture;

@property (nonatomic, assign, getter = isVip) BOOL vip;

- (instancetype) initWithDic : (NSDictionary *) dic;
+ (instancetype) microBlogWithDic : (NSDictionary *) dic;
+ (NSArray *) microBlogsList;


@end

cell的frame类(包含一个AMMicroBlog对象)

这个类就可以完整的描述cell所需要的所有信息.

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@class AMMicroBlog;
@interface AMMicroBlogFrame : NSObject

@property (nonatomic, strong) AMMicroBlog * microBlog;   /*=======核心语句========*/

@property (nonatomic, assign, readonly) CGRect iconFrame;    <span style="white-space:pre">		</span>/*====通过microBlog的setter方法,完成对所有属性的据算, 赋值*====/
@property (nonatomic, assign, readonly) CGRect nameFrame;
@property (nonatomic, assign, readonly) CGRect vipFrame;
@property (nonatomic, assign, readonly) CGRect textFrame;
@property (nonatomic, assign, readonly) CGRect pictureFrame;

@property (nonatomic, assign, readonly) CGFloat rowHeight;

@end


自定义cell类

该类通过一个AMMicroBlogFrame对象将以上两个类封装起来.

#import <UIKit/UIKit.h>

@class AMMicroBlogFrame;

@interface AMMicroBlogCell : UITableViewCell

@property (nonatomic, strong) AMMicroBlogFrame * microBlogFrame;   /*=======核心语句======*/

+(instancetype) microBlogCellWithTableView : (UITableView *) tableView;


@end

===============================

对于frame的计算时机,最早可以在从外部取的数据的时候就可以计算出frame, 因为只有文字的尺度是动态计算的.

在给内存中的模拟数组取值的时候就计算....

@property (nonatomic, strong) NSArray * microBlogFrames;


- (NSArray *)microBlogFrames{
    if (!_microBlogFrames) {
        NSArray * microBlogs = [AMMicroBlog microBlogsList];
        NSMutableArray * frames = [NSMutableArray array];
        for (AMMicroBlog * blog in microBlogs) {
            AMMicroBlogFrame * frame = [[AMMicroBlogFrame alloc] init];
            frame.microBlog = blog;     //micrBlog的setter方法中,根据内容计算出frame
            [frames addObject: frame];
        }
        _microBlogFrames = frames;
    }
    return _microBlogFrames;
}


           AMMicroBlogFrame * frame = [[AMMicroBlogFrame alloc] init];
            frame.microBlog = blog;     //micrBlog的setter方法中,根据内容计算出frame

AMMicroBlogFrame类中有一个AMMicroBlog类的microBlog属性,我们重写则个属性额setter方法, 在该方法中计算出AMMicroBlogFrame类中frame属性值.

@implementation AMMicroBlogFrame

- (void)setMicroBlog:(AMMicroBlog *)microBlog{
    
    _microBlog = microBlog;
    
    CGFloat margin = 10;
    //头像
    CGFloat iconW = 30;
    CGFloat iconH = 30;
    CGFloat iconX = margin;
    CGFloat iconY = margin;
    
    _iconFrame = CGRectMake(iconX, iconY, iconW, iconH);
    //名字
    CGSize nameSize = [self sizeWithText: self.microBlog.name maxSize: CGSizeMake(MAXFLOAT, MAXFLOAT) fontSize: 15];
    CGFloat nameX = CGRectGetMaxX(_iconFrame) + margin;
    CGFloat nameY = iconY + (iconH - nameSize.height) / 2;
    _nameFrame = CGRectMake(nameX, nameY, nameSize.width, nameSize.height);
    
    //VIP
    CGFloat vipW = 14;
    CGFloat vipH = 14;
    CGFloat vipY = nameY;
    CGFloat vipX = CGRectGetMaxX(_nameFrame) + margin;
    _vipFrame = CGRectMake(vipX, vipY, vipW, vipH);
    
    //内容
    CGSize textSize = [self sizeWithText: self.microBlog.text maxSize: CGSizeMake(355, MAXFLOAT) fontSize: 14];
    CGFloat textX = iconX;
    CGFloat textY =  CGRectGetMaxY(_iconFrame) + margin;
    _textFrame = CGRectMake(textX, textY, textSize.width, textSize.height);
    //图片
    
    if (self.microBlog.picture) {
        CGFloat pictureW = 100;
        CGFloat pictureH = 100;
        CGFloat pictureX = margin;
        CGFloat pictureY = margin + CGRectGetMaxY(_textFrame);
        _pictureFrame = CGRectMake(pictureX, pictureY, pictureW, pictureH);
        _rowHeight = CGRectGetMaxY(_pictureFrame) + margin;
    }else{
        _rowHeight = CGRectGetMaxY(_textFrame) + margin;
    }
}

- (CGSize) sizeWithText : (NSString *) text maxSize : (CGSize) maxSize fontSize: (CGFloat) fontSize{
    // CGSize maxSize = CGSizeMake(MAXFLOAT, MAXFLOAT);
    CGSize nameSize = [text boundingRectWithSize:maxSize options: NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize: fontSize]} context: nil].size;
    return nameSize;
}


到此cell所需要的数据都已经完整了......

=================================

下面就是按照tableView生成的一般过程就OK了.

分组---行数---cell内容


在tableView的创建过程中,对于行高的控制,如果行高都是一样的可以直接用rowHeight属性, 但是往往行高不一致,那就需要使用代理方法控制

- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    AMMicroBlogFrame * frame = self.microBlogFrames[indexPath.row];
    return frame.rowHeight;
    
    
}

通过我们调试 发现, 这个代理方法的执行是在  cell内容之前的, 也就是执行

- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;

然后在执行

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

这一点很重要, 如果我们没有在最开始的时候就求的了 cell的所有frame数据, 那么让我们执行行高方法的时候, 很可能无法拿到行高的数值.


==================================


分组----行数

我们就此省略, 直接写cell内容方法..

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    AMMicroBlogCell * cell = [AMMicroBlogCell microBlogCellWithTableView: tableView];
    AMMicroBlogFrame * microBlog = self.microBlogFrames[indexPath.row];
    cell.microBlogFrame = microBlog;  /*****核心语句******/
    
    
    return cell;
}


当执行到

    cell.microBlogFrame = microBlog;  /*****核心语句******/
这条语句的时候,  AMMicroBlogFrame类中的microBlogFrame属性的setter方法会完成一系列的 cell赋值-------内容和frame.


microBlogFrame的setter方法:


- (void)setMicroBlogFrame:(AMMicroBlogFrame *)microBlogFrame{
        _microBlogFrame = microBlogFrame;<span style="white-space:pre">	</span>  //只需也要拿到一个  AMMicroBLogFrame对象就可以完成内容和frame的设置, 参详AMMicroBlogFrame类定义
        [self setSubViewsContent];       //内容
        [self setSubViewsFrame];<span style="white-space:pre">	</span>//frame
}


- (void) setSubViewsContent{
    AMMicroBlog * microBlog = self.microBlogFrame.microBlog;
    
    self.iconView.image = [UIImage imageNamed: microBlog.icon];
    self.nameView.text = microBlog.name;
    self.vipView.image = [UIImage imageNamed: @"vip"];
    if(! microBlog.isVip){
        self.vipView.hidden = YES;
        self.nameView.textColor = [UIColor blackColor];      //因为cell的复用所以考虑对控件的属性设置,不然复用时候会出现问题
    }else{
        self.vipView.hidden = NO;
        self.nameView.textColor = [UIColor redColor];
    }
    self.textView.text = microBlog.text;
    if(microBlog.picture){
        self.pictureView.image = [UIImage imageNamed: microBlog.picture];
    }
}

- (void) setSubViewsFrame{
    
    
    self.iconView.frame = self.microBlogFrame.iconFrame;
    self.nameView.frame = self.microBlogFrame.nameFrame;
    self.vipView.frame = self.microBlogFrame.vipFrame;
    self.textView.frame = self.microBlogFrame.textFrame;
    self.pictureView.frame = self.microBlogFrame.pictureFrame;
}


Done
===========================


代码很简答, 但是其中的 处理问题的思想 很重要....再钓到鱼的同时 还 要学到如何做一个 渔者!


封装总结, 首先创建源数据类模型, AMMicroBLog,  然后在AMMicoBlogFrame中封装AMMicroBlog, 最后在AMMicroBlogCell中封装AMMicoBlogFrame...


AMMicroBlog----------->AMMicroBlogFrame------------>AMMicroBlogCell


还有一个重要的对setter和geterr方法的使用..............OC中这个语法, 确实不错!





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值