UI之 08-QQ自定义布局

作者: 高俊 | 来源:发表于2016-04-05 14:46 被阅读396次

    效果展示:

    Snip20160405_1.png

    01 加载模型数据\创建cell

    在这里面, 这个代码和步骤, 与我们上一次总结的一样, 唯一要注意的细节就是我们在

    昨天创建自定义cell的第二步中的有一个创建cell中的控件, 中
    我们创建:

    /**
     *  时间
     */
    @property (nonatomic, weak) UILabel *timeView;
    /**
     *  头像
     */
    @property (nonatomic, weak) UIImageView *iconView;
    /**
     *  正文
     */
    @property (nonatomic, weak) UIButton *textView;
    

    这几个控件的时候, 我们会发现, 他们都是弱指针, 所以我们不能直接将这些控件添加给我们的cell,所以我们一般会选择这样做:

    // 1.时间
    UILabel *timeView = [[UILabel alloc] init];
    [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];
        [self.contentView addSubview:textView];
        self.textView = textView;
    

    为什么不可以直接添加到cell中???

    由于 , 这个是利用弱指针创建的, 所以, 当我们这一行添加代码运行过之后, 这些控件会自被销毁

    02 计算子控件的frame

    我们在计算我们的frame的时候, 情况和我上一次做的微博一样

    frame的.h文件

    // 正文的字体
    #define GJTextFont [UIFont systemFontOfSize:15]
    
    #import <Foundation/Foundation.h>
    
    @class GJMessage;
    
    @interface GJMessageFrame : 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) GJMessage *message;
    @end
    

    frame的.m文件

    #import "GJMessageFrame.h"
    #import "GJMessage.h"
    
    @implementation GJMessageFrame
    /**
     *  计算文字尺寸
     *
     *  @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:(GJMessage *)message
    {
        _message = message;
        // 间距
        CGFloat padding = 10;
        // 屏幕的宽度
        CGFloat screenW = [UIScreen mainScreen].bounds.size.width;
        
        // 1.时间
        CGFloat timeX = 0;
        CGFloat timeY = 0;
        CGFloat timeW = screenW;
        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 == GJMessageTypeOther) {// 别人发的
            iconX = padding;
        } else { // 自己的发的
            iconX = screenW - padding - iconW;
        }
        _iconF = CGRectMake(iconX, iconY, iconW, iconH);
        
        // 3.正文
        CGFloat textY = iconY;
        // 文字的尺寸
        CGSize textMaxSize = CGSizeMake(150, MAXFLOAT);
        CGSize textSize = [self sizeWithText:message.text font:MJTextFont maxSize:textMaxSize];
        CGFloat textX;
        if (message.type == GJMessageTypeOther) {// 别人发的
            textX = CGRectGetMaxX(_iconF) + padding;
        } else {// 自己的发的
            textX = iconX - padding - textSize.width;
        }
    //    _textF = CGRectMake(textX, textY, textSize.width, textSize.height);
        _textF = (CGRect){{textX, textY}, textSize};
        
        // 4.cell的高度
        CGFloat textMaxY = CGRectGetMaxY(_textF);
        CGFloat iconMaxY = CGRectGetMaxY(_iconF);
        _cellHeight = MAX(textMaxY, iconMaxY) + padding;
    }
    
    @end
    

    03 修正聊天细节

    小细节:

    1. 我们如果按照上面的代码写的话, 就会发现, 我们的每一个聊天内容(cell)就可以被选中. 在我们的[super viewDidLoad];下面写上这样一行代码就可以了: (这个是利用代码写的)

    self.tableView.allowSelection = NO;
    

    storyboard中也可以这个大家可以自己找一下, 就是在我们的tableView中的

    2 . 有些消息时间一致, 但是它依旧会显示两个相同的时间, 解决问题:

    • 首先, 我们如果确定两个相同的时间不会一起显示, 我们就需要将第二个相同的时间隐藏 所以我们就需要在我们的数据模型类中增加一个新的属性hiddenTime他的类型是BOOL是为了判断是否显示时间 代码就是:

    数据模型类.h文件

    @property (nonatomic, assign) BOOL hiddenTime;
    

    frame模型类.m文件

    if (massage.hiddenTime == NO){
    
        CGFloat timeX = 0;
        CGFloat timeY = 0;
        CGFloat timeW = screenW;
        CGFloat timeH = 40;
        _timeF = CGRectMake(timeX, timeY, timeW, timeH);
    }
    
    • 如何确定这个BOOL类型的正确性???

    我们就需要在我们的控制器的数据转模型中写了
    为了比较时间相同与否, 我们需要拿到上一个时间, 然后两个时间进行比较, 这就需要在控制器中的数据转模型中写了, 代码如下:

    for (NSDictionary *dict in dictArray) {
                // 消息模型
                GJMessage *msg = [GJMessage messageWithDict:dict];
                
                // 取出上一个模型
                GJMessageFrame *lastMf = [mfArray lastObject];
                GJMessage *lastMsg = lastMf.message;
                
                // 判断两个消息的时间是否一致
                msg.hideTime = [msg.time isEqualToString:lastMsg.time];
                
                // frame模型
                GJMessageFrame *mf = [[GJMessageFrame alloc] init];
                mf.message = msg;
                
                // 添加模型
                [mfArray addObject:mf];
    }
    

    为什么是lastObject???
    这是由于我们在添加模型的之前, 我们最后一个模型就是就是我们的上一个时间. 所以, 我们使用的是lastObject而且, 整个代码要写在[mfArray addObject:mf];

    3 . 关于我们聊天正文的背景图片

    我们在设置聊天正文背景的时候, 是不是直接将图片放到正文后面???
    其实不是的, 如果我们是直接拖到里面会发生这样的情况:

    Snip20160405_2.png

    我们可以看到, 我们的那个图片已经变形了, 所以我们解决的办法就是利用我们美工制作的图片的原理:

    其实我们可以发现, 在我们的正文背景图片中, 边缘的都有点失真了,
    而中间的那一部分, 却很完美,(这个就是我们美工利用特殊的工具才能做出来的效果) 所以我们要利用一个方法, 当文字, 变多时, 将我们的图片中间的那个一部分不断的放大, 不断地填充. 来完成我们平时QQ聊天时的那种效果.
    而, 我们要不断放大我们图片中间的那一点. 就系统, 需要告诉我们的系统, 是需要将哪一个部分不断放大, 这就需要下面的这个方法了 :

    当然这是在我们的GJMassageCell.m文件中写的

    // 正文的背景
        if (message.type == MJMessageTypeMe) { // 自己发的,蓝色
            
            UIImage *normal = [@"chat_send_nor"];
            CGFloat w = normal.size.width * 0.5;
            CGFloat h = normal.size.height * 0.5;
            UIImage *lastNomal = [normal resizableImageWithCapInsets:UIEdgeInsetsMake(h, w, h, w)];
            [self.textView setBackgroundImage: lastNormal forState: UIControlStateNormal];
        } else { // 别人发的,白色
            
            
            // 大概的步骤和上面一样
        }
    

    `
    注意: 我们关于那个一个中间部分的算法, 其实就是最中心的那一个点, 只是这一个点, 坐标连1都不到.

    当然这样, 我们的代码可以封装(以前我们就说过, 一旦遇到相同的代码, 一般都可以封装)

    4 .封装上面的部分代码:

    为什么封装代码???

    有时候 , 在做其他的项目的时候 , 发现有时候, 我们的这种图片拉伸, 和上面的文本框大小设置, 有很多的地方可以使用, 但是如果我们不进行封装的话 ,一旦我们在其他地方使用的时候, 就必须拥有这个模型.

    解决方法 ,就是创建一个分类, 来存储我们的这些将来可以共用的方法, 而, 我们注意到, 我们的上面的图片拉伸,是我们的UIImage的, 所以我们要创建一个UIImage的分类来 存放这个拉伸图片的方法.

    首先创建一个UIImage的分类:

    Snip20160405_3.png

    然后在这个分类中写我们的方法:

    // .h文件
    #import <UIKit/UIKit.h>
    
    @interface UIImage (Extension)
    + (UIImage *)resizableImage:(NSString *)name;
    @end
    

    图片分类的.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
    

    而这个方法的调用, 就变成这个样子了:

    // 4.正文的背景
        if (message.type == GJMessageTypeMe) { // 自己发的,蓝色
            [self.textView setBackgroundImage:[UIImage resizableImage:@"chat_send_nor"] forState:UIControlStateNormal];
        } else { // 别人发的,白色
            [self.textView setBackgroundImage:[UIImage resizableImage:@"chat_recive_nor"] forState:UIControlStateNormal];
        }
    

    而我们先前说的, 关于我们正文字体的设置, 也是相同的道理. 但是区别就在于, 我们字体设置, 是创建一个对象方法, 而上面的是类方法

    // .h文件
    #import <Foundation/Foundation.h>
    
    @interface NSString (Extension)
    /**
     *  返回字符串所占用的尺寸
     *
     *  @param font    字体
     *  @param maxSize 最大尺寸
     */
    - (CGSize)sizeWithFont:(UIFont *)font maxSize:(CGSize)maxSize;
    @end
    
    
    // .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;
    }
    

    调用的方式为:

     [textView setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
            [self.contentView addSubview:textView];
            self.textView = textView;
    

    新知识: (这一部分的代码和我们做的这个应用的代码不相关)

    4. 通知机制:

    **原因: **
    在上面我们会发现, 当我们的点击那个输入按钮的时候, 我们的键盘(手机键盘)弹出, 会将我们输入的那个按钮遮住, 所以我们要利用一个新的知识让我们的输入框, 当我们的键盘弹出的时候, 他会逐渐上升, 当键盘隐藏的时候, 他会逐渐下降.

    为什么不使用代理

    因为, 如果是监听我们键盘的弹出或者隐藏, 是没有办法用代理的, 所以我们要学习新的知识: 通知

    4.1 什么是通知:

    通知就是用于多个对象之间通信的.
    假如说对象A发生了一些事情, 他想告诉对象B, 和对象C, 甚至对象D都可以.

    **代理和通知之间的区别: **
    代理只能是一对一, 就是一个对象对应一个对象 .
    通知可以是一对多, 多对一, 多对多

    而今天我们讲通知那些知识:

    • 通知的发布
    • 通知的监听
    • 通知的移除

    4. 2 通知的三要素:

    • 通知名称
    • 通知发布者
    • 通知接受者
    Snip20160405_4.png

    4. 3 通知中心

    Snip20160405_5.png

    从上面的图片我们也可以看出, 我们通知的发布者, 和接收者 , 中间需要一个通知中心帮助我们把通知发布者, 发布的通知告诉接收者.

    4. 4发布通知:

    Snip20160405_6.png

    以上的三个方法都是我们发布通知的方法.

    4. 5 注册通知监听器

    Snip20160405_7.png

    4. 6 取消注册通知监听器

    Snip20160405_8.png

    其实为了避免出现野指针, 我们一般会在监听器销毁之前取消注册, 因为, 就像上面所说的, 如果不这样做的话, 会导致系统崩溃

    话不多说, 直接看代码:

    **介绍: **

    这是一个利用, 用户阅读新闻的情景介绍通知的, 所以在这里面有两个类:
    一个是company新闻公司.
    一个是person用户类 .

    公司通过用户所喜爱的栏目(军事新闻, 或者娱乐新闻)来决定是否给用户发送通知

    代码如下:

    首先是公司类中的代码

    #import <Foundation/Foundation.h>
    
    @interface NewsCompany : NSObject
    /**
     *  机构名称
     */
    @property (nonatomic, copy) NSString *name;
    @end
    

    就是一个简单的新闻公司名称属性

    person类(.h文件的代码就不写了, 因为就只是一个简单的方法声明, 这个方法的名称可以在.m文件中看到)

    #import "Person.h"
    #import "NewsCompany.h"
    
    @implementation Person
    - (void)newsCome:(NSNotification *)note
    {
        // 通知的发布者
        NewsCompany *obj = note.object;
        
        NSLog(@"%@接收到了%@发出的通知,通知内容是:%@", self.name, obj.name, note.userInfo);
    }
    
    - (void)dealloc
    {
    //    [super dealloc];
        
        // 取消注册通知监听器
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    @end
    

    main函数:

    #import <Foundation/Foundation.h>
    #import "Person.h"
    #import "NewsCompany.h"
    
    int main(int argc, const char * argv[])
    {
        @autoreleasepool {
            // 1.初始化机构
            NewsCompany *tx = [[NewsCompany alloc] init];
            tx.name = @"腾讯新闻";
            
            NewsCompany *sina = [[NewsCompany alloc] init];
            sina.name = @"新浪新闻";
            
            // 2.初始化3个人
            Person *zhangsan = [[Person alloc] init];
            zhangsan.name = @"张三";
            
            Person *lisi = [[Person alloc] init];
            lisi.name = @"李四";
            
            Person *wangwu = [[Person alloc] init];
            wangwu.name = @"王五";
            
            NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
            // 3.添加监听器
            // zhangsan只监听tx发出的junshi_news_come通知
            // 2
            [center addObserver:zhangsan selector:@selector(newsCome:) name:@"junshi_news_come" object:nil];
            [center addObserver:zhangsan selector:@selector(newsCome:) name:@"yule_news_come" object:nil];
            // 1
            [center addObserver:lisi selector:@selector(newsCome:) name:nil object:tx];
            // 2
            [center addObserver:wangwu selector:@selector(newsCome:) name:nil object:nil];
            
            // 4.发布新闻
    
            // tx发布了一则叫做junshi_news_come的通知
            [center postNotificationName:@"junshi_news_come"
                                  object:tx
                                userInfo:@{@"title" : @"伊拉克战争停止了",
                                           @"intro" : @"伊拉克战争停止了.........."}];
            
            // sina发布了一则叫做junshi_news_come的通知
            [center postNotificationName:@"yule_news_come"
                                  object:sina
                                userInfo:@{@"title" : @"6456456456456",
                                           @"intro" : @"7657567567567"}];
        }
        return 0;
    }
    

    这个代码就可以简单的介绍了, 关于通知的一些方法的使用.

    说了这么多的通知, 他和我们的键盘有什么关系吗???

    其实, 我们说了这么就是想讲一下, 我们的这个东西:

    Snip20160405_9.png

    我们的这个UIDevice中的一些通知, 相当一部分是用户不需要关心的, 因为 , 但是, 作为我们开发者, 我们必须知道, 这些通知, 其中就包括了, 我们的键盘通知 :

    Snip20160405_10.png

    而上面的关于我们键盘的通知, 那些又是对现在的我们来说是有用的呢???

    其实我们需要在意的是那些, 带Will的方法, 因为我们的输入框是要随着我们键盘的上升而上升, 随着我们键盘的下降而下降的.

    回到正题上: 利用上面的知识完成程序

    5. 键盘处理

    首先, 由于我们通知是控制器监听我们键盘的弹出与显示
    所以我们, 这个键盘处理的代码应该写在控制器里面

    首先我们是要在viewDidLoad这个方法中写: 其次, 按照上面的介绍方法

    1. 首先, 应该监听键盘的通知
     // 2.监听键盘的通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
    

    2 . 然后我们要在监听键盘frame变化的方法中写我们的代码:

        /**
         *  当键盘改变了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);
            }];
        }
        
        /*
         UIKeyboardAnimationCurveUserInfoKey = 7;  // 动画的执行节奏(速度)
         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
         UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; //  键盘显示完毕后的frame
         
         // 键盘隐藏
         UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 264}, {320, 216}}";
         UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 480}, {320, 216}}";
         */
    

    这里面的设置窗口颜色的那一行代码的意思, 就是:

    当我们的键盘退出的那一刻, 我们会发现, 在键盘和我们tableView之间会出现黑色区域, 这是由于: 其实在我们手机界面后面还有一个winview, 为了不让颜色显示出来所以要改变颜色


    注意这一行代码为什么是这样写:

     // 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);
            }];
    
    • 首先, 拿到我们键盘最后的frame
    • 然后再利用键盘的Y值 - view的高度
    • 这个差值就是view移动的距离

    为什么是这样算的???

    分析一下:
    假设view的高度为480
    当我们的键盘的Y值为264的时候, 那么算出来的值就是 -216(这个距离恰好是键盘隐藏时的距离)

    如果键盘的Y值为480(即为隐藏状态)-480 = 0, 那么view就是充满整个屏幕.

    1. 取消注册监听通知
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    1. 后面小处理

    用过QQ都知道, 当我们拖拽我们手机上面的界面时 , 我们的键盘会自动隐藏 , 所以我们现在做好这个工作

    要想让键盘由于用户的拖拽 自动隐藏, 我们需要监听用户的点击, 拖拽, 那就需要利用代理

    由于我们以前就介绍过scrollView, 所以我们就需要用scrollView的代理, 当然, 我们的UITableViewDelegate继承自scrollView的代理, 所以我们只用直接遵守UITableView的协议, 就好了. 然后在代理中写上隐藏键盘的代码:

    /**
     *  当开始拖拽表格的时候就会调用
     */
    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {
        // 退出键盘
        [self.view endEditing:YES];
    }
    @end
    

    06. 文本输入框处理

    一些小的处理;

    Snip20160405_11.png
    1. 第一个是显示我们的发送按钮, 以前的是return 现在我们给改成了send

    2. 第二个的那个Auto-enable-Return Key, 当我们在文本框内输入数据的时候, 就文本框没有数据, 那个发送按钮依旧可以点击, 而当我们选择这个选项之后, 就可以解决这个问题

    3. 第三个: 注意到, 我们的在文本框输入数据的时候, 发现: 数据是直接顶格显示的, 这个不够美观, 所以我们要将文本框的左边留一点空隙

    • 首先, 利用拖线 让文本框成为控制器的属性, (inputView)
    • 其实我们每一个文本框都有这两个view (leftView, rightView)我们选择就是用一个看不见的leftView来使文本框左边留出一点缝隙
     // 3.设置文本框左边显示的view
        self.inputView.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 8, 0)];
    
    • 然后, 我们的每一个view都具有这样一个枚举: leftViewMode他是用来分出这个leftView显示的方式, 时间
    self.inputView.leftViewMode = UITextFieldViewModeAlways;
    

    而这个就是永远显示的意思

    07 自动回复

    在我们的自动回复之前, 首先我们要完成的是, 我们可以发消息

    如果要使我们发送的消息可以显示到界面上, 我们首先要监听文本框和键盘的点击.

    • 遵守代理:
    UITextFieldDelegate
    
    • 实现方法:
    /**
     *  点击了return按钮(键盘最右下角的按钮)就会调用
     */
    - (BOOL)textFieldShouldReturn:(UITextField *)textField
    

    然后在这个方法中写关于我们发送消息的代码:

    首先, 我们可以试想一下, 关于自动回复, 意思就是我们发一条消息, 他就自动回复一条 , 我们是如果发的(就是简单的增加一个模型, 在模型中添加数据), 它就会如何会h回复, 所以我们直接将我们发送消息, 和系统自动回复消息, 整合到一个方法中去. 只需要告诉(传参)这个方法, 是谁发消息以及发送消息的内容:

    /**
     *  发送一条消息
     */
    - (void)addMessage:(NSString *)text type:(GJMessageType)type
    {
        // 1.数据模型
        GJMessage *msg = [[GJMessage alloc] init];
        msg.type = type;
        msg.text = text;
        // 设置数据模型的时间
        NSDate *now = [NSDate date];
        NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
        fmt.dateFormat = @"HH:mm";
        msg.time = [fmt stringFromDate:now];
        
        // 看是否需要隐藏时间
        GJMessageFrame *lastMf = [self.messageFrames lastObject];
        GJMessage *lastMsg = lastMf.message;
        msg.hideTime = [msg.time isEqualToString:lastMsg.time];
        
        // 2.frame模型
        GJMessageFrame *mf = [[GJMessageFrame 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];
    }
    

    而发送消息的步骤就在上面的方法中

    而这样我们的文本框代理需要做的就是调用该方法然后

    将是谁发的, 以及发送的内容告诉这个方法便可:

    #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;
    }
    

    至于自动回复, 我们写了一个这样的plist文件:

    Snip20160405_12.png

    只要我们遍历我们发送的内容中的每一个汉字, 有和上面相同的汉字, 就自动回复后面的文字, 如果没有就直接发送滚蛋

    **取出plist文件: **

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

    遍历

    /**
     *  根据自己发的内容取得自动回复的内容
     *
     *  @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 @"滚蛋";
    }
    

    这样就完成了, 自动回复功能了.

    作者说:

    以后的代码量可能比较多, 所以在这里我就不写了. 也是为我自己节省时间那嘛, 见谅啦各位

    相关文章

      网友评论

      • 封竹:输入框 你是怎么固定放 table view 底部的
        高俊:@封竹 在设置tableView的frame的时候, 预先留出一部分空间给输入框
      • _常小仙儿:不错,理解很透彻
        高俊:@爱生活Geek 谢谢夸奖 :relaxed:
      • 流刃若火泣:有demo吗?github也行啊
        高俊:@流刃若火泣 不好意思, 这个我还真没有, 抱歉了
      • 码蚁Q:不错, 继续加油

      本文标题:UI之 08-QQ自定义布局

      本文链接:https://www.haomeiwen.com/subject/lwaylttx.html