源代码
文本链接:
链接:
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
运行结果
![](https://img.haomeiwen.com/i13294749/783d1bba29a0ec2a.gif)
附一: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
![](https://img.haomeiwen.com/i13294749/cde000cf2da160e4.png)
xib文件与代码关联时注意File's Owner
附五:简单的开发逻辑
![](https://img.haomeiwen.com/i13294749/10e9d156f58cb6a4.png)
-
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
![](https://img.haomeiwen.com/i13294749/2c5567135fc995fb.png)
区别:xib是独立的文件 ,而一个storyboard里面可以包含多个界面
xib的使用
![](https://img.haomeiwen.com/i13294749/696aa5dd759da482.png)
为Xib添加一个界面
1.使用代码关联
- (void)loadView{
self.view = [[[NSBundle mainBundle]
loadNibNamed:@"MainViewController" owner:nil options:nil] firstObject];
}
2. 使用界面关联
![](https://img.haomeiwen.com/i13294749/a132b6b75941bfb6.png)
界面和类关联
![](https://img.haomeiwen.com/i13294749/e63d5c7938990cb6.png)
快速搭建界面
![](https://img.haomeiwen.com/i13294749/71af60ceed8e1888.png)
使用xib文件创建界面
![](https://img.haomeiwen.com/i13294749/d72258becf5b1fdf.png)
使用IBAction关联事件
![](https://img.haomeiwen.com/i13294749/c94770bc4500e083.png)
使用IBOutlet关联对象
![](https://img.haomeiwen.com/i13294749/e06186c9bdcacfd9.png)
网友评论