代码改变世界

iOS UI基础-9.2 UITableView 简单微博列表

2015-09-18 22:25  jiangys  阅读(418)  评论(0编辑  收藏  举报

概述

我们要实现的效果:

这个界面布局也是UITableView实现的,其中的内容就是UITableViewCell,只是这个UITableViewCell是用户自定义实现的。虽然系统自带的UITableViewCell已经够强大了,但是这个界面布局并不能满足我们的需求。

在上面的cell布局里,我们可以知道,每个cell的高度都是不固定的。因此,我们通过代码来自定义cell.

代码自定义cell

步骤:

1.新建一个继承自UITableViewCell的类

2.重写initWithStyle:reuseIdentifier:方法

  • 添加所有需要显示的子控件(不需要设置子控件的数据和frame,  子控件要添加到contentView中)
  • 进行子控件一次性的属性设置(有些属性只需要设置一次, 比如字体\固定的图片)

3.提供2个模型

  • 数据模型: 存放文字数据\图片数据
  • frame模型: 存放数据模型\所有子控件的frame\cell的高度

4.重写frame模型里的数据模型的Setter方法:在这个方法中,设置子控制的frame和每个cell高度

5.cell拥有一个frame模型(不要直接拥有数据模型)

6.重写frame模型属性的setter方法: 在这个方法中设置子控件的显示数据和frame

7.frame模型数据的初始化已经采取懒加载的方式(每一个cell对应的frame模型数据只加载一次)

下面分步来写相应的代码

Cell视图设置

1.新建一个继承自UITableViewCell的类,重写initWithStyle:reuseIdentifier:方法

2.重写frame模型属性的setter方法: 在这个方法中设置子控件的显示数据和frame

StatusCell.h

#import <UIKit/UIKit.h>
@class StatusFrame;

@interface StatusCell : UITableViewCell
@property (nonatomic, strong) StatusFrame *statusFrame;

+ (instancetype)cellWithTableView:(UITableView *)tableView;
@end

StatusCell.m

//
//  StatusCell.m
//  9.2 - 简单微博
//
//  Created by apple on 15/9/18.
//  Copyright (c) 2015年 weconex. All rights reserved.
//

// 昵称的字体
#define MJNameFont [UIFont systemFontOfSize:14]
// 正文的字体
#define MJTextFont [UIFont systemFontOfSize:15]

#import "StatusCell.h"
#import "Status.h"
#import "StatusFrame.h"

@interface StatusCell()
/**
 *  头像
 */
@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 StatusCell

/**
 *  重写initWithStyle:reuseIdentifier:方法
 *  添加所有需要显示的子控件(不需要设置子控件的数据和frame,子控件要添加到contentView中)
 *  进行子控件一次性的属性设置(有些属性只需要设置一次, 比如字体\固定的图片)
 */
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    if (self==[super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        // 1.头像
        UIImageView *iconView = [[UIImageView alloc] init];
        [self.contentView addSubview:iconView];
        self.iconView = iconView;//相当于xib的拉线
        
        // 2.昵称
        UILabel *nameView = [[UILabel alloc] init];
        nameView.font = MJNameFont;
        [self.contentView addSubview:nameView];
        self.nameView = nameView;
        
        // 3.会员图标
        UIImageView *vipView = [[UIImageView alloc] init];
        vipView.image = [UIImage imageNamed:@"vip"];
        [self.contentView addSubview:vipView];
        self.vipView = vipView;
        
        // 4.正文
        UILabel *textView = [[UILabel alloc] init];
        textView.numberOfLines = 0;
        textView.font = MJTextFont;
        [self.contentView addSubview:textView];
        self.textView = textView;
        
        // 5.配图
        UIImageView *pictureView = [[UIImageView alloc] init];
        [self.contentView addSubview:pictureView];
        self.pictureView = pictureView;
    }
    return self;
}

/**
 *  在这个方法中设置子控件的frame和显示数据
 */
-(void)setStatusFrame:(StatusFrame *)statusFrame
{
    _statusFrame = statusFrame;
    
    // 1.设置数据
    [self settingData];
    
    // 2.设置frame
    [self settingFrame];
}

/**
 *  设置数据
 */
- (void)settingData
{
    // 微博数据
    Status *status = self.statusFrame.status;
    
    // 1.头像
    self.iconView.image = [UIImage imageNamed:status.icon];
    
    // 2.昵称
    self.nameView.text = status.name;
    
    // 3.会员图标
    if (status.vip) {
        self.vipView.hidden = NO;
        
        self.nameView.textColor = [UIColor redColor];
    } else {
        self.vipView.hidden = YES;
        
        self.nameView.textColor = [UIColor blackColor];
    }
    
    // 4.正文
    self.textView.text = status.text;
    
    // 5.配图
    if (status.picture) { // 有配图
        self.pictureView.hidden = NO;
        self.pictureView.image = [UIImage imageNamed:status.picture];
    } else { // 没有配图
        self.pictureView.hidden = YES;
    }
}

/**
 *  设置frame
 */
- (void)settingFrame
{
    // 1.头像
    self.iconView.frame = self.statusFrame.iconF;
    
    // 2.昵称
    self.nameView.frame = self.statusFrame.nameF;
    
    // 3.会员图标
    self.vipView.frame = self.statusFrame.vipF;
    
    // 4.正文
    self.textView.frame = self.statusFrame.textF;
    
    // 5.配图
    if (self.statusFrame.status.picture) {// 有配图
        self.pictureView.frame = self.statusFrame.pictureF;
    }
}

+ (instancetype)cellWithTableView:(UITableView *)tableView
{
    static NSString *ID = @"status";
    StatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[StatusCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
    }
    return cell;
}

@end

 

模型定义

提供2个模型

  • 数据模型(Status): 存放文字数据\图片数据
  • frame模型(StatusFrame): 存放数据模型\所有子控件的frame\cell的高度

重写frame模型里的数据模型(Status)的Setter方法:在这个方法中,设置子控制的frame和每个cell高度

Status.h

//
//  Status.h
//  9.2 - 简单微博
//
//  Created by apple on 15/9/18.
//  Copyright (c) 2015年 weconex. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface Status : UICollectionViewCell
@property (nonatomic, copy) NSString *text; // 内容
@property (nonatomic, copy) NSString *icon; // 头像
@property (nonatomic, copy) NSString *name; // 昵称
@property (nonatomic, copy) NSString *picture; // 配图
@property (nonatomic, assign) BOOL vip;

- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)statusWithDict:(NSDictionary *)dict;
@end

Status.m

//
//  Status.m
//  9.2 - 简单微博
//
//  Created by apple on 15/9/18.
//  Copyright (c) 2015年 weconex. All rights reserved.
//

#import "Status.h"

@implementation Status

- (instancetype)initWithDict:(NSDictionary *)dict
{
    if (self = [super init]) {
        [self setValuesForKeysWithDictionary:dict];
    }
    return self;
}

+ (instancetype)statusWithDict:(NSDictionary *)dict
{
    return [[self alloc] initWithDict:dict];
}
@end

StatusFrame.h

//
//  StatusFrame.h
//  9.2 - 简单微博
//
//  Created by apple on 15/9/18.
//  Copyright (c) 2015年 weconex. All rights reserved.
//  frame模型: 存放数据模型\所有子控件的frame\cell的高度

#import <UIKit/UIKit.h>
@class Status;

@interface StatusFrame : UITableViewCell

/**
 *  头像的frame
 */
@property (nonatomic, assign, readonly) CGRect iconF;
/**
 *  昵称的frame
 */
@property (nonatomic, assign, readonly) CGRect nameF;
/**
 *  会员图标的frame
 */
@property (nonatomic, assign, readonly) CGRect vipF;
/**
 *  正文的frame
 */
@property (nonatomic, assign, readonly) CGRect textF;
/**
 *  配图的frame
 */
@property (nonatomic, assign, readonly) CGRect pictureF;

/**
 *  cell的高度
 */
@property (nonatomic, assign, readonly) CGFloat cellHeight;

/**
 *  存放数据模型
 */
@property (nonatomic, strong) Status *status;

@end

StatusFrame.m

//
//  StatusFrame.m
//  9.2 - 简单微博
//
//  Created by apple on 15/9/18.
//  Copyright (c) 2015年 weconex. All rights reserved.
//
// 昵称的字体
#define MJNameFont [UIFont systemFontOfSize:14]
// 正文的字体
#define MJTextFont [UIFont systemFontOfSize:15]

#import "StatusFrame.h"
#import "Status.h"

@implementation StatusFrame

/**
 *  计算文字尺寸
 *
 *  @param text    需要计算尺寸的文字
 *  @param font    文字的字体
 *  @param maxSize 文字的最大尺寸
 */
- (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font maxSize:(CGSize)maxSize
{
    NSDictionary *attrs = @{NSFontAttributeName : font};
    return [text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;
}

/**
 *  设置每一条微博的Frame 及cell的高度
 *
 *  @param status 微博模型
 */
-(void)setStatus:(Status *)status
{
    _status = status;
    
    // 子控件之间的间距
    CGFloat padding = 10;
    
    // 1.头像
    CGFloat iconX = padding;
    CGFloat iconY = padding;
    CGFloat iconW = 30;
    CGFloat iconH = 30;
    _iconF = CGRectMake(iconX, iconY, iconW, iconH);
    
    // 2.昵称
    // 文字的字体
    CGSize nameSize = [self sizeWithText:self.status.name font:MJNameFont maxSize:CGSizeMake(MAXFLOAT, MAXFLOAT)];
    CGFloat nameX = CGRectGetMaxX(_iconF) + padding;
    CGFloat nameY = iconY + (iconH - nameSize.height) * 0.5;
    _nameF = CGRectMake(nameX, nameY, nameSize.width, nameSize.height);
    
    // 3.会员图标
    CGFloat vipX = CGRectGetMaxX(_nameF) + padding;
    CGFloat vipY = nameY;
    CGFloat vipW = 14;
    CGFloat vipH = 14;
    _vipF = CGRectMake(vipX, vipY, vipW, vipH);
    
    // 4.正文
    CGFloat textX = iconX;
    CGFloat textY = CGRectGetMaxY(_iconF) + padding;
    CGSize textSize = [self sizeWithText:self.status.text font:MJTextFont maxSize:CGSizeMake(300, MAXFLOAT)];
    _textF = CGRectMake(textX, textY, textSize.width, textSize.height);
    
    // 5.配图
    if (self.status.picture) {// 有配图
        CGFloat pictureX = textX;
        CGFloat pictureY = CGRectGetMaxY(_textF) + padding;
        CGFloat pictureW = 100;
        CGFloat pictureH = 100;
        _pictureF = CGRectMake(pictureX, pictureY, pictureW, pictureH);
        
        _cellHeight = CGRectGetMaxY(_pictureF) + padding;
    } else {
        _cellHeight = CGRectGetMaxY(_textF) + padding;
    }
}
@end

控制器使用Cell

cell拥有一个frame模型(不要直接拥有数据模型)

frame模型数据的初始化已经采取懒加载的方式(每一个cell对应的frame模型数据只加载一次)

ViewController.m

//
//  ViewController.m
//  9.2 - 简单微博
//
//  Created by apple on 15/9/18.
//  Copyright (c) 2015年 weconex. All rights reserved.
//

#import "ViewController.h"
#import "StatusFrame.h"
#import "Status.h"
#import "StatusCell.h"

@interface ViewController ()
/**
 *  存放所有cell的frame模型数据
 */
@property (nonatomic, strong) NSArray *statusFrames;
@end

@implementation ViewController

-(NSArray *)statusFrames
{
    if (_statusFrames==nil) {
        // 初始化
        // 1.获得plist的全路径
        NSString *path = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil];
        
        // 2.加载数组
        NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
        
        // 3.将dictArray里面的所有字典转成模型对象,放到新的数组中
        NSMutableArray *statusFrameArray = [NSMutableArray array];
        for (NSDictionary *dict in dictArray) {
            // 3.1.创建Status模型对象
            Status *status = [Status statusWithDict:dict];
            
            // 3.2.创建StatusFrame模型对象
            StatusFrame *statusFrame = [[StatusFrame alloc] init];
            statusFrame.status = status;
            
            // 3.2.添加模型对象到数组中
            [statusFrameArray addObject:statusFrame];
        }
        
        // 4.赋值
        _statusFrames = statusFrameArray;
    }
    
    return _statusFrames;
}


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

// 隐藏导航栏
- (BOOL)prefersStatusBarHidden
{
    return YES;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.statusFrames.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 1.创建cell
    StatusCell *cell = [StatusCell cellWithTableView:tableView];
    
    // 2.在这个方法算好了cell的高度
    cell.statusFrame = self.statusFrames[indexPath.row];
    
    // 3.返回cell
    return cell;
}

/**
 *  返回每个单元格的高度
 */
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    StatusFrame *statusFrame=self.statusFrames[indexPath.row];
    return statusFrame.cellHeight;
}

@end

请按照步骤来,当然,上面的代码有一些步骤是混合在一起了。

 

源码下载:点击下载