简述整个项目的开发过程

1.在main.stroybord 中搭建基本界面

2.创建模型,一个是数据模型,一个是frame模型

3.实现对cell操作的封装

4.解决显示时间的细节问题

5.解决聊天内容的背景问题

6.用通知机制监听键盘

7.发送消息

一、在main.stroybord 中搭建基本界面

二、创建模型,一个是数据模型,一个是frame模型

根据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;
}

三、实现对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";//先从缓存池中取cellZLMessageCell *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

四、解决显示时间的细节问题

如果时间是一样的,就只显示一个时间

在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.

 五、解决聊天内容的背景问题

//设置图片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};

六、用通知机制监听键盘

首先介绍一下通知机制

代码如下:

// 监听键盘的通知[[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.取得键盘最后的frameCGRect 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.设置文本框左边显示的viewself.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:56msg.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];
}

最终界面如图:

学的不精,笔记做的不好,见谅见谅。。。。。。。

转载于:https://www.cnblogs.com/Lorraine1/p/5766054.html

关情纸尾-----UIKit基础--QQ自定义布心布局相关推荐

  1. 关情纸尾-----Quartz2D-绘制富文本,绘制图片.

    一般使用UIKit给我们提供的绘图来绘制一些文字,图片这些东西. UIKit给我们提供画图的方法底层也是分为四步.所以也必须在drawRect方法当中去写. 1.如何画文字? 先创建好要画的文字 使用 ...

  2. special effects - 鼠标移动,出现自定义的表情拖尾

    鼠标移动,出现自定义的表情拖尾 一. 效果图 二. 实现代码 一. 效果图 二. 实现代码 <!DOCTYPE html> <html lang="en"> ...

  3. django基础、自定义框架

    django基础 HTTP 超文本传输协议 是在应用层 如今广泛使用的是HTTP1.1 默认为80端口 5层协议: HTTP协议 应用层 TCP/UDP协议 运输层 IP 网络层 数据链路层 物理层 ...

  4. 纸的大小图解_折纸大全图解基础之如何裁切美元尺寸纸张

    本育儿文章是育儿天堂最新发布的<折纸大全图解基础之如何裁切美元尺寸纸张>的详细页面,觉得有用就收藏了,这里给大家转摘到育儿天堂,为了大家阅读方便. 折纸大全图解中有一类折纸教程是比力奇特的 ...

  5. QQ自定义在线机型状态源码

    简介: QQ自定义在线状态源码 网盘下载地址: http://kekewl.cc/nr2LzGn4gQN0 图片:

  6. 柱底反力求和lisp软件_AutoLISP 基础——认识自定义函数

    AutoLISP 基础--认识自定义函数 ( 本文由 LL_J ‚认识自定义函数‛和‚ Autolisp 编程心得‛两篇巨著合成, 并融入了其他人的一些经验,以快速打通你的任督二脉 --自贡黄明儒注 ...

  7. 纸壳CMS现已支持自定义扩展字段

    简介 纸壳CMS是开源免费的可视化内容管理系统. GitHub https://github.com/SeriaWei/ZKEACMS 自定义字段 纸壳CMS现已支持自定义字段,在不修改代码的情况下, ...

  8. 零基础创建自定义gym环境——以股票市场为例

    零基础创建自定义gym环境--以股票市场为例 翻译自medium上的一篇文章Create custom gym environments from scratch - A stock market e ...

  9. QQ自定义个人文件夹

    QQ自定义的配置是从X:\Users\Public\Documents\Tencent\QQ\UserDataInfo.ini这个文件中读出来的,当设置QQ自定义个人文件夹却无效,可能是上面的配置文件 ...

最新文章

  1. 推荐个所见即所得的编辑器
  2. 学习深度网络需要直观的感知
  3. 迭代器与生成器yield
  4. 移动端web设计尺寸_移动端页面设计规范尺寸大起底
  5. 12019.LMT84模拟温度传感器
  6. oracle 客户端 for mips,盒子里的世界: MIPS虚拟机(zt)
  7. 浅析ASP.NET 2.0的用户密码加密机制
  8. Untiy3D里用C#做出连线题目~
  9. Linux 发展史小览
  10. whisper客服源码_whisper客服系统兼容HTTPS方案
  11. CAN通讯程序C语言,AT90CAN单片机CAN通信模块介绍及软件编程
  12. php 一天是多少秒,1天 等于 86400 秒?
  13. [Zer0pts2020]Can you guess it?
  14. 手机端上传照片实现 压缩、拖放、缩放、裁剪、合成拼图等功能
  15. 红外热成像仪测温模块简要介绍说明
  16. IDEA报错:Plugin ‘org.springframework.boot:spring-boot-maven-plugin:‘ not found
  17. 超强技巧分享,如何将人民币大小写转换?
  18. 千里走单骑:02-北京到上海骑记--Day1.首日征程
  19. 前端国际化如何对中文——>英文自动化翻译小demo
  20. 大容量充电宝或成乘机“累赘”

热门文章

  1. Andriod 破解之道(一)
  2. 一个表格中文字怎么换行_Excel表格中怎样快速将阿拉伯数字转化为大写文字?这样操作一键完成...
  3. QGraphicsScene 的简单理解--关于手册的简要翻译
  4. mysql 接口访问_MySQL的数据库访问接口-阿里云开发者社区
  5. vue 心跳监控_vue websocket 加心跳
  6. 2017年10月05日普及组 约数
  7. Codeforces 1326F Wise Men (容斥原理、状压 DP、子集和变换、划分数)
  8. 关于三维莫队问题的一些思考和探究
  9. mvc和php的关系,php - 什么是HMVC模式?
  10. 点击延迟_300ms 延迟是什么,如何解决