• UI基础--UITableView实现仿QQ聊天页面


    需求:类似于QQ聊天页面的展示,内容包括有头像、时间、聊天内容。相同时间发生的内容,只显示第一条内容的时间,并且点击输入框时,可以滚动到最后一条内容信息。具体效果图:

    实例的文件结构:

    实现的具体步骤:

    1、布局界面,主要包括一个UIImageView、3个UIButton、1个UITextField;

    2、自定义数据模型类,并测试数据是否能正常加载;

    3、自定义cell,由于每行数据的高度都是不规则的,所以考虑先自定义好frame再来写自定义cell。属性包括frame模型以及生成可重用cell的方法,要注意的是需要重写- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier方法,写给cell各子控件赋值以及生成frame的方法;

    4、自定义frame,属性包括模型数据和行高以及cell中各个子控件的frame;

    5、在控制器中写UITableView的 数据显示的方法;

    6、订阅键盘通知,并且弹出键盘时,改变view的frame,最后也要记得销毁通知(有订阅就要有销毁);

    7、写UITextFieldDelegate方法,在界面中显示新发送的消息;

     

    具体的代码:

    Model:

     

     1 //
     2 //  JWMessage.h
     3 //  12-24-Message-Two
     4 //
     5 //  Created by xiaomoge on 14/12/24.
     6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
     7 //
     8 
     9 #import <Foundation/Foundation.h>
    10 typedef enum {
    11     JWMessageTypeSelf,
    12     JWMessageTypeOther
    13 } JWMessageType;
    14 @interface JWMessage : NSObject
    15 @property (nonatomic,copy) NSString *text;
    16 @property (nonatomic,copy) NSString *time;
    17 @property (nonatomic,assign) JWMessageType type;
    18 @property (nonatomic,assign,getter=isHiddemTime) BOOL hiddemTime;
    19 - (instancetype)initWithDic:(NSDictionary *)dic;
    20 + (instancetype)messageWithDic:(NSDictionary *)dic;
    21 + (NSMutableArray *)messageList;
    22 @end

     

     1 //
     2 //  JWMessage.m
     3 //  12-24-Message-Two
     4 //
     5 //  Created by xiaomoge on 14/12/24.
     6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
     7 //
     8 
     9 #import "JWMessage.h"
    10 
    11 @implementation JWMessage
    12 - (instancetype)initWithDic:(NSDictionary *)dic {
    13     if (self = [super init]) {
    14         [self setValuesForKeysWithDictionary:dic];
    15     }
    16     return self;
    17 }
    18 + (instancetype)messageWithDic:(NSDictionary *)dic {
    19     return [[self alloc] initWithDic:dic];
    20 }
    21 
    22 + (NSMutableArray *)messageList {
    23     NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"messages" ofType:@"plist"]];
    24     
    25     NSMutableArray *tempArray = [NSMutableArray array];
    26     //定义一个前信息
    27     JWMessage *preMessage;
    28     for (NSDictionary *dic in array) {
    29         JWMessage *message = [JWMessage messageWithDic:dic];
    30         //判断前信息和当前信息是否相同,如相同即隐藏当前信息的时间frame
    31         if ([message.time isEqualToString:preMessage.time]) {
    32             message.hiddemTime = YES;
    33         }
    34         [tempArray addObject:message];
    35         //获得前信息的数据
    36         preMessage = [tempArray lastObject];
    37     }
    38     return tempArray;
    39 }
    40 @end
     1 //
     2 //  JWMessageFrame.h
     3 //  12-24-Message-Two
     4 //
     5 //  Created by xiaomoge on 14/12/24.
     6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
     7 //
     8 #define TEXTFONT 14
     9 #import <UIKit/UIKit.h>
    10 @class JWMessage;
    11 @interface JWMessageFrame : NSObject
    12 @property (nonatomic,assign) CGFloat rowHeight;
    13 @property (nonatomic,assign) CGRect timeFrame;
    14 @property (nonatomic,assign) CGRect iconFrame;
    15 @property (nonatomic,assign) CGRect textFrame;
    16 @property (nonatomic,strong) JWMessage *message;
    17 + (NSMutableArray *)messageFrameList;
    18 @end
     1 //
     2 //  JWMessageFrame.m
     3 //  12-24-Message-Two
     4 //
     5 //  Created by xiaomoge on 14/12/24.
     6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
     7 //
     8 
     9 #import "JWMessageFrame.h"
    10 #import "JWMessage.h"
    11 #import "NSString+Ext.h"
    12 @implementation JWMessageFrame
    13 /*
    14  重写set方法,设置fram
    15  */
    16 - (void)setMessage:(JWMessage *)message {
    17     _message = message;
    18     
    19     //屏幕宽度
    20     UIScreen *screen = [UIScreen mainScreen];
    21     CGFloat screenW = screen.bounds.size.width;
    22     //间距
    23     CGFloat margin = 10;
    24     
    25     //时间frame
    26     if (!message.hiddemTime) {//如果时间不相同时,才设置时间的frame
    27         _timeFrame = CGRectMake(0, 0, screenW, 40);
    28     }
    29         
    30     //头像frame
    31     CGFloat iconW = 50;
    32     CGFloat iconH = 50;
    33     CGFloat iconX;
    34     CGFloat iconY = CGRectGetMaxY(_timeFrame);
    35     if (message.type == JWMessageTypeSelf) {
    36         //自己的头像在右边,所以是屏幕的宽度减去间距,再减去头像的宽度
    37         iconX = screenW - margin - iconW;
    38     }else {
    39         iconX = margin;
    40     }
    41     _iconFrame = CGRectMake(iconX, iconY, iconW, iconH);
    42     
    43     //内容frame
    44     //取得内容的大小
    45     CGSize textSize = [message.text setTextSize:CGSizeMake(200, MAXFLOAT) andFontSize:TEXTFONT];
    46     //取得内容按钮的大小
    47     CGSize btnSize = CGSizeMake(textSize.width + 40, textSize.height + 40);
    48     CGFloat textX;
    49     //内容的Y值和头像的Y值是一样的
    50     CGFloat textY = iconY;
    51     if (message.type == JWMessageTypeSelf) {
    52         //自己的内容在右边,所以是头像的X值减去按钮的宽度,再减去间距
    53         textX = iconX - btnSize.width - margin;
    54     }else {
    55         //对方的内容在左边,所以是头像的宽度加上间距
    56         textX = iconW + margin;
    57     }
    58     _textFrame = CGRectMake(textX, textY, btnSize.width, btnSize.height);
    59     
    60     //行高
    61     //取得内容的最大Y值
    62     CGFloat textMax = CGRectGetMaxY(_textFrame);
    63     //取得头像的最大Y值
    64     CGFloat iconMax = CGRectGetMaxY(_iconFrame);
    65     //行高的多少是根据内容多少来判断的,内容少时是头像的最大Y值,内容过多时,就是内容的最大Y值了,所以用了一个MAX函数,取最大值
    66     _rowHeight = MAX(textMax, iconMax) + margin;
    67     
    68 }
    69 + (NSMutableArray *)messageFrameList {
    70     NSArray *message = [JWMessage messageList];
    71     NSMutableArray *tem = [NSMutableArray array];
    72     for (JWMessage *msg in message) {
    73         JWMessageFrame *frame = [[JWMessageFrame alloc] init];
    74         frame.message = msg;
    75         [tem addObject:frame];
    76     }
    77     return tem;
    78 }
    79 @end

    View:

     1 //
     2 //  JWMessageCell.h
     3 //  12-24-Message-Two
     4 //
     5 //  Created by xiaomoge on 14/12/24.
     6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
     7 //
     8 
     9 #import <UIKit/UIKit.h>
    10 @class JWMessageFrame;
    11 @interface JWMessageCell : UITableViewCell
    12 @property (nonatomic,strong) JWMessageFrame *messageFrame;
    13 + (instancetype)cellWithTableView:(UITableView *)tableView;
    14 @end
     1 //
     2 //  JWMessageCell.m
     3 //  12-24-Message-Two
     4 //
     5 //  Created by xiaomoge on 14/12/24.
     6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
     7 //
     8 
     9 #import "JWMessageCell.h"
    10 #import "JWMessage.h"
    11 #import "JWMessageFrame.h"
    12 #import "UIImage+Ext.h"
    13 @interface JWMessageCell ()
    14 @property (nonatomic,weak) UILabel *timeLabel;
    15 @property (nonatomic,weak) UIImageView *iconView;
    16 @property (nonatomic,weak) UIButton *textBtn;
    17 @end
    18 @implementation JWMessageCell
    19 //创建可重用的cell
    20 + (instancetype)cellWithTableView:(UITableView *)tableView {
    21     //创建缓存池标识
    22     static NSString *resue = @"msg";
    23     //当缓存池有空闲的cell时,可重用
    24     JWMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:resue];
    25     //当缓存池内暂时没有空闲的cell时,自动创建
    26     if (!cell) {
    27         cell = [[self alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:resue];
    28     }
    29     return cell;
    30 }
    31 //重写
    32 - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    33     if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
    34         //清除背景颜色
    35         self.backgroundColor = [UIColor clearColor];
    36         //初始化时间子控件
    37         UILabel *time = [[UILabel alloc] init];
    38         [self.contentView addSubview:time];
    39         self.timeLabel = time;
    40         //把时间居中显示
    41         time.textAlignment = NSTextAlignmentCenter;
    42         //初始化头像子控件
    43         UIImageView *img = [[UIImageView alloc] init];
    44         [self.contentView addSubview:img];
    45         self.iconView = img;
    46         //设置头像的圆角
    47         img.layer.cornerRadius = 25;
    48         //设置是否剪裁多余的部分
    49         img.layer.masksToBounds = YES;
    50         //初始化内容子控件
    51         UIButton *text = [UIButton buttonWithType:UIButtonTypeCustom];
    52         [self.contentView addSubview:text];
    53         self.textBtn = text;
    54         [text setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    55         text.titleLabel.numberOfLines = 0;
    56         //设置内容的间距
    57         text.contentEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20);
    58         //设置内容的字体大小
    59         text.titleLabel.font = [UIFont systemFontOfSize:TEXTFONT];
    60     }
    61     return self;
    62 }
    63 - (void)setMessageFrame:(JWMessageFrame *)messageFrame {
    64     _messageFrame = messageFrame;
    65     [self setSubviewsContent];
    66     [self setSubviewsFrame];
    67 }
    68 - (void)setSubviewsContent {
    69     JWMessage *msg = self.messageFrame.message;
    70     //给时间子控件赋值
    71     self.timeLabel.text = msg.time;
    72     //给头像子控件赋值
    73     self.iconView.image = [UIImage imageNamed:msg.type == JWMessageTypeSelf ? @"me" :@"other"];
    74     //给内容子控件赋值
    75     [self.textBtn setTitle:msg.text forState:UIControlStateNormal];
    76     //设置内容子控件的背景图片
    77     if (msg.type == JWMessageTypeSelf) {
    78        [self.textBtn setBackgroundImage:[UIImage setImage:@"chat_send_nor"] forState:UIControlStateNormal];
    79         [self.textBtn setBackgroundImage:[UIImage setImage:@"chat_send_press_pic"] forState:UIControlStateHighlighted];
    80     }else {
    81         [self.textBtn setBackgroundImage:[UIImage setImage:@"chat_recive_nor"] forState:UIControlStateNormal];
    82         [self.textBtn setBackgroundImage:[UIImage setImage:@"chat_recive_press_pic"] forState:UIControlStateHighlighted];
    83     }
    84 }
    85 - (void)setSubviewsFrame {
    86     self.timeLabel.frame = self.messageFrame.timeFrame;
    87     self.iconView.frame = self.messageFrame.iconFrame;
    88     self.textBtn.frame = self.messageFrame.textFrame;
    89 }
    90 @end

    Controller:

     1 //
     2 //  ViewController.m
     3 //  12-24-Message-Two
     4 //
     5 //  Created by xiaomoge on 14/12/24.
     6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
     7 //
     8 
     9 #import "ViewController.h"
    10 #import "JWMessage.h"
    11 #import "JWMessageFrame.h"
    12 #import "JWMessageCell.h"
    13 @interface ViewController ()<UITextFieldDelegate,UITableViewDataSource,UITableViewDelegate>
    14 @property (weak, nonatomic) IBOutlet UITableView *tableView;
    15 @property (nonatomic,strong) NSMutableArray *messageFrame;
    16 @end
    17 
    18 @implementation ViewController
    19 #pragma mark - 隐藏状态栏
    20 - (BOOL)prefersStatusBarHidden {
    21     return YES;
    22 }
    23 #pragma mark - 懒加载
    24 - (NSMutableArray *)messageFrame {
    25     if (!_messageFrame) {
    26         _messageFrame = [JWMessageFrame messageFrameList];
    27     }
    28     return _messageFrame;
    29 }
    30 - (void)viewDidLoad {
    31     [super viewDidLoad];
    32     //取消分割线
    33     self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    34     //设置背景颜色
    35     self.tableView.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1];
    36     //订阅键盘通知
    37     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(clickTableView:) name:UIKeyboardWillChangeFrameNotification object:nil];
    38 }
    39 //键盘frame发生改变时,view也跟着改变
    40 - (void)clickTableView:(NSNotification *)noti {
    41     CGFloat duration = [noti.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];
    42     CGRect frame = [noti.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    43     CGFloat offX = frame.origin.y - self.view.frame.size.height;
    44     [UIView animateWithDuration:duration animations:^{
    45         self.view.transform = CGAffineTransformMakeTranslation(0, offX);
    46     }];
    47 }
    48 //销毁订阅键盘通知
    49 - (void)dealloc {
    50     [[NSNotificationCenter defaultCenter] removeObserver:self];
    51 }
    52 #pragma mark - UITableViewDataSource方法
    53 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    54     return self.messageFrame.count;
    55 }
    56 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    57     JWMessageCell *cell = [JWMessageCell cellWithTableView:tableView];
    58     cell.messageFrame = self.messageFrame[indexPath.row];
    59     return cell;
    60 }
    61 #pragma mark - UITableViewDelegate方法
    62 - (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    63     JWMessageFrame *frame = self.messageFrame[indexPath.row];
    64     return frame.rowHeight;
    65 }
    66 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    67     [self.view endEditing:YES];
    68 }
    69 #pragma mark - UITextFieldDelegate方法
    70 -(BOOL)textFieldShouldReturn:(UITextField *)textField {
    71     JWMessage *msg = [[JWMessage alloc] init];
    72     msg.type = JWMessageTypeSelf;
    73     msg.text = textField.text;
    74     NSDate *date = [NSDate date];
    75     NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    76     formatter.dateFormat = @"HH:mm";
    77     msg.time = [formatter stringFromDate:date];
    78     
    79     //判断前一条信息和当前信息的时间是否相同
    80     JWMessage *preMessage = (JWMessage *)[[self.messageFrame lastObject] message];
    81     if ([preMessage.time isEqualToString:msg.time]) {
    82         msg.hiddemTime = YES;
    83     }
    84     
    85     JWMessageFrame *frame = [[JWMessageFrame alloc] init];
    86     frame.message = msg;
    87     [self.messageFrame addObject:frame];
    88     
    89     //重新加载数据
    90     [self.tableView reloadData];
    91     NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.messageFrame.count - 1 inSection:0];
    92     //滚动显示最后一条数据
    93     [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
    94     return YES;
    95 }
    96 @end

    Category:

     1 //
     2 //  UIImage+Ext.m
     3 //  12-24-Message-Two
     4 //
     5 //  Created by xiaomoge on 14/12/24.
     6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
     7 //
     8 
     9 #import "UIImage+Ext.h"
    10 
    11 @implementation UIImage (Ext)
    12 //平铺图片,改变图片的大小
    13 + (UIImage *)setImage:(NSString *)name {
    14     UIImage *imageName = [UIImage imageNamed:name];
    15     return [imageName stretchableImageWithLeftCapWidth:imageName.size.width * 0.5 topCapHeight:imageName.size.height * 0.5];
    16 }
    17 @end

    Category中的NSString+Ext内容和上一篇的内容是一样的,都是获取文字的大小,可回看。http://www.cnblogs.com/xiaomoge/p/4200169.html

    在这里,要注意的:

    1、Category中的方法,该方法是在图片的中心,获取1X的像素来平铺图片,直到平铺满整个图片为止。

     

  • 相关阅读:
    element input搜索框探索
    Github网站css加载不出来的处理方法(转,亲测有效)
    通过用axios发送请求,全局拦截请求,获取到错误弄明白promise对象
    vuex和localStorage/sessionStorage 区别
    leetcode刷题笔记十一 盛最多水的容器 Scala版本
    leetcode刷题笔记十 正则表达式 Scala版本
    leetcode刷题笔记九 回文数 Scala版本
    leetcode刷题笔记八 字符串转整性 Scala版本
    leetcode刷题笔记七 整数反转 Scala版本
    leetcode刷题笔记六 Z字型转换 Scala版本
  • 原文地址:https://www.cnblogs.com/xiaomoge/p/4200221.html
Copyright © 2020-2023  润新知