QQ聊天表情输入

作者: 小石头呢 | 来源:发表于2019-03-06 08:27 被阅读40次

源代码

文本链接:
链接:
https://pan.baidu.com/s/1hOJsS1scX05eQVjWtpEZyg 密码:rdov
可执行文件链接:
链接:
https://pan.baidu.com/s/1ihKun_L4Boj9O-7RncW7eQ 密码:qdm1

主要代码:

//
//  OperationView.h
//  QQ聊天表情输入
//
//  Created by 许磊 on 2019/2/27.
//  Copyright © 2019年 xulei. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface OperationView : UIView

//创建方法交给自己 自己最了解

/**创建一个OperationView,控制输入位置显示*/
+(OperationView *)showOperationViewWithFrame:(CGRect)frame andFatherView:(UIView *)containerView;

/**隐藏键盘*/
-(void)hideKeyboard;

@end


//
//  OperationView.m
//  QQ聊天表情输入
//
//  Created by 许磊 on 2019/2/27.
//  Copyright © 2019年 xulei. All rights reserved.
//

#import "OperationView.h"
#import "FaceView.h"

//键盘类型
typedef enum {
    kKeyboardTypeKeyboard, //正常键盘 0
    kKeyboardTypeFace        //表情键盘  1
}kKeyboardType;

@interface OperationView()<UITextViewDelegate>

//关联的文本框
@property (weak, nonatomic) IBOutlet UITextView *textView;

//属性化的FaceView
@property (nonatomic,strong) FaceView *faceView;

//键盘是否可移动
@property (nonatomic,assign) BOOL shouldMove;

@end

@implementation OperationView

#pragma mark -------faceView ---------
//重写faceView的get方法
-(FaceView *)faceView{
    
    if (_faceView == nil) {
        
        self.faceView = [FaceView faceViewWithFrame:CGRectMake(0, 0, self.frame.size.width, [UIScreen mainScreen].bounds.size.height- self.frame.origin.y-50) andContainer:nil selectBlock:^(UIImage *faceImage) {
            
            //表情按钮被点击了
            [self showImageToTextView:faceImage];
            
        } deleteBlock:^{
            
            //删除按钮被点击了
            [self deleteImageFromTextView];
            
        }];
    }
    
    return _faceView;
}

#pragma mark -------showImageToTextView ---------
//显示表情到输入框中
-(void)showImageToTextView:(UIImage *)faceImage{
    
    //创建图片附件
    NSTextAttachment *attach = [[NSTextAttachment alloc] init];
    attach.image =faceImage;
    attach.bounds = CGRectMake(0, 0, 25, 25);
    
    //新的图片-将图片附件转化为属性字符串
    NSAttributedString *attachStr = [NSAttributedString attributedStringWithAttachment:attach];
    
    //输入框中的内容-创建可变的属性字符串 保存当前显示的文本内容
    NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:self.textView.attributedText];
    
    //将新的图片的字符串加到输入框中
    [attStr appendAttributedString:attachStr];
    
    //将新的拼接内容显示出来
    self.textView.attributedText = attStr;
    
}

#pragma mark -------deleteImageFromTextView ---------
//删除表情
-(void)deleteImageFromTextView{
    
    if (self.textView.text.length > 0) {
        
        //获取原来的内容
        NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithAttributedString:self.textView.attributedText];
        
        //删除最后一个字符的内容
        [attr deleteCharactersInRange:NSMakeRange(attr.length-1, 1)];
        
        //显示
        self.textView.attributedText = attr;
    }
    
}



#pragma mark -------showOperationViewWithFrame: ---------
//创建
+(OperationView *)showOperationViewWithFrame:(CGRect)frame andFatherView:(UIView *)containerView{
    
    //xib创建 xib放在数组里 只有它一个
    OperationView *op = [[[NSBundle mainBundle] loadNibNamed:@"OperationView" owner:nil options:nil] firstObject];
    
    //确定位置
    op.frame = frame;
    
    //添加
    [containerView addSubview:op];
    
    return op;
}

#pragma mark -------awakeFromNib ---------
//当.nib文件被加载的时
-(void)awakeFromNib{
    
    [super awakeFromNib];
    
    NSLog(@"here");
    [self keyboardNotifi];
    
    //设置代理
    self.textView.delegate = self;
    
    //刚开始 键盘可以移动
    self.shouldMove = YES;
    
}

#pragma mark -------keyboardNotifi ---------
//设置消息监听
-(void)keyboardNotifi{
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
}

#pragma mark -------keyboardFrameChange: ---------
//移动OperationView
-(void)keyboardFrameChange:(NSNotification *)notifi{
    NSLog(@"%@",notifi);
    
    //获取键盘弹出之后的y
    
    //1.获取userInfo 对应的字典
    NSDictionary *infoDic = notifi.userInfo;
    
    //2.获取键盘停止时的坐标
    CGRect endRect = [[infoDic objectForKey:@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
    
    //3.改变操作视图的y坐标 0.25秒出现
    if (_shouldMove == YES) {
        [UIView animateWithDuration:0.25 animations:^{
            self.frame = CGRectMake(0,endRect.origin.y-self.frame.size.height,self.frame.size.width,self.frame.size.height);
        }];
    }
    
}

#pragma mark -------hideKeyboard ---------
//隐藏键盘
-(void)hideKeyboard{

    //可以移动
    self.shouldMove = YES;
    
    [self.textView resignFirstResponder];
}

#pragma mark -------changeKeyboardType: ---------
//切换键盘
- (IBAction)changeKeyboardType:(UIButton *)sender {
    
    //隐藏当前的键盘
    [self.textView resignFirstResponder];
    
    if (sender.tag == kKeyboardTypeKeyboard) {
        
        //图片切换
        [sender setBackgroundImage:[UIImage imageNamed:@"keyboard"] forState:UIControlStateNormal];
        
        //切换为表情键盘
        self.textView.inputView = self.faceView;
        
        sender.tag = kKeyboardTypeFace;
        
    } else {
        
        //图片切换
        [sender setBackgroundImage:[UIImage imageNamed:@"chat_bottom_smile_nor"] forState:UIControlStateNormal];
        
        //切换为正常键盘
        self.textView.inputView = nil;
        
         sender.tag = kKeyboardTypeKeyboard;
        
    }
    
    //添加立体旋转效果
    //[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:sender cache:NO];
    [UIView animateWithDuration:.25 animations:^{
        
        sender.layer.transform = CATransform3DRotate(sender.layer.transform, M_PI, 0, 1, 0);
        
    }];
    
    [self performSelector:@selector(showKeyboard) withObject:nil afterDelay:0.25];
    
}

#pragma mark -------showKeyboard ---------
//显示键盘
-(void)showKeyboard{
    
    [self.textView becomeFirstResponder];
}

#pragma mark -------textViewDidBeginEditing: ---------
//开始编辑了
-(void)textViewDidBeginEditing:(UITextView *)textView{
    //不能移动
    self.shouldMove = NO;
}

#pragma mark -------textViewDidEndEditing: ---------
//结束编辑了
-(void)textViewDidEndEditing:(UITextView *)textView{
    //可以移动
    self.shouldMove = YES;
}


@end


//
//  FaceView.h
//  QQ聊天表情输入
//
//  Created by 许磊 on 2019/2/28.
//  Copyright © 2019年 xulei. All rights reserved.
//

#import <UIKit/UIKit.h>

/**定义一个Block用于返回一个UIImage对象*/
typedef void(^SelectBlock)(UIImage *faceImage);

/**定义一个Block用于传递一个消息*/
typedef void(^DeleteBlock)(void);

@interface FaceView : UIView

/**创建一个FaceView,显示键盘位置视图*/
+(FaceView *)faceViewWithFrame:(CGRect)frame andContainer:(UIView *)containerView selectBlock:(SelectBlock)s_block deleteBlock:(DeleteBlock)d_block;


@end


//
//  FaceView.m
//  QQ聊天表情输入
//
//  Created by 许磊 on 2019/2/28.
//  Copyright © 2019年 xulei. All rights reserved.
//

#import "FaceView.h"
#import "FaceModel.h"
#import "GlassView.h"

#define kPadding ((self.frame.size.width-6*35)/7)

@interface FaceView()<UIScrollViewDelegate>

/**关联的UIScrollView*/
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;

/**关联的UIPageControl*/
@property (weak, nonatomic) IBOutlet UIPageControl *pageControl;

/**存放表情模型的数组 */
@property (nonatomic,strong) NSMutableArray *faceModelsArray;

/**定义一个SelectBlock类型的属性*/
@property (nonatomic,copy) SelectBlock s_block;

/**定义一个DeleteBlock类型的属性*/
@property (nonatomic,copy) DeleteBlock d_block;

/**属性化的GlassView*/
@property (nonatomic,strong) GlassView *glassView;

@end

@implementation FaceView

#pragma mark -------glassView ---------
//重写glassView的get方法
-(GlassView *)glassView{
    
    if (_glassView == nil) {
        
        self.glassView = [GlassView glassViewWithFrame:CGRectMake(0, 0, 60, 91) container:self];
    }
    
    //隐藏
    _glassView.hidden = NO;
    
    return _glassView;
}


#pragma mark -------longPressGesture: ---------
//长按手势 先拖拽到控件再拖拽到代码
- (IBAction)longPressGesture:(UILongPressGestureRecognizer *)sender {
    
    if (sender.state == UIGestureRecognizerStateEnded) {
        
        //结束了 隐藏放大镜
        self.glassView.hidden = YES;
        
    } else {
        
        //改变位置
        CGPoint location = [sender locationInView:_scrollView];
        self.glassView.center = CGPointMake(location.x, location.y-48);
        
        //得到模型
        FaceModel *model = [self.faceModelsArray objectAtIndex:[self caculateImage:location]];
        UIImage *face = [UIImage imageNamed:model.imageName];
        
        //显示到放大镜上
        self.glassView.faceImage.image = face;
        
    }
    
}


#pragma mark -------caculateImage: ---------
//计算当前放大镜该显示什么图片
-(NSInteger)caculateImage:(CGPoint) location {
    
    int page = location.x / self.scrollView.frame.size.width;
    
    int row = (location.y - 10) / (35 + kPadding);
    
    int colum = (location.x - page *self.scrollView.frame.size.width -10) / (35 + kPadding);
    
    int index = page *24 + row *6 + colum;
    
    return index;
}


#pragma mark -------faceViewWithFrame: ---------
//创建
+(FaceView *)faceViewWithFrame:(CGRect)frame andContainer:(UIView *)containerView selectBlock:(SelectBlock)s_block deleteBlock:(DeleteBlock)d_block{
    
    //创建
    FaceView *fv = [[[NSBundle mainBundle] loadNibNamed:@"FaceView" owner:nil options:nil] firstObject];
    
    //确定block
    fv.s_block = s_block;
    fv.d_block = d_block;
    
    //添加
    [containerView addSubview:fv];
    
    return fv;
}

#pragma mark -------awakeFromNib ---------
//当.nib文件被加载的时
-(void)awakeFromNib{
    
    [super awakeFromNib];
    
    //加载数据
    [self loadFaceData];
    
    //显示表情
    [self UIInit];
    
    //设置代理
    self.scrollView.delegate = self;
    
}

#pragma mark -------loadFaceData ---------
//加载表情
-(void)loadFaceData{
    
    //读取表信息 短语 图片
    //扩展 专门写获取数据的类
    
    //获得路径
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"face" ofType:@"plist"];
    
    //读取文件的内容
    NSArray *array = [NSArray arrayWithContentsOfFile:filePath];
    
    //初始化数组
    self.faceModelsArray = [NSMutableArray array];
    
    //对读取的内容解析
    for (NSDictionary *faceDic in array) {
        
        //图片名
        NSString *imageName = [faceDic objectForKey:@"png"];
        
        //把数据封装成对象
        FaceModel *model = [FaceModel new];
        model.imageName = imageName;
        
        //将封装的对象保存到数组
        [self.faceModelsArray addObject:model];
        
    }
    
}

#pragma mark -------UIInit ---------
//显示表情
-(void)UIInit{
    
    //循环创建图片
    for (int i = 0; i < _faceModelsArray.count; i++) {
        
        //获取i对应的表情模型
        FaceModel *model = [self.faceModelsArray objectAtIndex:i];
        
        //创建
        //计算x y坐标
        int page = i/23;
        int row = (i-page*23)/6;
        int column = (i-page*23-row*6);
        UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(page*self.scrollView.frame.size.width+kPadding+(35+kPadding)*column, kPadding+(35+kPadding)*row, 35, 35)];
        
        //添加表情
        [btn setImage:[UIImage imageNamed:model.imageName] forState:UIControlStateNormal];
        
        //添加事件
        [btn addTarget:self action:@selector(faceButtonDidClicked:) forControlEvents:UIControlEventTouchUpInside];
        
        //添加
        [self.scrollView addSubview:btn];
    }
    
    //创建删除图片
    for (int i = 0; i < _faceModelsArray.count/24+1; i++) {
        
        //创建
        UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake((i+1)*self.scrollView.frame.size.width-kPadding-35,kPadding+(kPadding+35)*3, 35, 35)];
        
         //添加表情
        [btn setImage:[UIImage imageNamed:@"compose_emotion_delete_highlighted"] forState:UIControlStateNormal];
        
        //添加事件
        [btn addTarget:self action:@selector(deleteButtonDidClicked:) forControlEvents:UIControlEventTouchUpInside];
        
        //添加
        [self.scrollView addSubview:btn];
    }
    
    //设置scrollView的contentsize
    self.scrollView.contentSize = CGSizeMake((_faceModelsArray.count/24+1)*self.scrollView.frame.size.width, self.scrollView.frame.size.height);
    
}

#pragma mark -------scrollViewDidEndDecelerating: ---------
//滚动结束 减速停止的时候就开始执行
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    
    //设置页数
    int page = scrollView.contentOffset.x/self.scrollView.frame.size.width;
    
    [self.pageControl setCurrentPage:page];
}

#pragma mark -------pageControlerValueChange: ---------
//scrollView随着pageController变动
- (IBAction)pageControlerValueChange:(UIPageControl *)sender {
    
    [self.scrollView setContentOffset:CGPointMake(sender.currentPage * _scrollView.frame.size.width, 0) animated:YES];
}


#pragma mark -------faceButtonDidClicked: ---------
//表情按钮被点击时做的事
-(void)faceButtonDidClicked:(UIButton *)sender{
    
    //回调
    self.s_block(sender.imageView.image);
}

#pragma mark -------faceButtonDidClicked: ---------
//删除按钮被点击时做的事
-(void)deleteButtonDidClicked:(UIButton *)sender{
    
    //回调
    self.d_block();
}


@end


//
//  FaceModel.h
//  QQ聊天表情输入
//
//  Created by 许磊 on 2019/2/28.
//  Copyright © 2019年 xulei. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface FaceModel : NSObject

//表情图片名
@property (nonatomic,strong) NSString *imageName;

@end


//
//  FaceModel.m
//  QQ聊天表情输入
//
//  Created by 许磊 on 2019/2/28.
//  Copyright © 2019年 xulei. All rights reserved.
//

#import "FaceModel.h"

@implementation FaceModel

@end


//
//  GlassView.h
//  QQ聊天表情输入
//
//  Created by 许磊 on 2019/3/1.
//  Copyright © 2019年 xulei. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface GlassView : UIView

/**关联的放大的图片*/
@property (weak, nonatomic) IBOutlet UIImageView *faceImage;

/**创建一个GlassView,显示放大的图片*/
+(GlassView *)glassViewWithFrame:(CGRect)frame container:(UIView *)containerView;

@end


//
//  GlassView.m
//  QQ聊天表情输入
//
//  Created by 许磊 on 2019/3/1.
//  Copyright © 2019年 xulei. All rights reserved.
//

#import "GlassView.h"

@implementation GlassView

#pragma mark -------glassViewWithFrame: ---------
/**创建一个GlassView,显示放大的图片*/
+(GlassView *)glassViewWithFrame:(CGRect)frame container:(UIView *)containerView{
    
    //创建
    GlassView *gv = [[[NSBundle mainBundle] loadNibNamed:@"GlassView" owner:nil options:nil] firstObject];
    
    //添加
    [containerView addSubview:gv];
    
    return gv;
}

@end


//
//  ViewController.m
//  QQ聊天表情输入
//
//  Created by 许磊 on 2019/2/26.
//  Copyright © 2019年 xulei. All rights reserved.
//

#import "ViewController.h"
#import "OperationView.h"

@interface ViewController ()

/**定义一个属性变量,方便操作OperationView*/
@property (nonatomic,strong) OperationView *opView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self UIInit];
}


#pragma mark -------UIInit ---------
//界面创建
-(void)UIInit{
    
    //创建一个输入框
    self.opView = [OperationView showOperationViewWithFrame:CGRectMake(0,self.view.frame.size.height-50,self.view.frame.size.width,50) andFatherView:self.view];
    
}

#pragma mark -------touchesBegan: ---------
//触摸事件
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    [self.opView hideKeyboard];
    
}

@end

运行结果

运行结果

附一:awakeFromNib

在使用xib的时候才会涉及到此方法的使用,当.nib文件被加载的时候,会发送一个awakeFromNib的消息到.nib文件中的每个对象,每个对象都可以定义自己的awakeFromNib函数来响应这个消息,执行一些必要的操作

附二:动画

视图.layer.transform
CATransform3D---3D动画
//不带make的多次,带make一次

//平移
CATransform3D CATransform3DMakeTranslation (CGFloat tx,
    CGFloat ty, CGFloat tz)
CATransform3D CATransform3DTranslate (CATransform3D t, CGFloat tx,
    CGFloat ty, CGFloat tz)

//缩放
CATransform3D CATransform3DMakeScale (CGFloat sx, 
CGFloat sy, CGFloat sz)
CATransform3D CATransform3DScale (CATransform3D t, CGFloat sx,
CGFloat sy, CGFloat sz)

//旋转
CATransform3D CATransform3DMakeRotation (CGFloat angle, CGFloat x,
    CGFloat y, CGFloat z)
CATransform3D CATransform3DRotate (CATransform3D t, CGFloat angle,
    CGFloat x, CGFloat y, CGFloat z)
视图.transform
CGAffineTransform---2D动画
//不带make的多次,带make一次

//平移
CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx,
  CGFloat ty)
CGAffineTransform CGAffineTransformTranslate(CGAffineTransform t,
  CGFloat tx, CGFloat ty)
//缩放
CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
CGAffineTransform CGAffineTransformScale(CGAffineTransform t,
  CGFloat sx, CGFloat sy) 
//旋转
CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle)
CGAffineTransform CGAffineTransformRotate(CGAffineTransform t,
  CGFloat angle)

附三:富文本-带有属性的字符串

NSAttributedString & NSMutableAttributedString

//将图片转化为属性字符串(不可变)
+ (NSAttributedString *)attributedStringWithAttachment:
                                    (NSTextAttachment *)attachment;

//NSTextAttachment的常用属性
@property (strong, NS_NONATOMIC_IOSONLY) UIImage *image;
@property (NS_NONATOMIC_IOSONLY) CGRect bounds;

//NSMutableAttributedString初始化方法之一
- (instancetype)initWithAttributedString:(NSAttributedString *)attrStr;

//NSMutableAttributedString追加NSAttributedString
- (void)appendAttributedString:(NSAttributedString *)attrString;

//NSMutableAttributedString删除某个位置的NSAttributedString
- (void)deleteCharactersInRange:(NSRange)range;

//NSMutableAttributedString中插入一段NSAttributedString
- (void)insertAttributedString:(NSAttributedString *)attrString 
                                       atIndex:(NSUInteger)loc;

//NSMutableAttributedString中一部分被NSAttributedString替换
- (void)replaceCharactersInRange:(NSRange)range 
                     withAttributedString:(NSAttributedString *)attrString;

附四:File's Owner

示意图
xib文件与代码关联时注意File's Owner

附五:简单的开发逻辑

示意图
  • 1.自定义一个继承于UIView的OperationView封装我们对输入框的操作
    a.xib文件布局
    b.让输入框和键盘同时弹出来

  • 2.自定义一个继承于UIView的FaceView封装我们对表情键盘的操作
    a.xib文件布局(UIScrollView、UIPageControl控件,Long Press Gesture Recognizer长按手势)
    b.自定义FaceModel文件封装我们获得的表情数据
    c.将获得的表情数据显示在FaceView里面
    d.给表情添加事件-富文本,显示到输入框中
    e.给删除按钮添加事件-富文本,清楚作用

  • 3.自定义一个继承于UIView的GlassView封装我们对长按表情的操作
    a.xib文件布局
    b.添加长按事件达到放大的效果

附六:xib的使用

快速搭建界面的两种方式:1.Storyboard 2.Xib

示意图

区别:xib是独立的文件 ,而一个storyboard里面可以包含多个界面

xib的使用

示意图

为Xib添加一个界面
1.使用代码关联

- (void)loadView{
     self.view = [[[NSBundle mainBundle] 
          loadNibNamed:@"MainViewController" owner:nil options:nil] firstObject];
}

2. 使用界面关联

示意图

界面和类关联

示意图

快速搭建界面

示意图

使用xib文件创建界面

示意图

使用IBAction关联事件

示意图

使用IBOutlet关联对象

示意图

相关文章

网友评论

    本文标题:QQ聊天表情输入

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