1.键盘通知
•我们经常需要在键盘弹出或者隐藏的时候做一些特定的操作,因此需要监听键盘的状态
•键盘状态改变的时候,系统会发出一些特定的通知
UIKeyboardWillShowNotification
/
/
键盘即将显示
UIKeyboardDidShowNotification
//
键盘显示完毕
UIKeyboardWillHideNotification
/
/
键盘即将隐藏
UIKeyboardDidHideNotification
//
键盘隐藏完毕
UIKeyboardWillChangeFrameNotification
/
/
键盘的位置尺寸即将发生改变
UIKeyboardDidChangeFrameNotification
//
键盘的位置尺寸改变完毕
2.利用通知机制实现键盘处理 在QQ聊天布局中的应用案例
2.1实现文件结构
2.2Category
// UIImage+Extension.m#import "UIImage+Extension.h"
@implementation UIImage (Extension)
/**
* 返回一张可以随意拉伸不变形的图片
*
* @param name 图片名字
*/
+ (UIImage *)resizableImage:(NSString *)name
{
UIImage *normal = [UIImage imageNamed:name];
CGFloat w = normal.size.width * 0.5;
CGFloat h = normal.size.height * 0.5;
return [normal resizableImageWithCapInsets:UIEdgeInsetsMake(h, w, h, w)];
}
@end
//
// NSString+Extension.m
#import "NSString+Extension.h"
@implementation NSString (Extension)
- (CGSize)sizeWithFont:(UIFont *)font maxSize:(CGSize)maxSize
{
NSDictionary *attrs = @{NSFontAttributeName : font};
return [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;
}
@end
2.3View
// MJMessageCell.m
#import "MJMessageCell.h"
#import "MJMessageFrame.h"
#import "MJMessage.h"
#import "UIImage+Extension.h"
@interface MJMessageCell()
/**
* 时间
*/
@property (nonatomic, weak) UILabel *timeView;
/**
* 头像
*/
@property (nonatomic, weak) UIImageView *iconView;
/**
* 正文
*/
@property (nonatomic, weak) UIButton *textView;
@end
@implementation MJMessageCell
+ (instancetype)cellWithTableView:(UITableView *)tableView
{
static NSString *ID = @"message";
MJMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[MJMessageCell 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.textAlignment = NSTextAlignmentCenter;
timeView.textColor = [UIColor grayColor];
timeView.font = [UIFont systemFontOfSize:13];
[self.contentView addSubview:timeView];
self.timeView = timeView;
// 2.头像
UIImageView *iconView = [[UIImageView alloc] init];
[self.contentView addSubview:iconView];
self.iconView = iconView;
// 3.正文
UIButton *textView = [[UIButton alloc] init];
textView.titleLabel.numberOfLines = 0; // 自动换行
textView.titleLabel.font = MJTextFont;
textView.contentEdgeInsets = UIEdgeInsetsMake(MJTextPadding, MJTextPadding, MJTextPadding, MJTextPadding);
[textView setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[self.contentView addSubview:textView];
self.textView = textView;
// 4.设置cell的背景色
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)setMessageFrame:(MJMessageFrame *)messageFrame
{
_messageFrame = messageFrame;
MJMessage *message = messageFrame.message;
// 1.时间
self.timeView.text = message.time;
self.timeView.frame = messageFrame.timeF;
// 2.头像
NSString *icon = (message.type == MJMessageTypeMe) ? @"me" : @"other";
self.iconView.image = [UIImage imageNamed:icon];
self.iconView.frame = messageFrame.iconF;
// 3.正文
[self.textView setTitle:message.text forState:UIControlStateNormal];
self.textView.frame = messageFrame.textF;
// 4.正文的背景
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];
}
}
// 拉伸图片
// [normal stretchableImageWithLeftCapWidth:<#(NSInteger)#> topCapHeight:<#(NSInteger)#>];
// [normal resizableImageWithCapInsets:<#(UIEdgeInsets)#> resizingMode:<#(UIImageResizingMode)#>];
@end
2.4Controller
// MJViewController.m
#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;
@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.监听键盘的通知 self表示控制器来监听
[[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)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - 文本框代理
/**
* 点击了return按钮(键盘最右下角的按钮)就会调用
*/
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
NSLog(@"%@", textField.text);
// 返回YES即可
return YES;
}
/**
* 当键盘改变了frame(位置和尺寸)的时候调用
*/
- (void)keyboardWillChangeFrame:(NSNotification *)note
{
// 设置窗口的颜色(控制器的UIwindow)
self.view.window.backgroundColor = self.tableView.backgroundColor;
// 0.取出键盘动画的时间
CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 1.取得键盘最后的frame 字典里面存放的都是对象 CGRectValue把note.userInfo[UIKeyboardFrameEndUserInfoKey] 转成结构体
CGRect keyboardFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
// 2.计算控制器的view需要平移的距离
CGFloat transformY = keyboardFrame.origin.y - self.view.frame.size.height;
// 3.执行动画 (transform 平移、缩放、旋转)
[UIView animateWithDuration:duration animations:^{
self.view.transform = CGAffineTransformMakeTranslation(0, transformY);
}];
}
/*
UIKeyboardAnimationCurveUserInfoKey = 7; // 动画的执行节奏(速度 有可能是先慢后快也可能是先快后慢 7是枚举) Animation 动画 Curve 曲线速率
UIKeyboardAnimationDurationUserInfoKey = "0.25"; // 键盘弹出\隐藏动画所需要的时间
UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}";
UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 588}";
UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 372}";
UIKeyboardFrameChangedByUserInteraction = 0;
// 键盘弹出
UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 480}, {320, 216}}";// 键盘刚出来那一刻的frame Y的坐标值是480
UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; // 键盘显示完毕后的frame ,Y的坐标值是264
// 键盘隐藏
UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 264}, {320, 216}}";
UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 480}, {320, 216}}";
*/
- (BOOL)prefersStatusBarHidden
{
return YES;
}
- (NSMutableArray *)messageFrames
{
if (_messageFrames == nil) {
NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"messages.plist" ofType:nil]];
NSMutableArray *mfArray = [NSMutableArray array];
for (NSDictionary *dict in dictArray) {
// 消息模型
MJMessage *msg = [MJMessage messageWithDict:dict];
// 取出上一个模型
MJMessageFrame *lastMf = [mfArray lastObject];
MJMessage *lastMsg = lastMf.message;
// 判断两个消息的时间是否一致
msg.hideTime = [msg.time isEqualToString:lastMsg.time];
// frame模型
MJMessageFrame *mf = [[MJMessageFrame alloc] init];
mf.message = msg;
// 添加模型
[mfArray addObject:mf];
}
_messageFrames = mfArray;
}
return _messageFrames;
}
#pragma mark - 数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.messageFrames.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1.创建cell
MJMessageCell *cell = [MJMessageCell cellWithTableView:tableView];
// 2.给cell传递模型
cell.messageFrame = self.messageFrames[indexPath.row];
// 3.返回cell
return cell;
}
#pragma mark - 代理方法
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
MJMessageFrame *mf = self.messageFrames[indexPath.row];
return mf.cellHeight;
}
/**
* 当开始拖拽表格的时候就会调用
*/
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
// 退出键盘
[self.view endEditing:YES];
}
@end
2.5 Model
// MJMessage.m
//
#import "MJMessage.h"
@implementation MJMessage
+ (instancetype)messageWithDict:(NSDictionary *)dict
{
return [[self alloc] initWithDict:dict];
}
- (instancetype)initWithDict:(NSDictionary *)dict
{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
@end
//
// MJMessageFrame.m
#import "MJMessageFrame.h"
#import "MJMessage.h"
#import "NSString+Extension.h"
@implementation MJMessageFrame
- (void)setMessage:(MJMessage *)message
{
_message = message;
// 间距
CGFloat padding = 10;
// 屏幕的宽度
CGFloat screenW = [UIScreen mainScreen].bounds.size.width;
// 1.时间
if (message.hideTime == NO) { // 显示时间
CGFloat timeX = 0;
CGFloat timeY = 0;
CGFloat timeW = screenW;
CGFloat timeH = 40;
_timeF = CGRectMake(timeX, timeY, timeW, timeH);
}
// 2.头像
CGFloat iconY = CGRectGetMaxY(_timeF) + padding;
CGFloat iconW = 40;
CGFloat iconH = 40;
CGFloat iconX;
if (message.type == MJMessageTypeOther) {// 别人发的
iconX = padding;
} else { // 自己的发的
iconX = screenW - padding - iconW;
}
_iconF = CGRectMake(iconX, iconY, iconW, iconH);
// 3.正文
CGFloat textY = iconY;
// 文字计算的最大尺寸
CGSize textMaxSize = CGSizeMake(200, MAXFLOAT);
// 文字计算出来的真实尺寸(按钮内部label的尺寸)
CGSize textRealSize = [message.text sizeWithFont:MJTextFont maxSize:textMaxSize];
// 按钮最终的真实尺寸
CGSize textBtnSize = CGSizeMake(textRealSize.width + MJTextPadding * 2, textRealSize.height + MJTextPadding * 2);
CGFloat textX;
if (message.type == MJMessageTypeOther) {// 别人发的
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};
// 4.cell的高度
CGFloat textMaxY = CGRectGetMaxY(_textF);
CGFloat iconMaxY = CGRectGetMaxY(_iconF);
_cellHeight = MAX(textMaxY, iconMaxY) + padding;
}
@end
3.运行结果