簡述整個項目的開發過程數組
1.在main.stroybord 中搭建基本界面緩存
2.建立模型,一個是數據模型,一個是frame模型ide
3.實現對cell操做的封裝字體
4.解決顯示時間的細節問題動畫
5.解決聊天內容的背景問題atom
6.用通知機制監聽鍵盤spa
7.發送消息代理
1、在main.stroybord 中搭建基本界面code
2、建立模型,一個是數據模型,一個是frame模型orm
根據message.plist文件建立模型
數據模型ZLMessage
1 #import <Foundation/Foundation.h> 2 3 4 5 typedef enum { 6 ZLMessageTypeMe = 0, //0表示本身,而且系統默認的 也是0 ,而後遞增長1 7 ZLMessageTypeOther 8 }ZLMessageType; 9 10 @interface ZLMessage : NSObject 11 12 //時間 13 @property (nonatomic, strong) NSString *time; 14 15 //內容 16 @property (nonatomic, strong)NSString *text; 17 18 //類型 19 @property (nonatomic, assign)ZLMessageType type; 20 21 //是否顯示時間 22 @property (nonatomic, assign)BOOL hideTime; 23 24 +(instancetype)messageWithDict:(NSDictionary *)dict; 25 -(instancetype)initWithDict:(NSDictionary *)dict; 26 @end
#import "ZLMessage.h" @implementation ZLMessage +(instancetype)messageWithDict:(NSDictionary *)dict{ return [[self alloc] initWithDict:dict]; } -(instancetype)initWithDict:(NSDictionary *)dict{ if (self = [super init]) { [self setValuesForKeysWithDictionary:dict]; } return self; } @end
frame模型ZLMessageFrame
#import <Foundation/Foundation.h> #import "UIKit/UIkit.h" @class ZLMessage; @interface ZLMessageFrame : NSObject // 頭像的frame @property (nonatomic, assign, readonly) CGRect iconF; //時間的frame @property (nonatomic, assign, readonly) CGRect timeF; //正文的frame @property (nonatomic, assign, readonly) CGRect textF; //cell的高度 @property (nonatomic, assign, readonly) CGFloat cellHeight; //數據模型 @property (nonatomic, strong) ZLMessage* message; @end
#define ZLTextFont [UIFont systemFontOfSize:15]
#import "ZLMessageFrame.h"
#import "ZLMessage.h"
@implementation ZLMessageFrame
/**
* 計算文字尺寸
*
* @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;
}
-(void)setMessage:(ZLMessage *)message
{ _message = message;
//計算頭像,正文,時間的Frame
CGFloat sreenW = [UIScreen mainScreen].bounds.size.width;
CGFloat padding = 10;
//1.時間
CGFloat timeX = 0;
CGFloat timeY = 0;
CGFloat timeW = sreenW;
CGFloat timeH = 40;
_timeF = CGRectMake(timeX, timeY, timeW, timeH);
//2.頭像
CGFloat iconY = CGRectGetMaxY(_timeF);
CGFloat iconW = 40;
CGFloat iconH = 40;
CGFloat iconX;
if (message.type == ZLMessageTypeOther) {
iconX = padding;
} else {
iconX = sreenW - iconW - padding;
}
_iconF = CGRectMake(iconX, iconY, iconW, iconH);
//3.正文
CGFloat textY = iconY;
CGFloat textX;
//文字的尺寸
CGSize textMaxSize = CGSizeMake(150, MAXFLOAT);
CGSize textSize = [self sizeWithText:message.text font:ZLTextFont maxSize:textMaxSize];
if (message.type == ZLMessageTypeOther) {
textX = CGRectGetMaxX(_iconF) + padding;
} else {
textX = iconX - padding - textSize.width;
}
_textF = (CGRect){{textX,textY},textSize};
//cell 的高度
CGFloat textMaxY = CGRectGetMaxY(_textF);
CGFloat iconMaxY = CGRectGetMaxY(_iconF);
_cellHeight = MAX(textMaxY, iconMaxY) + padding;
}
@end
- (NSMutableArray *)messagesFrames { if (_messagesFrames == nil){ //取出路徑 NSString *path = [[NSBundle mainBundle] pathForResource:@"messages.plist" ofType:nil]; //取出該路勁下的數組 NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; NSMutableArray *mfArray = [NSMutableArray array]; //遍歷數組造成字典 for (NSDictionary* dict in dictArray) { //消息模型 ZLMessage *msg = [ZLMessage messageWithDict:dict]; //frame模型 ZLMessageFrame *mgf =[[ZLMessageFrame alloc] init]; mgf.message = msg; [mfArray addObject:mgf]; } _messagesFrames = mfArray; } return _messagesFrames; }
3、實現對cell操做的封裝
#import <UIKit/UIKit.h> @class ZLMessageFrame; @interface ZLMessageCell : UITableViewCell +(instancetype) cellWithTableView:(UITableView *)tableView; @property (nonatomic, strong) ZLMessageFrame* messageFrame; @end
#define ZLTextFont [UIFont systemFontOfSize:15] #import "ZLMessageCell.h" #import "ZLMessageFrame.h" #import "ZLMessage.h" @interface ZLMessageCell() //時間 @property (nonatomic, weak) UILabel *timeView; //頭像 @property (nonatomic, weak) UIImageView *iconView; //正文 @property (nonatomic, weak) UIButton *textView;; @end @implementation ZLMessageCell +(instancetype) cellWithTableView:(UITableView *)tableView { static NSString *ID = @"message"; //先從緩存池中取cell ZLMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:ID ]; //若是緩存池中沒有就本身建立cell,而且要帶有標記 if (cell == nil) { cell = [[ZLMessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; } return cell; } //作一次性的初始化 -(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self){ //子控件的建立和初始化 //1.時間 UILabel *timeView = [[UILabel alloc] init]; timeView.textColor = [UIColor grayColor]; timeView.textAlignment = NSTextAlignmentCenter; timeView.font = [UIFont systemFontOfSize:13]; [self.contentView addSubview:timeView]; self.timeView = timeView; //2.頭像 UIImageView *iconView = [[UIImageView alloc] init]; //iconView.backgroundColor = [UIColor redColor]; [self.contentView addSubview:iconView]; self.iconView = iconView; //3.正文 UIButton *textView = [[UIButton alloc] init]; textView.titleLabel.numberOfLines = 0;//自動換行 textView.backgroundColor = [UIColor purpleColor]; textView.titleLabel.font = ZLTextFont; [self.contentView addSubview:textView]; self.textView = textView; } return self; } -(void)setMessageFrame:(ZLMessageFrame *)messageFrame { _messageFrame = messageFrame; ZLMessage *message = messageFrame.message; //時間 self.timeView.text = message.time; self.timeView.frame = messageFrame.timeF; //頭像 NSString *icon = (message.type == ZLMessageTypeMe) ? @"me" : @"other"; self.iconView.image = [UIImage imageNamed:icon]; self.iconView.frame = messageFrame.iconF; //正文 [self.textView setTitle:message.text forState:UIControlStateNormal]; self.textView.frame = messageFrame.textF; } @end
4、解決顯示時間的細節問題
若是時間是同樣的,就只顯示一個時間
在ZLMessage.h文件中添加屬性 //是否顯示時間 @property (nonatomic, assign)BOOL hideTime;
在控制器.m文件中添加代碼,即取出上一個模型和剛剛添加的模型的時間做比較,若是時間同樣,就只顯示上一個模型的time數據 //遍歷數組造成字典 for (NSDictionary* dict in dictArray) { //消息模型 ZLMessage *msg = [ZLMessage messageWithDict:dict]; //取出上一個模型 ZLMessageFrame *lastMf = [mfArray lastObject]; ZLMessage *lastMg = lastMf.message; msg.hideTime = [msg.time isEqualToString:lastMg.
5、解決聊天內容的背景問題
//設置圖片 if (message.type == MJMessageTypeMe) { // 本身發的,藍色 [self.textView setBackgroundImage:[UIImage resizableImage:@"chat_send_nor"] forState:UIControlStateNormal]; } else { // 別人發的,白色 [self.textView setBackgroundImage:[UIImage resizableImage:@"chat_recive_nor"] forState:UIControlStateNormal]; }
//爲了顯示對話框背景圖,須要設置正文按鈕的內邊距 textView.contentEdgeInsets = UIEdgeInsetsMake(ZLTextPadding, ZLTextPadding, ZLTextPadding, ZLTextPadding);
//文字計算的最大尺寸 CGSize textMaxSize = CGSizeMake(150, MAXFLOAT); //文字計算出來的真實尺寸(顯示文字的尺寸) CGSize textSize = [self sizeWithText:message.text font:ZLTextFont maxSize:textMaxSize]; //按鈕最終的尺寸 CGSize textBtnSize = CGSizeMake(textSize.width +ZLTextPadding *2, textSize.height +ZLTextPadding *2); if (message.type == ZLMessageTypeOther) { textX = CGRectGetMaxX(_iconF) + padding; } else { textX = iconX - padding - textBtnSize.width; } // _textF = CGRectMake(textX, textY, textSize.width +40, textSize.height +40); _textF = (CGRect){{textX,textY},textBtnSize};
6、用通知機制監聽鍵盤
首先介紹一下通知機制
代碼以下:
// 監聽鍵盤的通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; //取消監聽 - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } /** * 當鍵盤改變了frame(位置和尺寸)的時候調用 */ - (void)keyboardWillChangeFrame:(NSNotification *)note { // 設置窗口的顏色 self.view.window.backgroundColor = self.tableView.backgroundColor; // 0.取出鍵盤動畫的時間 CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; // 1.取得鍵盤最後的frame CGRect keyboardFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; // 2.計算控制器的view須要平移的距離 CGFloat transformY = keyboardFrame.origin.y - self.view.frame.size.height; // 3.執行動畫 [UIView animateWithDuration:duration animations:^{ self.view.transform = CGAffineTransformMakeTranslation(0, transformY); }]; }
7.發送消息
//監聽文本框 #import "MJViewController.h" #import "MJMessage.h" #import "MJMessageFrame.h" #import "MJMessageCell.h" @interface MJViewController () <UITableViewDataSource, UITableViewDelegate, UITableViewDelegate, UITextFieldDelegate> @property (weak, nonatomic) IBOutlet UITableView *tableView; @property (nonatomic, strong) NSMutableArray *messageFrames; @property (weak, nonatomic) IBOutlet UITextField *inputView; @property (nonatomic, strong) NSDictionary *autoreply; @end @implementation MJViewController - (void)viewDidLoad { [super viewDidLoad]; // 1.表格的設置 // 去除分割線 self.tableView.backgroundColor = [UIColor colorWithRed:235/255.0 green:235/255.0 blue:235/255.0 alpha:1.0]; self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; self.tableView.allowsSelection = NO; // 不容許選中 self.tableView.delegate = self; // 2.監聽鍵盤的通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; // 3.設置文本框左邊顯示的view self.inputView.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 8, 0)]; // 永遠顯示 self.inputView.leftViewMode = UITextFieldViewModeAlways; self.inputView.delegate = self; } /** * 發送一條消息 */ - (void)addMessage:(NSString *)text type:(MJMessageType)type { // 1.數據模型 MJMessage *msg = [[MJMessage alloc] init]; msg.type = type; msg.text = text; // 設置數據模型的時間 NSDate *now = [NSDate date]; NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; fmt.dateFormat = @"HH:mm"; // NSDate ---> NSString // NSString ---> NSDate // fmt.dateFormat = @"yyyy-MM-dd HH:mm:ss"; // 2014-08-09 15:45:56 // 09/08/2014 15:45:56 msg.time = [fmt stringFromDate:now]; // 看是否須要隱藏時間 MJMessageFrame *lastMf = [self.messageFrames lastObject]; MJMessage *lastMsg = lastMf.message; msg.hideTime = [msg.time isEqualToString:lastMsg.time]; // 2.frame模型 MJMessageFrame *mf = [[MJMessageFrame alloc] init]; mf.message = msg; [self.messageFrames addObject:mf]; // 3.刷新表格 [self.tableView reloadData]; // 4.自動滾動表格到最後一行 NSIndexPath *lastPath = [NSIndexPath indexPathForRow:self.messageFrames.count - 1 inSection:0]; [self.tableView scrollToRowAtIndexPath:lastPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; } /** * 根據本身發的內容取得自動回覆的內容 * * @param text 本身發的內容 */ - (NSString *)replayWithText:(NSString *)text { for (int i = 0; i<text.length; i++) { NSString *word = [text substringWithRange:NSMakeRange(i, 1)]; if (self.autoreply[word]) return self.autoreply[word]; } return @"滾蛋"; } #pragma mark - 文本框代理 /** * 點擊了return按鈕(鍵盤最右下角的按鈕)就會調用 */ - (BOOL)textFieldShouldReturn:(UITextField *)textField { // 1.本身發一條消息 [self addMessage:textField.text type:MJMessageTypeMe]; // 2.自動回覆一條消息 NSString *reply = [self replayWithText:textField.text]; [self addMessage:reply type:MJMessageTypeOther]; // 3.清空文字 self.inputView.text = nil; // 返回YES便可 return YES; } /** * 當開始拖拽表格的時候就會調用 */ - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { // 退出鍵盤 [self.view endEditing:YES]; }
最終界面如圖:
學的不精,筆記作的很差,見諒見諒。。。。。。。