下面的东西是编写自定义的表情键盘,话不多说,开门见山吧!下面主要用到的知识有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 //声明表情对应的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 = 30;
 frame.size.width = 30;
 self = [super initWithFrame:frame];
 if (self) {
 self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
 [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

2、FunctionView组件的封装,FunctionView就是使用FaceView组件和ScrollView组件把表情加载进来,在实例化FunctionView组件时,我们用到了自动布局来设置ScrollView和下面的Button.

FunctionView.h的代码如下,在.h中留有组件的接口和回调用的Block, plistFileName用于加载我们的资源文件时使用。

// FunctionView.h
// MyKeyBoard
//
// Created by 青玉伏案 on 14-9-16.
// Copyright (c) 2014年 Mrli. All rights reserved.
//
 
#import //定义对应的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:0 metrics:0 views:NSDictionaryOfVariableBindings(faceButton,moreButton)];
 [self addConstraints:buttonH];
 
 //垂直约束
 NSArray *button1V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[faceButton(44)]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(faceButton)];
 [self addConstraints:button1V];
 
 NSArray *button2V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[moreButton(44)]|" options:0 metrics:0 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 == 0) {
 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 = 0; i < imageData.count; i   ) {
 tempData = imageData[i];
 
 //解析数据,转换成函数headers要用的数据格式
 NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:2];
 [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:0 metrics:0 views:NSDictionaryOfVariableBindings(_headerScrollView)];
 [self addConstraints:scrollH];
 
 //垂直约束
 NSArray *scrolV = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[_headerScrollView]-50-|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(_headerScrollView)];
 [self addConstraints:scrolV];
 
 
 
 CGFloat scrollHeight = (self.frame).size.height-60;
 
 //根据图片量来计算scrollView的Contain的宽度
 CGFloat width = (headers.count/(scrollHeight/30))*30;
 self.headerScrollView.contentSize = CGSizeMake(width, scrollHeight);
 self.headerScrollView.pagingEnabled = YES;
 
 
 //图片坐标
 CGFloat x = 0;
 CGFloat y = 0;
 
 //往scroll上贴图片
 for (int i = 0; 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/30)) * 30;
 x = (i/(int)(scrollHeight/30)) * 30;
 
 FaceView *face = [[FaceView alloc] initWithFrame:CGRectMake(x, y, 0, 0)];
 [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 //定义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:0 metrics:0 views:NSDictionaryOfVariableBindings(faceButton,moreButton)];
 [self addConstraints:buttonH];
 
 //添加垂直约束
 NSArray *button1V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[faceButton]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(faceButton)];
 [self addConstraints:button1V];
 
 NSArray *button2V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[moreButton]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(moreButton)];
 [self addConstraints:button2V];
 
 }
 return self;
}
 
//接受传入的回调
-(void) setToolIndex:(ToolIndex)toolBlock
{
 self.myBlock = toolBlock;
}
 
//点击表情按钮要回调的方法
-(void) tapFaceButton: (id) sender
{
 self.myBlock(1);
}
 
//点击more要回调的方法
-(void) tapMoreButton: (id) sender
{
 self.myBlock(2);
}
 
@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 #import #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 == 0)
 {
 myImage = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([HistoryImage class]) inManagedObjectContext:self.manager];
 myImage.imageText = imageText;
 myImage.headerImage = image;
 myImage.time = [NSDate date];
 }
 else
 {
 myImage = result[0];
 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(0, 0, 320, 216)];
 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(0, 0, 0, 0)];
 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:0 metrics:0 views:NSDictionaryOfVariableBindings(_toolView)];
 [self.view addConstraints:toolHConstraint];
 
 //垂直约束
 NSArray *toolVConstraint = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[_toolView(44)]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(_toolView)];
 [self.view addConstraints:toolVConstraint];
 
 
 
 
 //回调toolView中的方法
 [self.toolView setToolIndex:^(NSInteger index)
 {
 NSLog(@"%d", index);
 
 switch (index) {
 case 1:
 [copy_self changeKeyboardToFunction];
 break;
  
 case 2:
 [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(0, 0, 320, 30)];
 
 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 = 216;
 self.functionView.frame = frame;
 self.moreView.frame = frame;
 
 }
 //横屏
 if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
 CGRect frame = self.functionView.frame;
 frame.size.height = 150;
 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的坐标系不同,得做 一个转换。发表文章的目的是想起到抛砖引玉的左右,有好的东西希望大家相互交流一下。

iOS组件封装与自动布局自定义表情键盘的更多相关文章

  1. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  2. iOS实现拖拽View跟随手指浮动效果

    这篇文章主要为大家详细介绍了iOS实现拖拽View跟随手指浮动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  4. ios – Testflight无法安装应用程序

    我有几个测试人员注册了testflight并连接了他们的设备……他们有不同的ios型号……但是所有这些都有同样的问题.当他们从“safari”或“testflight”应用程序本身单击应用程序的安装按钮时……达到约90%并出现错误消息…

  5. ibm-mobilefirst – 在iOS 7.1上获取“无法安装应用程序,因为证书无效”错误

    当我的客户端将他们的设备更新到iOS7.1,然后尝试从AppCenter更新我们的应用程序时,我收到了上述错误.经过一番搜索,我找到了一个类似问题的帖子here.但是后来因为我在客户端使用AppCenter更新应用程序的环境中,我无法使用USB插件并为他们安装应用程序.在发布支持之前,是否有通过AppCenter进行下载的解决方法?

  6. ios – 视图的简单拖放?

    我正在学习iOS,但我找不到如何向UIView添加拖放行为.我试过了:它说“UIView没有可见的接口声明选择器addTarget”此外,我尝试添加平移手势识别器,但不确定这是否是我需要的它被称为,但不知道如何获得事件的坐标.在iOS中注册移动事件回调/拖放操作的标准简单方法是什么?

  7. ios – 什么控制iTunes中iPhone应用程序支持的语言列表?

    什么控制iPhone应用程序的iTunes页面中支持的语言?

  8. ios – 获得APNs响应BadDeviceToken或Unregistered的可能原因是什么?

    我知道设备令牌在某些时候是有效的.用户如何使其设备令牌变坏?从关于“未注册”的文档:Thedevicetokenisinactiveforthespecifiedtopic.这是否意味着应用程序已被删除?.您应该看到四种分发方法:如果您选择AppStore或Enterprise,您将在后面的对话框中看到Xcode将APNS权利更改为生产:如果选择AdHoc或Development,则aps-environment下的文本将是开发,然后应与后端的配置匹配.

  9. ios – 当我关闭应用程序时,我从调试器获得消息:由于信号15而终止

    我怎么能解决这个问题,我不知道这个链接MypreviousproblemaboutCoredata对我的问题有影响吗?当我cmd应用程序的Q时,将出现此消息.Messagefromdebugger:Terminatedduetosignal15如果谁知道我以前的问题的解决方案,请告诉我.解决方法>来自调试器的消息:每当用户通过CMD-Q(退出)或STOP手动终止应用程序(无论是在iOS模拟器中还是

  10. ios – NSUbiquityIdentityDidChangeNotification和SIGKILL

    当应用程序被发送到后台时,我们会删除观察者吗?我遇到的问题是,当UbiquityToken发生变化时,应用程序终止,因为用户已经更改了iCloud设置.你们如何设法订阅这个通知,如果你不这样做,你会做什么来跟踪当前登录的iCloud用户?

随机推荐

  1. iOS实现拖拽View跟随手指浮动效果

    这篇文章主要为大家详细介绍了iOS实现拖拽View跟随手指浮动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. iOS – genstrings:无法连接到输出目录en.lproj

    使用我桌面上的项目文件夹,我启动终端输入:cd然后将我的项目文件夹拖到终端,它给了我路径.然后我将这行代码粘贴到终端中找.-name*.m|xargsgenstrings-oen.lproj我在终端中收到此错误消息:genstrings:无法连接到输出目录en.lproj它多次打印这行,然后说我的项目是一个目录的路径?没有.strings文件.对我做错了什么的想法?

  3. iOS 7 UIButtonBarItem图像没有色调

    如何确保按钮图标采用全局色调?解决方法只是想将其转换为根注释,以便为“回答”复选标记提供更好的上下文,并提供更好的格式.我能想出这个!

  4. ios – 在自定义相机层的AVFoundation中自动对焦和自动曝光

    为AVFoundation定制图层相机创建精确的自动对焦和曝光的最佳方法是什么?

  5. ios – Xcode找不到Alamofire,错误:没有这样的模块’Alamofire’

    我正在尝试按照github(https://github.com/Alamofire/Alamofire#cocoapods)指令将Alamofire包含在我的Swift项目中.我创建了一个新项目,导航到项目目录并运行此命令sudogeminstallcocoapods.然后我面临以下错误:搜索后我设法通过运行此命令安装cocoapodssudogeminstall-n/usr/local/bin

  6. ios – 在没有iPhone6s或更新的情况下测试ARKit

    我在决定下载Xcode9之前.我想玩新的框架–ARKit.我知道要用ARKit运行app我需要一个带有A9芯片或更新版本的设备.不幸的是我有一个较旧的.我的问题是已经下载了新Xcode的人.在我的情况下有可能运行ARKit应用程序吗?那个或其他任何模拟器?任何想法或我将不得不购买新设备?解决方法任何iOS11设备都可以使用ARKit,但是具有高质量AR体验的全球跟踪功能需要使用A9或更高版本处理器的设备.使用iOS11测试版更新您的设备是必要的.

  7. 将iOS应用移植到Android

    我们制作了一个具有2000个目标c类的退出大型iOS应用程序.我想知道有一个最佳实践指南将其移植到Android?此外,由于我们的应用程序大量使用UINavigation和UIView控制器,我想知道在Android上有类似的模型和实现.谢谢到目前为止,guenter解决方法老实说,我认为你正在计划的只是制作难以维护的糟糕代码.我意识到这听起来像很多工作,但从长远来看它会更容易,我只是将应用程序的概念“移植”到android并从头开始编写.

  8. ios – 在Swift中覆盖Objective C类方法

    我是Swift的初学者,我正在尝试在Swift项目中使用JSONModel.我想从JSONModel覆盖方法keyMapper,但我没有找到如何覆盖模型类中的Objective-C类方法.该方法的签名是:我怎样才能做到这一点?解决方法您可以像覆盖实例方法一样执行此操作,但使用class关键字除外:

  9. ios – 在WKWebView中获取链接URL

    我想在WKWebView中获取tapped链接的url.链接采用自定义格式,可触发应用中的某些操作.例如HTTP://我的网站/帮助#深层链接对讲.我这样使用KVO:这在第一次点击链接时效果很好.但是,如果我连续两次点击相同的链接,它将不报告链接点击.是否有解决方法来解决这个问题,以便我可以检测每个点击并获取链接?任何关于这个的指针都会很棒!解决方法像这样更改addobserver在observeValue函数中,您可以获得两个值

  10. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

返回
顶部