iOS开发之自定义表情键盘(组件封装与自动布局)
下面的东西是编写自定义的表情键盘,话不多说,开门见山吧!下面主要用到的知识有MVC, iOS开发中的自动布局,自定义组件的封装与使用,Block回调,CoreData的使用。有的小伙伴可能会问写一个自定义表情键盘肿么这么麻烦?下面将会介绍我们如何用上面提到的东西来定义我们的表情键盘。下面的内容会比较多,这篇博文还是比较有料的。
还是那句话写技术博客是少不了代码的,下面会结合代码来回顾一下iOS的知识,本篇博文中用到的知识点在前面的博客中都能找到相应的内容,本篇算是一个小小的功能整合。先来张图看一下本app的目录结构。我是根据自己对MVC的理解来构建的目录结构,希望起到抛砖引玉的作用,有好的解决方案欢迎评论或者留言指出。Face文件中存放的时我们的表情图片,Model文件封装的是从sqlite中读取历史头像的组件,View文件中封装的时我们自定义的组件,也就是自定义键盘相关的视图,Controller负责将我们的各个组件组装到一起完成我们想要的功能。下面会一一介绍。
上面是文件的组织结构,下面为了更为直观的了解我们想要的效果,下面先看几张截图,来直观的感受一下运行效果,上面是竖屏的显示效果,下面是横屏的显示效果。因为在封装自定义键盘中用到了自动布局所以横屏显示或者在更大的屏幕上显示是没问题的,常用表情是用户用过的表情,然后存在Sqlite中,显示时并按时间降序排列。more是用来扩展功能用的接口。话不多说,来的代码才是实在的。
一.View(自定义视图)
View文件夹下存放的时我们自定义的视图组件,因为是自定义的组件所以storyboard我们就用不了啦,所有的代码都必须手写,这样才能保证组件使用的灵活性和减少各个组件之间的耦合性,更利于团队之间的合作。在封装组件时要预留好外界可能使用到的接口,和返回该返回的数据。好啦,废话少说,来点干货吧!
1、FaceView组件的封装:FaceView即负责显示一个个的头像。在使用该组件时要传入要显示的图片和图片对应的文字(如【哈哈】),当点击图片的时候,会通过block回调的形式把该图片的image以及图片文字返回到使用的组件中去,下面是关键代码:
FaceView.h中的代码如下(下面代码是定义啦相应的Block类型和对外的接口):
#import <UIKit/UIKit.h> //声明表情对应的block,用于把点击的表情的图片和图片信息传到上层视图
typedef void (^FaceBlock) (UIImage *image, NSString *imageText); @interface FaceView : UIView //图片对应的文字
@property (nonatomic, strong) NSString *imageText;
//表情图片
@property (nonatomic, strong) UIImage *headerImage; //设置block回调
-(void)setFaceBlock:(FaceBlock)block; //设置图片,文字
-(void)setImage:(UIImage *) image ImageText:(NSString *) text; @end
FaceView.m中的代码如下
//
// FaceView.m
// MyKeyBoard
//
// Created by 青玉伏案 on 14-9-16.
// Copyright (c) 2014年 Mrli. All rights reserved.
// #import "FaceView.h" @interface FaceView ()
@property(strong, nonatomic) FaceBlock block;
@property (strong, nonatomic) UIImageView *imageView;
@end @implementation FaceView //初始化图片
- (id)initWithFrame:(CGRect)frame
{
//face的大小
frame.size.height = ;
frame.size.width = ;
self = [super initWithFrame:frame];
if (self) {
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(, , , )];
[self addSubview:self.imageView];
}
return self;
} -(void) setFaceBlock:(FaceBlock)block
{
self.block = block;
} -(void) setImage:(UIImage *)image ImageText:(NSString *)text
{
//显示图片
[self.imageView setImage:image]; //把图片存储起来
self.headerImage = image; self.imageText = text;
} //点击时回调
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
//判断触摸的结束点是否在图片中
if (CGRectContainsPoint(self.bounds, point))
{
//回调,把该头像的信息传到相应的controller中
self.block(self.headerImage, self.imageText);
}
} @end
代码说明:
主要就是block回调的使用,就是封装了一个自定义的button,具体内容请参考之前的博客“IOS开发之自定义Button(集成三种回调模式)”
2、FunctionView组件的封装,FunctionView就是使用FaceView组件和ScrollView组件把表情加载进来,在实例化FunctionView组件时,我们用到了自动布局来设置ScrollView和下面的Button
FunctionView.h的代码如下,在.h中留有组件的接口和回调用的Block, plistFileName用于加载我们的资源文件时使用,至于如何使用plist文件,请参考之前的博客:IOS开发之显示微博表情
//
// FunctionView.h
// MyKeyBoard
//
// Created by 青玉伏案 on 14-9-16.
// Copyright (c) 2014年 Mrli. All rights reserved.
// #import <UIKit/UIKit.h> //定义对应的block类型,用于数据的交互
typedef void (^FunctionBlock) (UIImage *image, NSString *imageText); @interface FunctionView : UIView
//资源文件名
@property (nonatomic, strong) NSString *plistFileName;
//接受block块
-(void)setFunctionBlock:(FunctionBlock) block; @end
FunctionView.m中的代码如下,常用表情是在sqlite中获取的,而全部表情是通过plist文件的信息在Face文件中加载的:
//
// FunctionView.m
// MyKeyBoard
//
// Created by 青玉伏案 on 14-9-16.
// Copyright (c) 2014年 Mrli. All rights reserved.
// #import "FunctionView.h"
#import "FaceView.h"
#import "ImageModelClass.h"
#import "HistoryImage.h" @interface FunctionView() @property (strong, nonatomic) FunctionBlock block;
//暂存表情组件回调的表情和表情文字
@property (strong, nonatomic) UIImage *headerImage;
@property (strong, nonatomic) NSString *imageText; //display我们的表情图片
@property (strong, nonatomic) UIScrollView *headerScrollView; //定义数据模型用于获取历史表情
@property (strong, nonatomic) ImageModelClass *imageModel; @end @implementation FunctionView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) { //实例化数据模型
self.imageModel =[[ImageModelClass alloc] init]; //实例化下面的button
UIButton *faceButton = [[UIButton alloc] initWithFrame:CGRectZero];
faceButton.backgroundColor = [UIColor grayColor]; [faceButton setTitle:@"全部表情" forState:UIControlStateNormal];
[faceButton setShowsTouchWhenHighlighted:YES];
[faceButton addTarget:self action:@selector(tapButton1:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:faceButton]; //实例化常用表情按钮
UIButton *moreButton = [[UIButton alloc] initWithFrame:CGRectZero];
moreButton.backgroundColor = [UIColor orangeColor];
[moreButton setTitle:@"常用表情" forState:UIControlStateNormal];
[moreButton setShowsTouchWhenHighlighted:YES];
[moreButton addTarget:self action:@selector(tapButton2:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:moreButton]; //给按钮添加约束
faceButton.translatesAutoresizingMaskIntoConstraints = NO;
moreButton.translatesAutoresizingMaskIntoConstraints = NO;
//水平约束
NSArray *buttonH = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[faceButton][moreButton(==faceButton)]|" options: metrics: views:NSDictionaryOfVariableBindings(faceButton,moreButton)];
[self addConstraints:buttonH]; //垂直约束
NSArray *button1V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[faceButton(44)]|" options: metrics: views:NSDictionaryOfVariableBindings(faceButton)];
[self addConstraints:button1V]; NSArray *button2V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[moreButton(44)]|" options: metrics: views:NSDictionaryOfVariableBindings(moreButton)];
[self addConstraints:button2V]; //默认显示表情图片
[self tapButton1:nil]; }
return self;
} //接受回调
-(void)setFunctionBlock:(FunctionBlock)block
{
self.block = block;
} //点击全部表情按钮回调方法
-(void)tapButton1: (id) sender
{
// 从plist文件载入资源
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:self.plistFileName ofType:@"plist"];
NSArray *headers = [NSArray arrayWithContentsOfFile:path]; if (headers.count == ) {
NSLog(@"访问的plist文件不存在");
}
else
{
//调用headers方法显示表情
[self header:headers];
}
} //点击历史表情的回调方法
-(void) tapButton2: (id) sender
{
//从数据库中查询所有的图片
NSArray *imageData = [self.imageModel queryAll];
//解析请求到的数据
NSMutableArray *headers = [NSMutableArray arrayWithCapacity:imageData.count]; //数据实体,相当于javaBean的东西
HistoryImage *tempData; for (int i = ; i < imageData.count; i ++) {
tempData = imageData[i]; //解析数据,转换成函数headers要用的数据格式
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:];
[dic setObject:tempData.imageText forKey:@"chs"];
UIImage *image = [UIImage imageWithData:tempData.headerImage];
[dic setObject:image forKey:@"png"]; [headers addObject:dic];
} [self header:headers]; } //负责把查出来的图片显示
-(void) header:(NSArray *)headers
{
[self.headerScrollView removeFromSuperview];
self.headerScrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
[self addSubview:self.headerScrollView]; //给scrollView添加约束
self.headerScrollView.translatesAutoresizingMaskIntoConstraints = NO;
//水平约束
NSArray *scrollH = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[_headerScrollView]-10-|" options: metrics: views:NSDictionaryOfVariableBindings(_headerScrollView)];
[self addConstraints:scrollH]; //垂直约束
NSArray *scrolV = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[_headerScrollView]-50-|" options: metrics: views:NSDictionaryOfVariableBindings(_headerScrollView)];
[self addConstraints:scrolV]; CGFloat scrollHeight = (self.frame).size.height-; //根据图片量来计算scrollView的Contain的宽度
CGFloat width = (headers.count/(scrollHeight/))*;
self.headerScrollView.contentSize = CGSizeMake(width, scrollHeight);
self.headerScrollView.pagingEnabled = YES; //图片坐标
CGFloat x = ;
CGFloat y = ; //往scroll上贴图片
for (int i = ; i < headers.count; i ++) {
//获取图片信息
UIImage *image;
if ([headers[i][@"png"] isKindOfClass:[NSString class]])
{
image = [UIImage imageNamed:headers[i][@"png"]];
}
else
{
image = headers[i][@"png"];
} NSString *imageText = headers[i][@"chs"]; //计算图片位置
y = (i%(int)(scrollHeight/)) * ;
x = (i/(int)(scrollHeight/)) * ; FaceView *face = [[FaceView alloc] initWithFrame:CGRectMake(x, y, , )];
[face setImage:image ImageText:imageText]; //face的回调,当face点击时获取face的图片
__weak __block FunctionView *copy_self = self;
[face setFaceBlock:^(UIImage *image, NSString *imageText)
{
copy_self.block(image, imageText);
}]; [self.headerScrollView addSubview:face];
} [self.headerScrollView setNeedsDisplay]; } @end
代码说明:
1、主要是通过对资源文件或者对从数据库中查询的资源进行遍历然后添加到ScrollView中
2.为了适应不同的屏幕给相应的组件添加了约束
3.ToolView组件的封装: ToolView就是在主屏幕上下面的类似于TabBar的东西,当键盘出来的时候,ToolView会运动到键盘上面的位置。为了使用不同的屏幕,也需要用自动布局来实现。
ToolView.h的代码如下:预留组件接口和声明block类型
//
// ToolView.h
// MyKeyBoard
//
// Created by 青玉伏案 on 14-9-16.
// Copyright (c) 2014年 Mrli. All rights reserved.
// /*****************
封装下面的工具条组件
*****************/
#import <UIKit/UIKit.h> //定义block块变量类型,用于回调,把本View上的按钮的index传到Controller中
typedef void (^ToolIndex) (NSInteger index); @interface ToolView : UIView //块变量类型的setter方法
-(void)setToolIndex:(ToolIndex) toolBlock; @end
ToolView.m的代码实现:
//
// ToolView.m
// MyKeyBoard
//
// Created by 青玉伏案 on 14-9-16.
// Copyright (c) 2014年 Mrli. All rights reserved.
// #import "ToolView.h" @interface ToolView () //定义ToolIndex类型的block,用于接受外界传过来的block
@property (nonatomic, strong) ToolIndex myBlock; @end @implementation ToolView - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) { //1初始化表情按钮
UIButton *faceButton = [[UIButton alloc] initWithFrame:CGRectZero];
faceButton.backgroundColor = [UIColor orangeColor];
[faceButton setTitle:@"表情" forState:UIControlStateNormal];
[faceButton setShowsTouchWhenHighlighted:YES];
[faceButton addTarget:self action:@selector(tapFaceButton:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:faceButton]; //初始化更多按钮
UIButton *moreButton = [[UIButton alloc] initWithFrame:CGRectZero];
moreButton.backgroundColor = [UIColor grayColor];
[moreButton setTitle:@"More" forState:UIControlStateNormal];
[moreButton setShowsTouchWhenHighlighted:YES];
[moreButton addTarget:self action:@selector(tapMoreButton:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:moreButton]; //给我们的按钮添加约束来让按钮来占满toolView;
faceButton.translatesAutoresizingMaskIntoConstraints = NO;
moreButton.translatesAutoresizingMaskIntoConstraints = NO; //添加水平约束
NSArray *buttonH = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[faceButton][moreButton(==faceButton)]|" options: metrics: views:NSDictionaryOfVariableBindings(faceButton,moreButton)];
[self addConstraints:buttonH]; //添加垂直约束
NSArray *button1V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[faceButton]|" options: metrics: views:NSDictionaryOfVariableBindings(faceButton)];
[self addConstraints:button1V]; NSArray *button2V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[moreButton]|" options: metrics: views:NSDictionaryOfVariableBindings(moreButton)];
[self addConstraints:button2V]; }
return self;
} //接受传入的回调
-(void) setToolIndex:(ToolIndex)toolBlock
{
self.myBlock = toolBlock;
} //点击表情按钮要回调的方法
-(void) tapFaceButton: (id) sender
{
self.myBlock();
} //点击more要回调的方法
-(void) tapMoreButton: (id) sender
{
self.myBlock();
} @end
代码说明:
主要是对block回调的应用和给相应的组件添加相应的约束
4.MoreView组件的封装代码就不往上贴啦,和上面的类似,下面是调用MoreView组件的运行效果,有兴趣的读者请自行编写,以上就是视图部分的代码了
二. Mode部分的内容:
1.先定义我们要使用的数据模型,数据模型如下,time是使用表情的时间,用于排序。
2.下面编写我们的ImageModelClass类,里面封装了我们操作数据要用的方法
ImageModelClass.h的代码如下,主要是预留的对外的接口:
//
// ImageModelClass.h
// MyKeyBoard
//
// Created by 青玉伏案 on 14-9-16.
// Copyright (c) 2014年 Mrli. All rights reserved.
// #import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "HistoryImage.h" @interface ImageModelClass : NSObject
//保存数据
-(void)save:(NSData *) image ImageText:(NSString *) imageText;
//查询所有的图片
-(NSArray *) queryAll;
@end
ImageModelClass.m的代码如下,主要是用CoreData对sqlite的操作:
//
// ImageModelClass.m
// MyKeyBoard
//
// Created by 青玉伏案 on 14-9-16.
// Copyright (c) 2014年 Mrli. All rights reserved.
// #import "ImageModelClass.h" @interface ImageModelClass () @property (nonatomic, strong) NSManagedObjectContext *manager; @end @implementation ImageModelClass
- (instancetype)init
{
self = [super init];
if (self) {
//通过上下文获取manager
UIApplication *application = [UIApplication sharedApplication];
id delegate = application.delegate;
self.manager = [delegate managedObjectContext];
}
return self;
} -(void)save:(NSData *)image ImageText:(NSString *)imageText
{
if (image != nil) {
NSArray *result = [self search:imageText]; HistoryImage *myImage; if (result.count == )
{
myImage = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([HistoryImage class]) inManagedObjectContext:self.manager];
myImage.imageText = imageText;
myImage.headerImage = image;
myImage.time = [NSDate date];
}
else
{
myImage = result[];
myImage.time = [NSDate date];
} //存储实体
NSError *error = nil;
if (![self.manager save:&error]) {
NSLog(@"保存出错%@", [error localizedDescription]);
} } } //查找
-(NSArray *)search:(NSString *) image
{
NSArray *result; //新建查询条件
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([HistoryImage class])]; //添加谓词
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"imageText=%@",image]; //把谓词给request
[fetchRequest setPredicate:predicate]; //执行查询
NSError *error = nil;
result = [self.manager executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(@"查询错误:%@", [error localizedDescription]);
}
return result;
} //查询所有的
-(NSArray *) queryAll
{
//新建查询条件
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([HistoryImage class])]; //添加排序规则
//定义排序规则
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"time" ascending:NO]; //添加排序规则
[fetchRequest setSortDescriptors:@[sortDescriptor]]; //执行查询
NSError *error = nil;
NSArray *result = [self.manager executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(@"查询错误:%@", [error localizedDescription]);
} return result;
} @end
代码说明:
1.保存图片时先查找图片是否存在,如果存在则更新时间,如果不存在则插入数据(写到这感觉想在用Hibernate写东西)。
三.Controller部分,把上面的组件进行组装
1.MainViewController.m中的延展部分的代码如下:
@interface MainViewController () //自定义组件
@property (nonatomic, strong) ToolView *toolView; @property (nonatomic, strong) FunctionView *functionView; @property (nonatomic, strong) MoreView *moreView; //系统组件
@property (strong, nonatomic) IBOutlet UITextView *myTextView; @property (strong, nonatomic) NSDictionary *keyBoardDic; @property (strong, nonatomic) IBOutlet UIImageView *imageView; @property (strong, nonatomic) NSString *sendString; //数据model
@property (strong, nonatomic) ImageModelClass *imageMode; @property (strong, nonatomic)HistoryImage *tempImage; @end
2.在viewDidLoad中进行组件的初始化和实现组件的Block回调,代码如下
- (void)viewDidLoad
{
[super viewDidLoad]; //从sqlite中读取数据
self.imageMode = [[ImageModelClass alloc] init]; //实例化FunctionView
self.functionView = [[FunctionView alloc] initWithFrame:CGRectMake(, , , )];
self.functionView.backgroundColor = [UIColor blackColor]; //设置资源加载的文件名
self.functionView.plistFileName = @"emoticons"; __weak __block MainViewController *copy_self = self;
//获取图片并显示
[self.functionView setFunctionBlock:^(UIImage *image, NSString *imageText)
{
NSString *str = [NSString stringWithFormat:@"%@%@",copy_self.myTextView.text, imageText]; copy_self.myTextView.text = str;
copy_self.imageView.image = image; //把使用过的图片存入sqlite
NSData *imageData = UIImagePNGRepresentation(image);
[copy_self.imageMode save:imageData ImageText:imageText];
}]; //实例化MoreView
self.moreView = [[MoreView alloc] initWithFrame:CGRectMake(, , , )];
self.moreView.backgroundColor = [UIColor blackColor];
[self.moreView setMoreBlock:^(NSInteger index) {
NSLog(@"MoreIndex = %d",index);
}]; //进行ToolView的实例化
self.toolView = [[ToolView alloc] initWithFrame:CGRectZero];
self.toolView.backgroundColor = [UIColor blackColor];
[self.view addSubview:self.toolView]; //给ToolView添加约束
//开启自动布局
self.toolView.translatesAutoresizingMaskIntoConstraints = NO; //水平约束
NSArray *toolHConstraint = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_toolView]|" options: metrics: views:NSDictionaryOfVariableBindings(_toolView)];
[self.view addConstraints:toolHConstraint]; //垂直约束
NSArray *toolVConstraint = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[_toolView(44)]|" options: metrics: views:NSDictionaryOfVariableBindings(_toolView)];
[self.view addConstraints:toolVConstraint]; //回调toolView中的方法
[self.toolView setToolIndex:^(NSInteger index)
{
NSLog(@"%d", index); switch (index) {
case :
[copy_self changeKeyboardToFunction];
break; case :
[copy_self changeKeyboardToMore];
break; default:
break;
} }]; //当键盘出来的时候通过通知来获取键盘的信息
//注册为键盘的监听着
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(keyNotification:) name:UIKeyboardWillChangeFrameNotification object:nil]; //给键盘添加dan
//TextView的键盘定制回收按钮
UIToolbar * toolBar = [[UIToolbar alloc]initWithFrame:CGRectMake(, , , )]; UIBarButtonItem * item1 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(tapDone:)];
UIBarButtonItem * item2 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem * item3 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
toolBar.items = @[item2,item1,item3]; self.myTextView.inputAccessoryView =toolBar; }
3.当横竖屏幕切换时设置自定义键盘的高度
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
//纵屏
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
CGRect frame = self.functionView.frame;
frame.size.height = ;
self.functionView.frame = frame;
self.moreView.frame = frame; }
//横屏
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
CGRect frame = self.functionView.frame;
frame.size.height = ;
self.functionView.frame = frame;
self.moreView.frame = frame;
}
}
4.当键盘出来的时候,改变toolView的位置,通过键盘的通知来实现。当横屏的时候键盘的坐标系和我们当前的Frame的坐标系不一样所以当横屏时得做一坐标系的转换,代码如下;
//当键盘出来的时候改变toolView的位置(接到键盘出来的通知要做的方法)
-(void) keyNotification : (NSNotification *) notification
{
NSLog(@"%@", notification.userInfo); self.keyBoardDic = notification.userInfo;
//获取键盘移动后的坐标点的坐标点
CGRect rect = [self.keyBoardDic[@"UIKeyboardFrameEndUserInfoKey"] CGRectValue]; //把键盘的坐标系改成当前我们window的坐标系
CGRect r1 = [self.view convertRect:rect fromView:self.view.window]; [UIView animateWithDuration:[self.keyBoardDic[UIKeyboardAnimationDurationUserInfoKey] floatValue] animations:^{ //动画曲线
[UIView setAnimationCurve:[self.keyBoardDic[UIKeyboardAnimationCurveUserInfoKey] doubleValue]]; CGRect frame = self.toolView.frame; frame.origin.y = r1.origin.y - frame.size.height; //根据键盘的高度来改变toolView的高度
self.toolView.frame = frame;
}];
}
5.系统键盘和自定义键盘切换的代码如下:
//切换键盘的方法
-(void) changeKeyboardToFunction
{
if ([self.myTextView.inputView isEqual:self.functionView])
{
self.myTextView.inputView = nil;
[self.myTextView reloadInputViews];
}
else
{
self.myTextView.inputView = self.functionView;
[self.myTextView reloadInputViews];
} if (![self.myTextView isFirstResponder])
{
[self.myTextView becomeFirstResponder];
}
}
以上就是上面展示效果的核心代码了,在做的时候感觉难点在于如何进行屏幕适配,尤其是当屏幕横过来的时候键盘的坐标系和我们frame的坐标系不同,得做一个转换。发表博客的目的是想起到抛砖引玉的作用,有好的东西希望大家相互交流一下。笔者水平有限难免有偏颇之处,欢迎批评指正。
Demo地址:https://github.com/lizelu/CustomeFaceKeyBoard
iOS开发之自定义表情键盘(组件封装与自动布局)的更多相关文章
- iOS swift 关于自定义表情键盘
目录 输入框 键盘监听 键盘切换 表情装载 表情加载 表情输入 表情输出 表情显示 结束语 demo下载 demo图片: 输入框 为了让输入框能够随着用户输入内容变化自动变化高度,这里的输入框使用UI ...
- iOS开发之微信聊天工具栏的封装
之前山寨了一个新浪微博(iOS开发之山寨版新浪微博小结),这几天就山寨个微信吧.之前已经把微信的视图结构简单的拖了一下(IOS开发之微信山寨版),今天就开始给微信加上具体的实现功能,那么就先从微信的聊 ...
- iOS开发UI篇—核心动画(UIView封装动画)
iOS开发UI篇—核心动画(UIView封装动画) 一.UIView动画(首尾) 1.简单说明 UIKit直接将动画集成到UIView类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画 ...
- 【好程序员笔记分享】——iOS开发之纯代码键盘退出
-iOS培训,iOS学习-------型技术博客.期待与您交流!------------ iOS开发之纯代码键盘退出(非常简单) iOS开发之纯代码键盘退出 前面说到了好几次关于键盘退出的,但 ...
- 详解iOS开发之自定义View
iOS开发之自定义View是本文要将介绍的内容,iOS SDK中的View是UIView,我们可以很方便的自定义一个View.创建一个 Window-based Application程序,在其中添加 ...
- ios开发之--仿(微信)自定义表情键盘
先附上demo:https://github.com/hgl753951/CusEmoji.git 效果图如下:
- IOS开发之自定义键盘
本文转载至 http://blog.csdn.net/majiakun1/article/details/41242069 实际开发过程中,会有自定义键盘的需求,比如,需要添加一个表情键盘.本文提供 ...
- Android开发案例 - 自定义虚拟键盘
所有包含IM功能的App(如微信, 微博, QQ, 支付宝等)都提供了Emoji表情之类的虚拟键盘, 如下图: 本文只着重介绍如何实现输入法键盘和自定义虚拟键盘的流畅切换, 而不介绍如何实现虚 ...
- 【Swift】IOS开发中自定义转场动画
在IOS开发中,我们model另外一个控制器的时候,一般都使用默认的转场动画. 其实我们可以自定义一些转场动画.达到不同的转场效果. 步骤如下:(photoBrowser是目标控制器) 1.在源控制器 ...
随机推荐
- C# winfrom 窗体的StartPosition 属性
StartPosition属性有如下选项,分别含义如下: CenterParent 窗体在其父窗体中居中. CenterScreen 窗体在当前显示窗口中居中,其尺寸在窗体大小中指定. Manual ...
- vue 学习笔记
使用vue框架做了一个项目,在这个过程中,摸索学习了vue,把过程学习到技术整理放在这里,供以后查看.
- java分享第十七天-02(封装操作excel类)
java解析EXCEL用的是POI的JAR包,兼容EXCEL2003及2007+版本的EXCEL所需要的JAR包:poi-3.8.jarpoi-ooxml.jarpoi-ooxml-schemas. ...
- Python之路Day18-正则及Django
一. 正则表达式 1.定义正则表达式 /.../ 用于定义正则表达式 /.../g 表示全局匹配 /.../i 表示不区分大小写 /.../m 表示多行匹配JS正则匹配时本身就是支持多行,此处多行匹 ...
- *HDU1847 博弈
Good Luck in CET-4 Everybody! Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K ...
- cookies,sessionStorage 和 localStorage区别
cookies,sessionStorage 和 localStorage 的区别 cookie是网站为了标示用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密). cook ...
- linux安装VMware-tools,
系统中可能预装了open-vm-tools和VMware-tools冲突,所以需要先将前者卸载在进行安装不同系统卸载使用的命令不一样,centos的命令可以使用rpm,ubuntu的命令可以使用dpk ...
- DevExpress控件的GridControl控件小结
DevExpress控件的GridControl控件小结 (由于开始使用DevExpress控件了,所以要点滴的记录一下) 1.DevExpress控件组中的GridControl控件不能使横向滚动条 ...
- DES原理
1.DES的描述 为了建立适用于计算机系统的商用密码,美国商业部的国家标准局NBS于1973年5月和1974年8月两次发布通告,向社会征求密码算法.在征得的算法中,由IBM公司提出的算法lucifer ...
- Jquery 仿 android Toast效果
JS代码如下: /** * 模仿android里面的Toast效果,主要是用于在不打断程序正常执行的情况下显示提示数据 * @param config * @return */var Toast = ...