最終效果圖:

<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+19S2qNLlY2VsbLXEt+LXsDwvcD4KPHA+PC9wPgo8cCBjbGFzcz0="p1">BeyondCell
// // BeyondCell.h // 29_仿微信聊天 // // Created by beyond on 14-9-4. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import@class BeyondCellFrame; @interface BeyondCell : UITableViewCell // 一行自定義的cell,初始化的時候,全部生成各個控件並添加到contentView,然後通過cellWithCellFrame方法,將參數CellFrame(內含Msg對象)的所有成員frame和數據 設置到cell中的各個控件上面去 // 返回xib界面上寫的重用cellID + (NSString *)cellID; // 通過一個WeiboFrames模型對象(它本身就含有一個Weibo數據 模型),返回一個填充好數據的cell對象 - (BeyondCell *)cellWithCellFrame:(BeyondCellFrame *)cellFrame; @end
//
// BeyondCell.m
// 29_仿微信聊天
//
// Created by beyond on 14-9-4.
// Copyright (c) 2014年 com.beyond. All rights reserved.
//
#import "BeyondCell.h"
#import "BeyondCellFrame.h"
#import "Msg.h"
// 類擴展,又叫匿名分類
@interface BeyondCell()
{
// 1,頭像
UIImageView *_headImg;
// 2,正文內容
UILabel *_content;
// 3,大圖片
UIImageView *_bgImg;
}
@end
@implementation BeyondCell
// 返回xib界面上寫的重用cellID
+ (NSString *)cellID
{
return @"BeyondCell";
}
// 當池中沒有Cell的時候,創建出一個純潔的Cell,一次性alloc 出所有的各個子控件 ,並加到contentView
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
//選中cell後背景無顏色
self.selectionStyle = UITableViewCellSelectionStyleNone;
self.backgroundColor = [UIColor clearColor];
// 不管三七二十一,先把所有的控件實例化,並添加到contentView裡面
// 1,頭像
_headImg = [[UIImageView alloc]init];
_headImg.layer.cornerRadius = 10;
_headImg.layer.masksToBounds = YES;
[self.contentView addSubview:_headImg];
// 2,大圖片
_bgImg = [[UIImageView alloc]init];
[self.contentView addSubview:_bgImg];
// 3,正文內容,添加大背景圖片裡面
_content = [[UILabel alloc]init];
_content.backgroundColor = [UIColor clearColor];
// 正文內容用的字體,宏定義在.h
_content.font = kContentFnt;
_content.numberOfLines = 0;
_content.lineBreakMode = NSLineBreakByWordWrapping;
[_bgImg addSubview:_content];
}
return self;
}
// 通過一個Frames模型對象(它本身就含有一個數據 模型),返回一個填充好數據的cell對象,將參數Frames(內含對象)的所有成員frames和數據 設置到cell中的各個控件上面去
- (BeyondCell *)cellWithCellFrame:(BeyondCellFrame *)cellFrame
{
Msg *msg = cellFrame.msg;
// 將模型對象中的所有屬性值,全部賦值到cell對象中的成員控件上顯示
// 1,頭像
if ([msg.name isEqualToString:@"nana"]) {
_headImg.image = [UIImage imageNamed:@"icon01.jpg"];
} else {
_headImg.image = [UIImage imageNamed:@"icon02.jpg"];
}
// 5,正文內容
_content.text = msg.content;
// 6,大圖片
if ([msg.name isEqualToString:@"nana"]) {
_bgImg.image = [UIImage imageStretchedWithName:@"chatfrom_bg_normal.png" xPos:0.5 yPos:0.6];
} else {
_bgImg.image = [UIImage
imageStretchedWithName:@"chatto_bg_normal.png" xPos:0.5 yPos:0.6];
}
// 1,頭像的frame
_headImg.frame = cellFrame.headImgFrame;
// 2,正文的frame
_content.frame = cellFrame.contentFrame;
// 3,bigImg的frame
_bgImg.frame = cellFrame.contentBgImgFrame;
return self;
}
@end封裝的數據源Model
// // Msg.h // 29_仿微信聊天 // // Created by beyond on 14-9-4. // Copyright (c) 2014年 com.beyond. All rights reserved. // 模型,成員:icon,正文text #import// 內容 用的字體 #define kContentFnt [UIFont fontWithName:@"HelveticaNeue" size:18.0f] @interface Msg : NSObject // 頭像圖片名 @property (nonatomic,copy) NSString *headImg; // 消息內容 @property (nonatomic,copy) NSString *content; @property (nonatomic,copy) NSString *name; @property (nonatomic,strong) NSString *recordFileFath; // 類方法,字典 轉 對象 類似javaBean一次性填充 + (Msg *)msgWithDict:(NSDictionary *)dict; // 對象方法,設置對象的屬性後,返回對象 - (Msg *)initWithDict:(NSDictionary *)dict; + (Msg*)msgWithName:(NSString *)name content:(NSString *)content recordFilePath:(NSString *)path; @end
//
// Msg.m
// 29_仿微信聊天
//
// Created by beyond on 14-9-4.
// Copyright (c) 2014年 com.beyond. All rights reserved.
//
#import "Msg.h"
@implementation Msg
// 類方法,字典 轉 對象 類似javaBean一次性填充
+ (Msg *)msgWithDict:(NSDictionary *)dict
{
return [[self alloc]initWithDict:dict];
}
// 對象方法,設置對象的屬性後,返回對象
- (Msg *)initWithDict:(NSDictionary *)dict
{
// 必須先調用父類NSObject的init方法
if (self = [super init]) {
// 設置對象自己的屬性
// 通過遍歷 將 字典 賦值為對象各個屬性
for (NSString *key in dict) {
[self setValue:dict[key] forKeyPath:key];
}
// 一次性 將 字典 賦值為對象各個屬性
// [self setValuesForKeysWithDictionary:dict];
}
// 返回填充好的對象
return self;
}
+ (Msg*)msgWithName:(NSString *)name content:(NSString *)content recordFilePath:(NSString *)path
{
Msg *msg = [[self alloc]init];
msg.name = name;
msg.content = content;
msg.recordFileFath = path;
return msg;
}
@end
重點:根據數據源計算frame
// // BeyondCellFrame.h // 29_仿微信聊天 // // Created by beyond on 14-9-4. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import@class Msg; // 控件與控件之間的外邊距 #define kMargin 7 // 頭像的高寬 #define kHeadImgHW 85 @interface BeyondCellFrame : NSObject // 最大的Y值,就是行高 @property (nonatomic,assign,readonly) CGFloat maxY; // 重要,擁有一個成員:對象,目的是在控制器中,傳遞對象進來之後,可以通過此模型對象的數據,計算出所有的frames @property (nonatomic,strong) Msg *msg; // 頭像 的frame @property (nonatomic,assign,readonly) CGRect headImgFrame; // 聊天正文的背景圖片 的frame @property (nonatomic,assign,readonly) CGRect contentBgImgFrame; // 正文內容 的frame @property (nonatomic,assign,readonly) CGRect contentFrame; @end
//
// BeyondCellFrame.m
// 29_仿微信聊天
//
// Created by beyond on 14-9-4.
// Copyright (c) 2014年 com.beyond. All rights reserved.
//
#import "BeyondCellFrame.h"
#import "Msg.h"
@implementation BeyondCellFrame
// CellFrame類 唯一的一個方法:設置Msg的時候,可以通過其數據,計算出各個frame,以及最大的Y,也就是行高
- (void)setMsg:(Msg *)msg
{
_msg = msg;
// 具體的計算各個frame的代碼,放在這兒~~~
if ([msg.name isEqualToString:@"nana"]) {
[self standLeft:msg];
} else {
[self standRight:msg];
}
}
// 我說的放在右邊
- (void)standRight:(Msg *)msg
{
// 1,頭像的frame
// 頭像的x
CGFloat headImgX = 320 - kHeadImgHW - kMargin;
// 頭像的y
CGFloat headImgY = 0;
// 頭像的H
CGFloat headImgH = kHeadImgHW;
// 頭像的W
CGFloat headImgW = kHeadImgHW;
_headImgFrame = CGRectMake(headImgX, headImgY, headImgH, headImgW);
//===============****************=======================
// 2,bg的frame
// 寬度W
CGFloat bgW = 320 - kHeadImgHW - kMargin;
// x
CGFloat bgX = 320 - bgW - kHeadImgHW - kMargin;
// y
CGFloat bgY = 0;
// CGFloat winWidth = [[UIApplication sharedApplication] statusBarFrame].size.width;
// 高度先假設 H
CGFloat bgH = 300;
_contentBgImgFrame = CGRectMake(bgX, bgY, bgW,bgH);
//===============****************=======================
// 3,正文的frame 正文添加到圖片裡面,以圖片的左上角為 0 0
// x
CGFloat contentX = kMargin*1.5;
// y
CGFloat contentY = kMargin;
// 寬度W 先假設大於一行
CGFloat contentW = bgW - contentX - kMargin ;
CGFloat contentH = 0;
// 判斷 內容夠不夠一行...
// 根據字體得到NSString的尺寸
CGSize oneLineSize = [msg.content sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:kContentFnt,NSFontAttributeName, nil]];
CGFloat oneLineW = oneLineSize.width;
if (oneLineW < contentW) {
// 如果不夠一行
CGFloat oneLineH = oneLineSize.height;
contentX = kMargin * 1.2;
_contentFrame = CGRectMake(contentX, contentY, oneLineW,oneLineH);
contentH = oneLineH;
contentW = oneLineW;
// 5,重新調整 contentBgImg的frame的高度
// 以下三步為OC標准代碼,因為OC中不允許直接修該對象中結構體屬性的成員的值,要通過中間的臨時結構體變量
CGRect frame = _contentBgImgFrame;
frame.size.width = contentW + kMargin *3.5;
frame.size.height = contentH + kMargin * 3 ;
frame.origin.x = 320 - contentW - headImgW - kMargin*4;
_contentBgImgFrame = frame;
} else {
// 如果超過一行,按下面算法計算 高度
// 根據內容動態設置 高度
CGRect tmpRect = [msg.content boundingRectWithSize:CGSizeMake(contentW, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:[NSDictionary dictionaryWithObjectsAndKeys:kContentFnt,NSFontAttributeName, nil] context:nil];
// 高度H
contentH = tmpRect.size.height;
_contentFrame = CGRectMake(contentX, contentY, contentW,contentH);
// 5,重新調整 contentBgImg的frame的高度
// 以下三步為OC標准代碼,因為OC中不允許直接修該對象中結構體屬性的成員的值,要通過中間的臨時結構體變量
CGRect frame = _contentBgImgFrame;
frame.size.height = contentH + kMargin * 3 ;
_contentBgImgFrame = frame;
}
// 8,這個時候就可以計算最大Y 即行高了
if (headImgH > _contentBgImgFrame.size.height) {
_maxY = CGRectGetMaxY(_headImgFrame) + kMargin;
} else {
_maxY = CGRectGetMaxY(_contentBgImgFrame) + kMargin;
}
}
- (void)standLeft:(Msg *)msg
{
// 1,頭像的frame
// 頭像的x
CGFloat headImgX = kMargin;
// 頭像的y
CGFloat headImgY = kMargin;
// 頭像的H
CGFloat headImgH = kHeadImgHW;
// 頭像的W
CGFloat headImgW = kHeadImgHW;
_headImgFrame = CGRectMake(headImgX, headImgY, headImgH, headImgW);
//===============****************=======================
// 4,bg的frame
// x
CGFloat bgX = _headImgFrame.size.width + kMargin;
// y
CGFloat bgY = _headImgFrame.origin.y;
// CGFloat winWidth = [[UIApplication sharedApplication] statusBarFrame].size.width;
// 寬度W
CGFloat bgW = 320 - bgX - kMargin;
// 高度H
CGFloat bgH = 300;
_contentBgImgFrame = CGRectMake(bgX, bgY, bgW,bgH);
// 4,正文的frame 正文添加到圖片裡面,以圖片的左上角為 0 0
// x
CGFloat contentX = kMargin*3;
// y
CGFloat contentY = kMargin;
// CGFloat winWidth = [[UIApplication sharedApplication] statusBarFrame].size.width;
// 寬度W 先假設大於一行
CGFloat contentW = bgW - contentX - kMargin ;
CGFloat contentH = 0;
// 判斷 內容夠不夠一行...
// 根據字體得到NSString的尺寸
CGSize oneLineSize = [msg.content sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:kContentFnt,NSFontAttributeName, nil]];
CGFloat oneLineW = oneLineSize.width;
if (oneLineW < contentW) {
// 如果不夠一行
CGFloat oneLineH = oneLineSize.height;
contentX = kMargin * 2;
_contentFrame = CGRectMake(contentX, contentY, oneLineW,oneLineH);
contentH = oneLineH;
contentW = oneLineW;
// 5,重新調整 contentBgImg的frame的高度
// 以下三步為OC標准代碼,因為OC中不允許直接修該對象中結構體屬性的成員的值,要通過中間的臨時結構體變量
CGRect frame = _contentBgImgFrame;
frame.size.width = contentW + kMargin *3.5;
frame.size.height = contentH + kMargin * 3 ;
_contentBgImgFrame = frame;
} else {
// 如果超過一行,按下面算法計算 高度
// 根據內容動態設置 高度
CGRect tmpRect = [msg.content boundingRectWithSize:CGSizeMake(contentW, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:[NSDictionary dictionaryWithObjectsAndKeys:kContentFnt,NSFontAttributeName, nil] context:nil];
// 高度H
contentH = tmpRect.size.height;
_contentFrame = CGRectMake(contentX, contentY, contentW,contentH);
// 5,重新調整 contentBgImg的frame的高度
// 以下三步為OC標准代碼,因為OC中不允許直接修該對象中結構體屬性的成員的值,要通過中間的臨時結構體變量
CGRect frame = _contentBgImgFrame;
frame.size.height = contentH + kMargin * 3 ;
_contentBgImgFrame = frame;
}
// 8,這個時候就可以計算最大Y 即行高了
if (headImgH > _contentBgImgFrame.size.height) {
_maxY = CGRectGetMaxY(_headImgFrame) + kMargin;
} else {
_maxY = CGRectGetMaxY(_contentBgImgFrame) + kMargin;
}
}
@end
控制器
//
// BeyondViewController.m
// 29_仿微信聊天
//
// Created by beyond on 14-9-2.
// Copyright (c) 2014年 com.beyond. All rights reserved.
//
#import "BeyondViewController.h"
#import "Msg.h"
#import "BeyondCellFrame.h"
#import "BeyondCell.h"
@interface BeyondViewController ()
{
// 從plist文件中加載的所有weiboFrames(因為它已經含有一個weibo成員),返回所有的對象組成的數組
NSMutableArray *_msgFrames;
}
@end
@implementation BeyondViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 初始化 對象數組
_msgFrames = [NSMutableArray array];
}
#pragma mark - UITextField代理,發送請求
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField.text.length == 0){
return NO;
}
BeyondCellFrame *frame = [[BeyondCellFrame alloc]init];
// ***********設置的WeiboFrames的成員weibo的同時,進行了復雜的計算,並填充了WeiboFrames各個frame成員
frame.msg = [Msg msgWithName:@"jackey" content:textField.text recordFilePath:@"NoRecord"];
// 添加到對象數組
[_msgFrames addObject:frame];
[self.tableView reloadData];
return YES;
}
#pragma mark - UITableView代理方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//去除cell間隔線
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
// 返回對象數組的長度
return _msgFrames.count;
}
// 生成自定義的cell,並傳遞cellFrame給它,設置好後,返回cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1,從池中取
BeyondCell *cell = [tableView dequeueReusableCellWithIdentifier:[BeyondCell cellID]];
// 2,取不到的時候,創建一個純潔的WeiboCell
if (cell == nil) {
cell = [[BeyondCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[BeyondCell cellID]];
}
// 3,設置獨一無二的數據
BeyondCellFrame *frame = [_msgFrames objectAtIndex:indexPath.row];
cell = [cell cellWithCellFrame:frame];
return cell;
}
// cellFrame對象數組有每一行的行高,其內部已經計算好了
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// cellFrame的成員有:Msg數據模型對象,以及根據數據模型計算出來的所有的frames,以及最大的Y即對應數據模型的行高
BeyondCellFrame *frame = [_msgFrames objectAtIndex:indexPath.row];
return frame.maxY;
}
// 恢復view全屏,並且讓鍵盤退出
- (void)exitKeyboard
{
[UIView animateWithDuration:0.2 animations:^{
self.view.frame = CGRectMake(0, 0, self.view.frame.size.width, [[UIScreen mainScreen]bounds].size.height);
}completion:^(BOOL finished){}];
[_chatInput resignFirstResponder];
}
// 滾至表格最後一行
- (void)scrollToLastCell;
{
if (_msgFrames.count >1) {
[self.chatTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_msgFrames.count-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}