QQ聊天布局(自定义Cell + 文本输入框处理事件)

来源:互联网 发布:吉祥网络上海二手房 编辑:程序博客网 时间:2024/05/17 00:11

1,开始比较复杂的学习了,花了1天来琢磨和重写敲了一次代码。


2,按之前自定义CELL的方式建立数据模型和数据frame模型,遵循MVC设计模式



==============
// 拉伸图片的方法

@implementation UIImage (extension)
+ (UIImage *)resizableImage:(NSString *)name
{
    UIImage *rtnImage = [UIImage imageNamed:name];
    CGFloat w = rtnImage.size.width * 0.5;
    CGFloat h = rtnImage.size.height * 0.5;
    return [rtnImage resizableImageWithCapInsets:UIEdgeInsetsMake(h, w, h, w)];
}
@end

=============
// 得到文字长宽的方法

@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

==================

@interface TXMessageCell()


@property (nonatomic, weak)  UILabel *timeView;
@property (nonatomic, weak)  UIImageView *iconView;
@property (nonatomic, weak)  UIButton *textView;


@end


@implementation TXMessageCell


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


- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // 1, time label
        UILabel *timeView = [[UILabel alloc] init];
        timeView.font = [UIFont systemFontOfSize:13];
        timeView.textColor = [UIColor blackColor];
        timeView.textAlignment = NSTextAlignmentCenter;
        [self.contentView addSubview:timeView];
        self.timeView = timeView;
        
        // 2, icon ImageView
        UIImageView *iconView = [[UIImageView alloc] init];
        [self.contentView addSubview:iconView];
        self.iconView = iconView;
        
        // 3, text button
        UIButton *textView = [[UIButton alloc] init];
        textView.titleLabel.font = [UIFont systemFontOfSize:14];
        textView.titleLabel.numberOfLines = 0;
        textView.contentEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20); // 设置button里面的内边距,在设置长宽时应该加上这个内边距,文字才不会变形
        [textView setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        [self.contentView addSubview:textView];
        self.textView = textView;
        
        // 4 clear cell backgroundcolor
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}


- (void)setMessageFrame:(TXMessageFrame *)messageFrame
{
    _messageFrame = messageFrame;
    
    [self settingData];
    
    [self settingFrame];
}
- (void)settingData
{
    TXMessage *message = self.messageFrame.message;
    
    if (message.hiddenTime == NO) {
        self.timeView.text = message.time;
    }
    
    if (message.type == TXMessageTypeOther) {
        self.iconView.image = [UIImage imageNamed:@"other"];
    } else {
        self.iconView.image = [UIImage imageNamed:@"me"];
    }
    
    [self.textView setTitle:message.text forState:UIControlStateNormal];
    
    if (message.type == TXMessageTypeOther) {
        [self.textView setBackgroundImage:[UIImage resizableImage:@"chat_recive_press_pic"] forState:UIControlStateNormal];
    } else {
       [self.textView setBackgroundImage:[UIImage resizableImage:@"chat_send_press_pic"] forState:UIControlStateNormal]; 
    }
}


- (void)settingFrame
{
    self.timeView.frame = self.messageFrame.timeF;
    
    self.iconView.frame = self.messageFrame.iconF;
    
    self.textView.frame = self.messageFrame.textF;
}


@end

=============

#import "TXMessageFrame.h"
#import "TXMessage.h"
#import "NSString+extension.h"


@implementation TXMessageFrame
- (void)setMessage:(TXMessage *)message
{
    _message = message;
    
    CGFloat padding = 10;
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    
    if(message.hiddenTime == NO){
        // 1, time
        CGFloat timeW = screenWidth;
        CGFloat timeH = 40;
        CGFloat timeX = 0;
        CGFloat timeY = 0;
        _timeF = CGRectMake(timeX, timeY, timeW, timeH);
    }
    
    // 2, icon
    CGFloat iconW = 40;
    CGFloat iconH = 40;
    CGFloat iconX;
    CGFloat iconY = CGRectGetMaxY(_timeF) + padding;
    
    if(message.type == TXMessageTypeOther){
        iconX = padding;
    }else{
        iconX = screenWidth - padding - iconW;
    }
    _iconF = CGRectMake(iconX, iconY, iconW, iconH);
    
    // 3, text
    CGSize textSize = [message.text sizeWithFont:[UIFont systemFontOfSize:14] maxSize:CGSizeMake(200, MAXFLOAT)];
    CGFloat textW = textSize.width;
    CGFloat textH = textSize.height;
    CGFloat textX;
    CGFloat textY = iconY;
    
    
    if(message.type == TXMessageTypeOther){
        textX = CGRectGetMaxX(_iconF) + padding;
    } else {
        textX = iconX - padding - textSize.width - 40;
    }
    _textF = CGRectMake(textX, textY, textW + 40, textH + 40); // 长宽各加上内边距的两倍
    
    _cellHeight = MAX(CGRectGetMaxY(_iconF), CGRectGetMaxY(_textF)) + padding;
}
@end

==============

#import "TXViewController.h"
#import "TXMessageFrame.h"
#import "TXMessage.h"
#import "TXMessageCell.h"


@interface TXViewController () <UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate>
@property (nonatomic, strong) NSMutableArray *messageFrames;
@property (weak, nonatomic) IBOutlet UITableView *myTableView;
@property (weak, nonatomic) IBOutlet UITextField *myInputView;
@property (nonatomic, strong) NSDictionary *autoreplay;
@end


@implementation TXViewController


- (void)viewDidLoad
{
    [super viewDidLoad];
    
   // tableView的颜色
    self.myTableView.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1];
    self.myTableView.separatorStyle = UITableViewCellSeparatorStyleNone; // 去掉tableViewCell之间的分割线
    self.myTableView.allowsSelection = NO; // 不允许选择Cell
    self.myTableView.delegate = self; //代理对象
    
    // 2 ,注册通知:把键盘改变自己的Frame的消息通过给self对象的keyBoardHandle方法去处理
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyBoardHandle:) name:UIKeyboardWillChangeFrameNotification object:nil];
    
    // 3
    UILabel *leftView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 5, 0)]; // 让输入框的光标与左边线有一定距离
    self.myInputView.leftView = leftView; 
    self.myInputView.leftViewMode = UITextFieldViewModeAlways; //当view的高度设置为0时,设置总是显示的模式
    self.myInputView.delegate = self; // 需要监听输入框的一些事件,所以把输入框的代理对象设置为控制器本身,在控制器中实现其代理方法
}


- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self]; // 即有注册,也必有移除监听
}

// 向 frame模型中添加一条信息,并刷新滚动到最新发布的底部消息
- (void)addMessageWithText:(NSString *)text type:(TXMessageType)type
{
    TXMessage *message = [[TXMessage alloc] init];
    message.text = text;
    message.type = type;
    
    NSDate *now = [NSDate date];
    NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
    fmt.dateFormat = @"HH:mm";
    message.time = [fmt stringFromDate:now]; // 格式日期
    
    TXMessageFrame *lastFrame = [self.messageFrames lastObject];
    message.hiddenTime = [message.time isEqualToString:lastFrame.message.time];
    
    TXMessageFrame *messageFrame = [[TXMessageFrame alloc] init];
    messageFrame.message = message;


    
    [self.messageFrames addObject:messageFrame];
    
    [self.myTableView reloadData]; // 刷新整个tableView
    
    NSIndexPath *indexpath = [NSIndexPath indexPathForRow:self.messageFrames.count - 1 inSection:0]; // 计算滚动到底部的那一行
    [self.myTableView scrollToRowAtIndexPath:indexpath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; // 

}

// UITextField代理方法,当点了键盘右下角的 发送按钮时,会被自动调用
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [self addMessageWithText:textField.text type:TXMessageTypeMe];
    
    NSString *othermsg = [self getOneAutoReplay:textField.text];
    [self addMessageWithText:othermsg type:TXMessageTypeOther]; // 加一条自动回复的消息
    
    self.myInputView.text = nil;
    
    return YES;
}


- (NSString *)getOneAutoReplay:(NSString *)text
{
    NSString *word;
    for (int i = 0; i < text.length; i++) {
        word = [text substringWithRange:NSMakeRange(i, 1)];
        if (self.autoreplay[word]) {
            return self.autoreplay[word];
        }
    }
    return text;
}

// 键盘发生FRAME改变时,通过NSNotification包装信息(userInfo),发送给监听对象的方法
/*
 UIKeyboardDidChangeFrameNotification; userInfo = {
 UIKeyboardAnimationCurveUserInfoKey = 7;
 UIKeyboardAnimationDurationUserInfoKey = "0.25";
 UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}";
 UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 588}";
 UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 372}";
 UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 480}, {320, 216}}";
 UIKeyboardFrameChangedByUserInteraction = 0;
 UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 264}, {320, 216}}";
 }}
 */
- (void)keyBoardHandle:(NSNotification *)note
{
    self.view.window.backgroundColor = self.myTableView.backgroundColor;// 设置UIWindow的背景色与tableView一致(没怎么明白,为什么不能设置在didViewLoad中?)
    
    CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; // 动画执行的时间
    
    CGRect lastFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    
    CGFloat lastY = lastFrame.origin.y - self.view.frame.size.height; // 键盘改变后的Y值 - view的高度
    
    [UIView animateWithDuration:duration animations:^{ // 使用键盘相同的动画时间,将view往上/下垂直改变Y坐标,便产生动画
        self.view.transform = CGAffineTransformMakeTranslation(0, lastY);
    }];
}

// 懒加载
- (NSMutableArray *)messageFrames
{
    if(_messageFrames == nil)
    {
        NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"messages.plist" ofType:nil]];
        NSMutableArray *framesArray = [NSMutableArray array];
        
        for (NSDictionary *dict in dictArray) {
            TXMessage *message = [TXMessage messageWithDict:dict];
            
            TXMessageFrame *lastFrame = [framesArray lastObject];
            message.hiddenTime = [message.time isEqualToString:lastFrame.message.time];
            
            TXMessageFrame *messageFrame = [[TXMessageFrame alloc] init];
            messageFrame.message = message;
            
            [framesArray addObject:messageFrame];
        }
        _messageFrames = framesArray;
    }
    return _messageFrames;
}


- (NSDictionary *)autoreplay
{
    if(_autoreplay == nil)
    {
        NSString *path = [[NSBundle mainBundle] pathForResource:@"autoreply.plist" ofType:nil];
        _autoreplay = [NSDictionary dictionaryWithContentsOfFile:path];
    }
    return _autoreplay;
}

// tableView数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.messageFrames.count;
}

// tableView数据源方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    TXMessageCell *cell = [TXMessageCell cellWithTableView:tableView];
    
    cell.messageFrame = self.messageFrames[indexPath.row];
    
    return cell;
}

// tableView代理方法
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [self.messageFrames[indexPath.row] cellHeight];
}

// tableView 继承 ScrollView, 也拥有“开始拖拽view”的代理方法,这里是退出键盘
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    [self.view endEditing:YES];
}

// 隐藏状态栏
- (BOOL)prefersStatusBarHidden
{
    return YES;
}


@end

==========

0 0
原创粉丝点击