zoukankan      html  css  js  c++  java
  • 猫猫学IOS(十八)UI之QQ聊天布局_键盘通知实现自动弹出隐藏_自动回复

    猫猫分享,必须精品

    素材代码地址:http://blog.csdn.net/u013357243/article/details/45000699
    原文地址:http://blog.csdn.net/u013357243?viewmode=contents

    先看图片

    第一步完成tableView和Cell的架子的图
    这里写图片描述
    完善图片
    这里写图片描述
    键盘弹出设置后图片:
    这里写图片描述
    自动回复图:
    这里写图片描述


    粗狂的架子

    tableView和Cell的创建

    首相tableView为了学习方便就直接用stroyBoard拖拽了,包括一些学习意义不大的图片等等都直接在stroyBoard中拖拽设置了,如果有想全部用代码写一遍的可以看我前面的文章,里面有各种各样的介绍。

    其实我们知道,上半部分就是一个tableView,但是需要我们自己来编写,这里我们的数据来自plist文件,首先建立了两个模型:
    NYMessageModel还有NYMessageFrameModel
    其中NYMessageFrameModel有这些属性
    
    @interface NYMessageFrameModel : NSObject
    //文本的位置
    @property (nonatomic, assign, readonly) CGRect textF;
    //时间的位置
    @property (nonatomic, assign, readonly) CGRect timeF;
    //头像的位置
    @property (nonatomic, assign, readonly) CGRect iconF;
    //每个cell的高度
    @property (nonatomic, assign, readonly) CGFloat cellH;
    //数据模型
    @property (nonatomic, strong) NYMessageModel *message;
    
    +(NSMutableArray *) messageFrames;
    @end
    

    这里的思路是有个Frame的模型,里面有message模型,
    我们返回的时Frame的一个数组,里面的每一个frame都有一个message,并且在调用的时候我们就调用frame的setMseeage方法,将从而将这个frame的其他的比如cellH等等属性设置好,这样我们的frame里面的数据边全面了,返回的是一个frame的array,其他人可以拿来直接就用了。
    实现里面是这样写的,主要就是靠调用messageFrame的setMessage方法——设置数据模型方法,将各个的Frame设置好。

    @implementation NYMessageFrameModel
    +(NSMutableArray *) messageFrames
    {
            NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"messages.plist" ofType:nil]];
            NSMutableArray *arrayM = [NSMutableArray array];
            for (NSDictionary *dict in array) {
                NYMessageFrameModel * messageFrame = [[NYMessageFrameModel alloc] init];
                //这时候 arrayM中得最后一个就是前一个。
                NYMessageFrameModel *lastFrame = [arrayM lastObject];
                //当前要加进去的Frame
                NYMessageModel *message =[NYMessageModel messageWithDict:dict];
                message.hideTime = [message.time isEqualToString:lastFrame.message.time];
    
                messageFrame.message = message;
    
                [arrayM addObject:messageFrame];
            }
            return arrayM;
    }
    
    -(void)setMessage:(NYMessageModel *)message
    {
        _message = message;
    
        //定义间距
        CGFloat padding = 10;
    
        //设置时间位置
        if (!message.hideTime) {
            CGFloat timeX = 0;
            CGFloat timeY = 0;
            CGFloat timeW = bScreenWidth;
            CGFloat timeH = bNormalH;
            _timeF = CGRectMake(timeX, timeY, timeW, timeH);
        }
    
    
        //设置头像位置
        CGFloat iconX;
        CGFloat iconY = CGRectGetMaxY(_timeF) + padding;
        CGFloat iconW = bIconW;
        CGFloat iconH = bIconH;
        if (message.type == NYMessagesModelTypeGatsby) {//自己发的
            iconX = bScreenWidth - padding - iconW;
        }else{//别人发的
            iconX = padding ;
        }
        _iconF = CGRectMake(iconX, iconY, iconW, iconH);
    
        //设置正文位置
        CGFloat textX ;
        CGFloat textY = iconY;
        CGSize textRealSize = [message.text boundingRectWithSize:CGSizeMake(150, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:bBtnFont} context:nil].size;
    
        CGSize btnSize = CGSizeMake(textRealSize.width + 40, textRealSize.height + 40);
    
        if (message.type == NYMessagesModelTypeGatsby) {
            textX = bScreenWidth -padding - iconW - padding - btnSize.width;
        }else{
            textX = padding + iconW + padding;
        }
    
    //    _textF = CGRectMake(textX, textY, textRealSize.width, textRealSize.height);
        _textF = (CGRect){{textX, textY},btnSize};
    
        //cell 的高
        CGFloat iconMaxY = CGRectGetMaxY(_iconF);
        CGFloat textMaxY = CGRectGetMaxY(_textF);
        _cellH = MAX(iconMaxY, textMaxY);
    
    }
    @end

    然后这是模型了,其他的在前面的都写过


    这里在放一下自定义cell的东东

    @interface NYMessageCell()
    //时间
    @property (nonatomic, weak) UILabel *timeView;
    //正文
    @property (nonatomic, weak) UIButton *textView;
    //头像
    @property (nonatomic, weak) UIImageView *iconView;
    
    
    @end
    
    @implementation NYMessageCell
    
    //用set方法来设置各个组件的位置
    -(void)setMessageFrame:(NYMessageFrameModel *)messageFrame{
        _messageFrame = messageFrame;
    //    时间位置
        _timeView.frame = messageFrame.timeF;
          //头像位置
        _iconView.frame = messageFrame.iconF;
    
        //正文位置
        _textView.frame = messageFrame.textF;
    
    //    设置时间内容
        NYMessageModel *model = messageFrame.message;
        _timeView.text = model.time;
        //头像内容
        if (model.type == NYMessagesModelTypeGatsby) {
            self.iconView.image = [UIImage imageNamed:@"Gatsby"];
        }else{
            self.iconView.image = [UIImage imageNamed:@"Jobs"];
        }
        //正文内容 这里是按钮,不能用点语法,因为他有好多状态。
        [self.textView setTitle:model.text forState:UIControlStateNormal];
        //设置正文背景图片
        if (model.type == NYMessagesModelTypeGatsby) {
            [self.textView setBackgroundImage:[UIImage resizeWithImageName:@"chat_send_nor"] forState:UIControlStateNormal];
    
        }else{
            //设置正文的背景图片
            [self.textView setBackgroundImage:[UIImage resizeWithImageName:@"chat_recive_nor"] forState:UIControlStateNormal];
        }
    
    }
    
    
    
    +(instancetype)messageCellWithTableView:(UITableView *)tableView
    {
        static NSString *ID = @"messageCell";
        NYMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[self alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
        }
    
        return cell;
    }
    
    - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
    {
        self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
        if (self) {
    
    
    
            //1时间
            UILabel *time = [[UILabel alloc]init];
            time.textAlignment = NSTextAlignmentCenter;
            time.font = [UIFont systemFontOfSize:13.0f];
            [self.contentView addSubview:time];
            self.timeView = time;
    
    
            //正文
            UIButton *text = [[UIButton alloc]init];
            text.titleLabel.font = bBtnFont;
            text.titleLabel.numberOfLines = 0;//自动换行
            text.contentEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20);//设置按钮文字的内边距
            [text setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//设置文字颜色
            [self.contentView addSubview:text];
            self.textView = text;
    
            //头像
            UIImageView *icon = [[UIImageView alloc]init];
            [self.contentView addSubview:icon];
            self.iconView = icon;
    
        }
        return self;
    }
    
    - (void)awakeFromNib
    {
        // Initialization code
    }
    
    - (void)setSelected:(BOOL)selected animated:(BOOL)animated
    {
        [super setSelected:selected animated:animated];
    
        // Configure the view for the selected state
    }
    
    @end

    然后写一下一些常用到得东西吧

    设置每个cell的高度

    代理方法

    /**每个cell的高度*/
    -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return [self.messageFrames[indexPath.row] cellH];
    }

    数据源方法设置tableView的行数和每个cell

    这里就不啰嗦了。

    /**行数*/
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
        return self.messageFrames.count;
    }
    /**cell*/
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NYMessageCell *cell = [NYMessageCell messageCellWithTableView:tableView];
        NYMessageFrameModel * messageFrame = self.messageFrames[indexPath.row];
    
        cell.messageFrame = messageFrame;
        return cell;
    }

    隐藏状态栏

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

    还有一种方法是将tableView向下面拉一下,这个主要用的原理是tableView继承自UIScrollView

     self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);

    去除cell的间隔线,点击选中功能

    点击选中功能去掉

    self.tableView.allowsSelection = NO;

    去除cell的间隔线

        //移除分割线
        self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

    还有设置颜色的方法,这里也拿出来记忆一下吧

    //设置背景颜色,要将cell颜色清空
        self.tableView.backgroundColor = [UIColor colorWithRed:225/255.0 green:225/255.0 blue:225/255.0 alpha:1.0];

    把时间相同的隐藏起来

    时间相同的cell 把时间设置隐藏
    首先为模型增加时间是否隐藏属性

    /**是否隐藏时间*/
    @property (nonatomic, assign) BOOL hideTime;

    然后再取的时候和上一个模型的时间比较,如果相同就隐藏——————这里可以做的很复杂,可以判短多少时间内我们隐藏。。。

    NYMessageFrameModel * messageFrame = [[NYMessageFrameModel alloc] init];
                //这时候 arrayM中得最后一个就是前一个。
                NYMessageFrameModel *lastFrame = [arrayM lastObject];
                //当前要加进去的Frame
                NYMessageModel *message =[NYMessageModel messageWithDict:dict];
                message.hideTime = [message.time isEqualToString:lastFrame.message.time];

    然后就是在模型里面设置时间位置了

    //设置时间位置
        if (!message.hideTime) {
            CGFloat timeX = 0;
            CGFloat timeY = 0;
            CGFloat timeW = bScreenWidth;
            CGFloat timeH = bNormalH;
            _timeF = CGRectMake(timeX, timeY, timeW, timeH);
        }

    简单理解,如果这个要显示时间,就有时间的位置,不显示,就不要。

    面向对象的思想,谁的数据谁负责。还有有人喜欢把这部分写到cell里面,但是cell主要负责展示的,一个好的数据模型,我们当然是想要直接就能用啦。

    文本自动换行文字的内边距文字颜色属性

    text.titleLabel.numberOfLines = 0;//自动换行
            text.contentEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20);//设置按钮文字的内边距
            [text setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//设置文字颜色
    

    背景图片拉伸

    拉伸这里主要是用中间的某一部分拉伸。
    这个用起来不错,就是说起来有点。。。
    我得理解就是我就拖拽中间一个点,其他不变。

    + (UIImage *)resizeWithImageName:(NSString *)name
    {
        UIImage *normal = [UIImage imageNamed:name];
    
        //    CGFloat w = normal.size.width * 0.5f ;
        //    CGFloat h = normal.size.height *0.5f ;
    
        CGFloat w = normal.size.width*0.5;
        CGFloat h = normal.size.height*0.5;
        //传入上下左右不需要拉升的编剧,只拉伸中间部分
        return [normal resizableImageWithCapInsets:UIEdgeInsetsMake(h, w, h, w)];
    
    
        // 1 = width - leftCapWidth  - right
        // 1 = height - topCapHeight  - bottom
    
        //传入上下左右不需要拉升的编剧,只拉伸中间部分,并且传入模式(平铺/拉伸)
        //    [normal :<#(UIEdgeInsets)#> resizingMode:<#(UIImageResizingMode)#>]
    
        //只用传入左边和顶部不需要拉伸的位置,系统会算出右边和底部不需要拉升的位置。并且中间有1X1的点用于拉伸或者平铺
        // 1 = width - leftCapWidth  - right
        // 1 = height - topCapHeight  - bottom
        //    return [normal stretchableImageWithLeftCapWidth:w topCapHeight:h];
    }

    键盘通知

    通知理解

    通知包括:
    通知的发布
    通知的监听
    通知的移除
    1,每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信。
    2,任何一个对象都可以向通知中心发布通知(NSNotification),描述自己在做什么。其他感兴趣的对象(Observer)可以申请在某个特定通知发布时(或者在某个特定的对象发布通知时)收到这个通知。
    这里写图片描述

    通知的用法

    • ⼀一个完整的通知⼀一般包含3个属性:

     - (NSString *)name; // 通知的名称
     -(id)object; // 通知发布者(是谁要发布通知)
     - (NSDictionary *)userInfo; // ⼀一些额外的信息(通知发布者传递给通知接收者 的信息内容)

    • 初始化⼀一个通知(NSNotification)对象

     + (instancetype)notificationWithName:(NSString *)aName object:
    (id)anObject;
     + (instancetype)notificationWithName:(NSString *)aName object:
    (id)anObject userInfo:(NSDictionary *)aUserInfo;
     - (instancetype)initWithName:(NSString *)name object:(id)object
       userInfo:(NSDictionary *)userInfo;

    通知中⼼心(NSNotificationCenter)提供了相应的⽅方法来帮助发布通知

    • - (void)postNotification:(NSNotification *)notification;

    ➢ 发布⼀一个notification通知,可在notification对象中设置通知的名称、通知发布者、额外信息 等

    • - (void)postNotificationName:(NSString *)aName object: (id)anObject;

    ➢ 发布⼀一个名称为aName的通知,anObject为这个通知的发布者,aUserInfo为额外信息

    • - (void)postNotificationName:(NSString *)aName object:(id)anObject
    userInfo:(NSDictionary *)aUserInfo;

    注册通知监听器

    通知中⼼心(NSNotificationCenter)提供了⽅方法来注册⼀一个监听通知的监听器(Observer)
    • - (void)addObserver:(id)observer selector:(SEL)aSelector name:
    (NSString *)aName object:(id)anObject;
    ➢ observer:监听器,即谁要接收这个通知
    ➢ aSelector:收到通知后,回调监听器的这个⽅方法,并且把通知对象当做参数传⼊入
    ➢ aName:通知的名称。如果为nil,那么⽆无论通知的名称是什么,监听器都能收到这个通知 ➢ anObject:通知发布者。如果为anObject和aName都为nil,监听器都收到所有的通知

    注册通知监听器

    • -(id)addObserverForName:(NSString*)nameobject:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^) (NSNotification *note))block;

    ➢ name:通知的名称
    ➢ obj:通知发布者
    ➢ block:收到对应的通知时,会回调这个block
    ➢ queue:决定了block在哪个操作队列中执⾏行,如果传nil,默认在当前操作队 列中同步执⾏行

    取消注册通知监听器

    • 通知中⼼心不会保留(retain)监听器对象,在通知中⼼心注册过的对象,必须在该对象释放前取消 注册。否则,当相应的通知再次出现时,通知中⼼心仍然会向该监听器发送消息。因为相应 的监听器对象已经被释放了,所以可能会导致应⽤用崩溃
    • 通知中⼼心提供了相应的⽅方法来取消注册监听器

     - (void)removeObserver:(id)observer;
     - (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;

    • ⼀一般在监听器销毁之前取消注册(如在监听器中加⼊入下列代码):

     - (void)dealloc {
    //[super dealloc]; ⾮非ARC中需要调⽤用此句
       [[NSNotificationCenter defaultCenter] removeObserver:self];
    }

    UIDevice通知

    UIDevice类提供了⼀一个单粒对象,它代表着设备,通过它可以获得⼀一些设备相关的信息,⽐比 如电池电量值(batteryLevel)、电池状态(batteryState)、设备的类型(model,⽐比如iPod、iPhone 等)、设备的系统(systemVersion)
    通过[UIDevice currentDevice]可以获取这个单粒对象
    UIDevice对象会不间断地发布⼀一些通知,下列是UIDevice对象所发布通知的名称常量:

    UIDeviceOrientationDidChangeNotification// 设备旋转
    UIDeviceBatteryStateDidChangeNotification// 电池状态改变
    UIDeviceBatteryLevelDidChangeNotification // 电池电量改变
    UIDeviceProximityStateDidChangeNotification// 近距离传感器(⽐比如设备贴 近了使⽤用者的脸部)

    键盘通知

    • 我们经常需要在键盘弹出或者隐藏的时候做⼀一些特定的操作,因此需要监听键盘的状态
    • 键盘状态改变的时候,系统会发出⼀一些特定的通知

    UIKeyboardWillShowNotification // 键盘即将显⽰示
     UIKeyboardDidShowNotification // 键盘显⽰示完毕
    UIKeyboardWillHideNotification // 键盘即将隐藏
     UIKeyboardDidHideNotification // 键盘隐藏完毕
     UIKeyboardWillChangeFrameNotification // 键盘的位置尺⼨寸即将发⽣生改变 
      UIKeyboardDidChangeFrameNotification // 键盘的位置尺⼨寸改变完毕

    通知和代理的选择

    • 共同点
    ➢ 利⽤用通知和代理都能完成对象之间的通信 (⽐比如A对象告诉D对象发⽣生了什么事情, A对象传递数据给D对象)
    • 不同点
    ➢ 代理 : ⼀一对⼀一关系(1个对象只能告诉另1个对象发⽣生了什么事情)
    ➢ 通知 : 多对多关系(1个对象能告诉N个对象发⽣生了什么事情, 1个对象能得知N个对象发 ⽣生了什么事情)

    QQ中键盘弹出或隐藏

    首先监听通知中心 监听keyboardChange
    运用原理就是如果键盘变化了,就通知我们的@selector方法。

    //监听通知中心 监听keyboardChange
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];

    键盘弹出

    -(void)keyboardDidChangeFrame:(NSNotification *)noti
    {
        //改变window的背景颜色
        self.view.window.backgroundColor = self.tableView.backgroundColor;
    
        //  键盘退出的frame
        CGRect frame = [noti.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    
    
        //键盘实时y
        CGFloat keyY = frame.origin.y;
    
        //屏幕的高度
        CGFloat screenH = [[UIScreen mainScreen] bounds].size.height;
    
        //动画时间
        CGFloat keyDuration = [noti.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];
    
        //执行动画
        [UIView animateWithDuration:keyDuration animations:^{
            self.view.transform = CGAffineTransformMakeTranslation(0, keyY - screenH);
        }];
    
    }

    键盘隐藏

    当tableview 滚动的时候 结束编辑事件 (退出键盘)

    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {
        [self.view endEditing:YES];
    }
    

    设置输入框的左边距

    inputView是下面的输入框。。。

    //设置输入框的左边距
        self.inputView.leftView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 8, 0)];
        //设置一直显示
        self.inputView.leftViewMode = UITextFieldViewModeAlways;

    自动回复还有键盘的return可以返回信息

    效果演示

    效果是这样的
    这里写图片描述

    这里写图片描述

    然后就是自动回复了,这里比较简单就是这样的:回复你一个滚蛋吗。。。
    里面还有一个数组,我设置的几个判断里面含有哪一些字就能回复什么,这里就不掩饰了。

    对发送按钮进行处理(键盘中的)

    首先设置代理UITextFieldDelegate 然后。。。

    #pragma mark - UITextField代理方法
    
    /**直接return YES即可实现在输入框输入文字,然后按下return 然后textField.text便出现内容*/
    -(BOOL)textFieldShouldReturn:(UITextField *)textField
    {
        //1,发送一条数据。
        [self addMessage:textField.text type:NYMessagesModelTypeGatsby];
    
        //自动回复
        NSString *autoStr = [self autoReplayWithText:textField.text];
        //将自动回复添加成一天聊天信息
        [self addMessage:autoStr type:NYMessagesModelTypeJobs];
    
        return YES;
    }
    
    //自动回复一条聊天信息
    - (NSString *)autoReplayWithText:(NSString *)text
    {
        //3自动回复
        for (int a = 0 ; a < text.length; a++) {
    
            NSString *subStr = [text substringWithRange:NSMakeRange(a, 1)];
    
            if (self.autoReplay[subStr]) {
                return  self.autoReplay[subStr];
            }
        }
    
        return @"滚蛋吗0";
    }
    
    //发送一条数据。
    -(void)addMessage:(NSString *)text type:(NYMessagesModelType)type
    
    {
    
        //如果内容为空,那么就直接返回
        if (text == nil) {
            return ;
        }
        //1,添加模型数据
        NYMessageModel *message = [[NYMessageModel alloc]init];
        //设置数据的值
        message.time = @"16:88";
        message.text = text;
        message.type = type;
        //设置内容的frame
        NYMessageFrameModel * fm = [[NYMessageFrameModel alloc]init];
        //将模型设置给frame
        fm.message = message;
    
        //添加到数值
        [self.messageFrames addObject:fm];
    
        //2,刷新表格
        [self.tableView reloadData];
        //关闭键盘
        [self scrollViewWillBeginDragging:self.tableView];
        //清空输入框的内容
        self.inputView.text = @"";
    
        //3,自动上移
        //移动的位置
        NSIndexPath *path = [NSIndexPath indexPathForRow:self.messageFrames.count - 1 inSection:0];
        //真正去的位置 atatScrollPosition :滚到得位置
        [self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionBottom animated:YES];
    }

    ps:新建iOS交流学习群:304570962
    可以加猫猫QQ:1764541256 或则微信znycat
    让我们一起努力学习吧。
    原文:http://blog.csdn.net/u013357243?viewmode=contents

  • 相关阅读:
    Sprinig.net 双向绑定 Bidirectional data binding and data model management 和 UpdatePanel
    Memcached是什么
    Spring.net 网络示例 codeproject
    jquery.modalbox.show 插件
    UVA 639 Don't Get Rooked
    UVA 539 The Settlers of Catan
    UVA 301 Transportation
    UVA 331 Mapping the Swaps
    UVA 216 Getting in Line
    UVA 10344 23 out of 5
  • 原文地址:https://www.cnblogs.com/znycat/p/4419169.html
Copyright © 2011-2022 走看看