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类型和对外的接口):

 1 #import <UIKit/UIKit.h>
 2
 3 //声明表情对应的block,用于把点击的表情的图片和图片信息传到上层视图
 4 typedef void (^FaceBlock) (UIImage *image, NSString *imageText);
 5
 6 @interface FaceView : UIView
 7
 8 //图片对应的文字
 9 @property (nonatomic, strong) NSString *imageText;
10 //表情图片
11 @property (nonatomic, strong) UIImage *headerImage;
12
13 //设置block回调
14 -(void)setFaceBlock:(FaceBlock)block;
15
16 //设置图片,文字
17 -(void)setImage:(UIImage *) image ImageText:(NSString *) text;
18
19 @end

      FaceView.m中的代码如下

 1 //
 2 //  FaceView.m
 3 //  MyKeyBoard
 4 //
 5 //  Created by 青玉伏案 on 14-9-16.
 6 //  Copyright (c) 2014年 Mrli. All rights reserved.
 7 //
 8
 9 #import "FaceView.h"
10
11 @interface FaceView ()
12 @property(strong, nonatomic) FaceBlock block;
13 @property (strong, nonatomic) UIImageView *imageView;
14 @end
15
16 @implementation FaceView
17
18 //初始化图片
19 - (id)initWithFrame:(CGRect)frame
20 {
21     //face的大小
22     frame.size.height = 30;
23     frame.size.width = 30;
24     self = [super initWithFrame:frame];
25     if (self) {
26         self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
27         [self addSubview:self.imageView];
28     }
29     return self;
30 }
31
32 -(void) setFaceBlock:(FaceBlock)block
33 {
34     self.block = block;
35 }
36
37
38 -(void) setImage:(UIImage *)image ImageText:(NSString *)text
39 {
40     //显示图片
41     [self.imageView setImage:image];
42
43     //把图片存储起来
44     self.headerImage = image;
45
46     self.imageText = text;
47 }
48
49 //点击时回调
50 -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
51 {
52     UITouch *touch = [touches anyObject];
53     CGPoint point = [touch locationInView:self];
54     //判断触摸的结束点是否在图片中
55     if (CGRectContainsPoint(self.bounds, point))
56     {
57         //回调,把该头像的信息传到相应的controller中
58         self.block(self.headerImage, self.imageText);
59     }
60 }
61
62 @end

    代码说明:

      主要就是block回调的使用,就是封装了一个自定义的button,具体内容请参考之前的博客“IOS开发之自定义Button(集成三种回调模式)

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

      FunctionView.h的代码如下,在.h中留有组件的接口和回调用的Block, plistFileName用于加载我们的资源文件时使用,至于如何使用plist文件,请参考之前的博客:IOS开发之显示微博表情

 1 //
 2 //  FunctionView.h
 3 //  MyKeyBoard
 4 //
 5 //  Created by 青玉伏案 on 14-9-16.
 6 //  Copyright (c) 2014年 Mrli. All rights reserved.
 7 //
 8
 9 #import <UIKit/UIKit.h>
10
11 //定义对应的block类型,用于数据的交互
12 typedef void (^FunctionBlock) (UIImage *image, NSString *imageText);
13
14 @interface FunctionView : UIView
15 //资源文件名
16 @property (nonatomic, strong) NSString *plistFileName;
17 //接受block块
18 -(void)setFunctionBlock:(FunctionBlock) block;
19
20 @end

      FunctionView.m中的代码如下,常用表情是在sqlite中获取的,而全部表情是通过plist文件的信息在Face文件中加载的:

  1 //
  2 //  FunctionView.m
  3 //  MyKeyBoard
  4 //
  5 //  Created by 青玉伏案 on 14-9-16.
  6 //  Copyright (c) 2014年 Mrli. All rights reserved.
  7 //
  8
  9 #import "FunctionView.h"
 10 #import "FaceView.h"
 11 #import "ImageModelClass.h"
 12 #import "HistoryImage.h"
 13
 14 @interface FunctionView()
 15
 16 @property (strong, nonatomic) FunctionBlock block;
 17 //暂存表情组件回调的表情和表情文字
 18 @property (strong, nonatomic) UIImage *headerImage;
 19 @property (strong, nonatomic) NSString *imageText;
 20
 21 //display我们的表情图片
 22 @property (strong, nonatomic) UIScrollView *headerScrollView;
 23
 24 //定义数据模型用于获取历史表情
 25 @property (strong, nonatomic) ImageModelClass *imageModel;
 26
 27 @end
 28
 29
 30 @implementation FunctionView
 31 - (id)initWithFrame:(CGRect)frame
 32 {
 33     self = [super initWithFrame:frame];
 34     if (self) {
 35
 36         //实例化数据模型
 37         self.imageModel =[[ImageModelClass alloc] init];
 38
 39         //实例化下面的button
 40         UIButton *faceButton = [[UIButton alloc] initWithFrame:CGRectZero];
 41         faceButton.backgroundColor = [UIColor grayColor];
 42
 43         [faceButton setTitle:@"全部表情" forState:UIControlStateNormal];
 44         [faceButton setShowsTouchWhenHighlighted:YES];
 45         [faceButton addTarget:self action:@selector(tapButton1:) forControlEvents:UIControlEventTouchUpInside];
 46         [self addSubview:faceButton];
 47
 48
 49         //实例化常用表情按钮
 50         UIButton *moreButton = [[UIButton alloc] initWithFrame:CGRectZero];
 51         moreButton.backgroundColor = [UIColor orangeColor];
 52         [moreButton setTitle:@"常用表情" forState:UIControlStateNormal];
 53         [moreButton setShowsTouchWhenHighlighted:YES];
 54         [moreButton addTarget:self action:@selector(tapButton2:) forControlEvents:UIControlEventTouchUpInside];
 55         [self addSubview:moreButton];
 56
 57         //给按钮添加约束
 58         faceButton.translatesAutoresizingMaskIntoConstraints = NO;
 59         moreButton.translatesAutoresizingMaskIntoConstraints = NO;
 60         //水平约束
 61         NSArray *buttonH = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[faceButton][moreButton(==faceButton)]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(faceButton,moreButton)];
 62         [self addConstraints:buttonH];
 63
 64         //垂直约束
 65         NSArray *button1V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[faceButton(44)]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(faceButton)];
 66         [self addConstraints:button1V];
 67
 68         NSArray *button2V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[moreButton(44)]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(moreButton)];
 69         [self addConstraints:button2V];
 70
 71         //默认显示表情图片
 72         [self tapButton1:nil];
 73
 74     }
 75     return self;
 76 }
 77
 78 //接受回调
 79 -(void)setFunctionBlock:(FunctionBlock)block
 80 {
 81     self.block = block;
 82 }
 83
 84 //点击全部表情按钮回调方法
 85 -(void)tapButton1: (id) sender
 86 {
 87     // 从plist文件载入资源
 88     NSBundle *bundle = [NSBundle mainBundle];
 89     NSString *path = [bundle pathForResource:self.plistFileName ofType:@"plist"];
 90     NSArray *headers = [NSArray arrayWithContentsOfFile:path];
 91
 92     if (headers.count == 0) {
 93         NSLog(@"访问的plist文件不存在");
 94     }
 95     else
 96     {
 97         //调用headers方法显示表情
 98         [self header:headers];
 99     }
100 }
101
102 //点击历史表情的回调方法
103 -(void) tapButton2: (id) sender
104 {
105     //从数据库中查询所有的图片
106     NSArray *imageData = [self.imageModel queryAll];
107     //解析请求到的数据
108     NSMutableArray *headers = [NSMutableArray arrayWithCapacity:imageData.count];
109
110     //数据实体,相当于javaBean的东西
111     HistoryImage *tempData;
112
113     for (int i = 0; i < imageData.count; i ++) {
114         tempData = imageData[i];
115
116         //解析数据,转换成函数headers要用的数据格式
117         NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:2];
118         [dic setObject:tempData.imageText forKey:@"chs"];
119         UIImage *image = [UIImage imageWithData:tempData.headerImage];
120         [dic setObject:image forKey:@"png"];
121
122         [headers addObject:dic];
123     }
124
125     [self header:headers];
126
127 }
128
129
130 //负责把查出来的图片显示
131 -(void) header:(NSArray *)headers
132 {
133     [self.headerScrollView removeFromSuperview];
134     self.headerScrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
135     [self addSubview:self.headerScrollView];
136
137     //给scrollView添加约束
138     self.headerScrollView.translatesAutoresizingMaskIntoConstraints = NO;
139     //水平约束
140     NSArray *scrollH = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[_headerScrollView]-10-|" options:0   metrics:0 views:NSDictionaryOfVariableBindings(_headerScrollView)];
141     [self addConstraints:scrollH];
142
143     //垂直约束
144     NSArray *scrolV = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[_headerScrollView]-50-|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(_headerScrollView)];
145     [self addConstraints:scrolV];
146
147
148
149     CGFloat scrollHeight = (self.frame).size.height-60;
150
151     //根据图片量来计算scrollView的Contain的宽度
152     CGFloat width = (headers.count/(scrollHeight/30))*30;
153     self.headerScrollView.contentSize = CGSizeMake(width, scrollHeight);
154     self.headerScrollView.pagingEnabled = YES;
155
156
157     //图片坐标
158     CGFloat x = 0;
159     CGFloat y = 0;
160
161     //往scroll上贴图片
162     for (int i = 0; i < headers.count; i ++) {
163         //获取图片信息
164         UIImage *image;
165         if ([headers[i][@"png"] isKindOfClass:[NSString class]])
166         {
167              image = [UIImage imageNamed:headers[i][@"png"]];
168         }
169         else
170         {
171             image = headers[i][@"png"];
172         }
173
174         NSString *imageText = headers[i][@"chs"];
175
176         //计算图片位置
177         y = (i%(int)(scrollHeight/30)) * 30;
178         x = (i/(int)(scrollHeight/30)) * 30;
179
180         FaceView *face = [[FaceView alloc] initWithFrame:CGRectMake(x, y, 0, 0)];
181         [face setImage:image ImageText:imageText];
182
183         //face的回调,当face点击时获取face的图片
184         __weak __block FunctionView *copy_self = self;
185         [face setFaceBlock:^(UIImage *image, NSString *imageText)
186          {
187              copy_self.block(image, imageText);
188          }];
189
190         [self.headerScrollView addSubview:face];
191     }
192
193     [self.headerScrollView setNeedsDisplay];
194
195 }
196
197 @end

      代码说明:

        1、主要是通过对资源文件或者对从数据库中查询的资源进行遍历然后添加到ScrollView中

        2.为了适应不同的屏幕给相应的组件添加了约束

    3.ToolView组件的封装: ToolView就是在主屏幕上下面的类似于TabBar的东西,当键盘出来的时候,ToolView会运动到键盘上面的位置。为了使用不同的屏幕,也需要用自动布局来实现。

      ToolView.h的代码如下:预留组件接口和声明block类型

 1 //
 2 //  ToolView.h
 3 //  MyKeyBoard
 4 //
 5 //  Created by 青玉伏案 on 14-9-16.
 6 //  Copyright (c) 2014年 Mrli. All rights reserved.
 7 //
 8
 9 /*****************
10  封装下面的工具条组件
11  *****************/
12 #import <UIKit/UIKit.h>
13
14 //定义block块变量类型,用于回调,把本View上的按钮的index传到Controller中
15 typedef void (^ToolIndex) (NSInteger index);
16
17 @interface ToolView : UIView
18
19 //块变量类型的setter方法
20 -(void)setToolIndex:(ToolIndex) toolBlock;
21
22 @end

      ToolView.m的代码实现:

 1 //
 2 //  ToolView.m
 3 //  MyKeyBoard
 4 //
 5 //  Created by 青玉伏案 on 14-9-16.
 6 //  Copyright (c) 2014年 Mrli. All rights reserved.
 7 //
 8
 9 #import "ToolView.h"
10
11 @interface ToolView ()
12
13 //定义ToolIndex类型的block,用于接受外界传过来的block
14 @property (nonatomic, strong) ToolIndex myBlock;
15
16 @end
17
18
19 @implementation ToolView
20
21 - (id)initWithFrame:(CGRect)frame
22 {
23     self = [super initWithFrame:frame];
24     if (self) {
25
26         //1初始化表情按钮
27         UIButton *faceButton = [[UIButton alloc] initWithFrame:CGRectZero];
28         faceButton.backgroundColor = [UIColor orangeColor];
29         [faceButton setTitle:@"表情" forState:UIControlStateNormal];
30         [faceButton setShowsTouchWhenHighlighted:YES];
31         [faceButton addTarget:self action:@selector(tapFaceButton:) forControlEvents:UIControlEventTouchUpInside];
32         [self addSubview:faceButton];
33
34
35         //初始化更多按钮
36         UIButton *moreButton = [[UIButton alloc] initWithFrame:CGRectZero];
37         moreButton.backgroundColor = [UIColor grayColor];
38         [moreButton setTitle:@"More" forState:UIControlStateNormal];
39         [moreButton setShowsTouchWhenHighlighted:YES];
40         [moreButton addTarget:self action:@selector(tapMoreButton:) forControlEvents:UIControlEventTouchUpInside];
41         [self addSubview:moreButton];
42
43
44         //给我们的按钮添加约束来让按钮来占满toolView;
45         faceButton.translatesAutoresizingMaskIntoConstraints = NO;
46         moreButton.translatesAutoresizingMaskIntoConstraints = NO;
47
48         //添加水平约束
49         NSArray *buttonH = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[faceButton][moreButton(==faceButton)]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(faceButton,moreButton)];
50         [self addConstraints:buttonH];
51
52         //添加垂直约束
53         NSArray *button1V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[faceButton]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(faceButton)];
54         [self addConstraints:button1V];
55
56         NSArray *button2V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[moreButton]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(moreButton)];
57         [self addConstraints:button2V];
58
59     }
60     return self;
61 }
62
63 //接受传入的回调
64 -(void) setToolIndex:(ToolIndex)toolBlock
65 {
66     self.myBlock = toolBlock;
67 }
68
69 //点击表情按钮要回调的方法
70 -(void) tapFaceButton: (id) sender
71 {
72     self.myBlock(1);
73 }
74
75 //点击more要回调的方法
76 -(void) tapMoreButton: (id) sender
77 {
78     self.myBlock(2);
79 }
80
81 @end

      代码说明:

        主要是对block回调的应用和给相应的组件添加相应的约束

    4.MoreView组件的封装代码就不往上贴啦,和上面的类似,下面是调用MoreView组件的运行效果,有兴趣的读者请自行编写,以上就是视图部分的代码了

     

  二. Mode部分的内容:

    1.先定义我们要使用的数据模型,数据模型如下,time是使用表情的时间,用于排序。

    2.下面编写我们的ImageModelClass类,里面封装了我们操作数据要用的方法

      ImageModelClass.h的代码如下,主要是预留的对外的接口:

 1 //
 2 //  ImageModelClass.h
 3 //  MyKeyBoard
 4 //
 5 //  Created by 青玉伏案 on 14-9-16.
 6 //  Copyright (c) 2014年 Mrli. All rights reserved.
 7 //
 8
 9 #import <Foundation/Foundation.h>
10 #import <CoreData/CoreData.h>
11 #import "HistoryImage.h"
12
13 @interface ImageModelClass : NSObject
14 //保存数据
15 -(void)save:(NSData *) image ImageText:(NSString *) imageText;
16 //查询所有的图片
17 -(NSArray *) queryAll;
18 @end

      ImageModelClass.m的代码如下,主要是用CoreData对sqlite的操作:

  1 //
  2 //  ImageModelClass.m
  3 //  MyKeyBoard
  4 //
  5 //  Created by 青玉伏案 on 14-9-16.
  6 //  Copyright (c) 2014年 Mrli. All rights reserved.
  7 //
  8
  9 #import "ImageModelClass.h"
 10
 11 @interface ImageModelClass ()
 12
 13 @property (nonatomic, strong) NSManagedObjectContext *manager;
 14
 15 @end
 16
 17 @implementation ImageModelClass
 18 - (instancetype)init
 19 {
 20     self = [super init];
 21     if (self) {
 22         //通过上下文获取manager
 23         UIApplication *application = [UIApplication sharedApplication];
 24         id delegate = application.delegate;
 25         self.manager = [delegate managedObjectContext];
 26     }
 27     return self;
 28 }
 29
 30 -(void)save:(NSData *)image ImageText:(NSString *)imageText
 31 {
 32     if (image != nil) {
 33         NSArray *result = [self search:imageText];
 34
 35         HistoryImage *myImage;
 36
 37         if (result.count == 0)
 38         {
 39             myImage = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([HistoryImage class]) inManagedObjectContext:self.manager];
 40             myImage.imageText = imageText;
 41             myImage.headerImage = image;
 42             myImage.time = [NSDate date];
 43         }
 44         else
 45         {
 46             myImage = result[0];
 47             myImage.time = [NSDate date];
 48         }
 49
 50         //存储实体
 51         NSError *error = nil;
 52         if (![self.manager save:&error]) {
 53             NSLog(@"保存出错%@", [error localizedDescription]);
 54         }
 55
 56     }
 57
 58 }
 59
 60
 61 //查找
 62 -(NSArray *)search:(NSString *) image
 63 {
 64     NSArray *result;
 65
 66         //新建查询条件
 67         NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([HistoryImage class])];
 68
 69         //添加谓词
 70         NSPredicate *predicate = [NSPredicate predicateWithFormat:@"imageText=%@",image];
 71
 72         //把谓词给request
 73         [fetchRequest setPredicate:predicate];
 74
 75         //执行查询
 76         NSError *error = nil;
 77         result = [self.manager executeFetchRequest:fetchRequest error:&error];
 78         if (error) {
 79             NSLog(@"查询错误:%@", [error localizedDescription]);
 80         }
 81     return result;
 82 }
 83
 84
 85
 86 //查询所有的
 87 -(NSArray *) queryAll
 88 {
 89     //新建查询条件
 90     NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([HistoryImage class])];
 91
 92     //添加排序规则
 93     //定义排序规则
 94     NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"time" ascending:NO];
 95
 96     //添加排序规则
 97     [fetchRequest setSortDescriptors:@[sortDescriptor]];
 98
 99
100     //执行查询
101     NSError *error = nil;
102     NSArray *result = [self.manager executeFetchRequest:fetchRequest error:&error];
103     if (error) {
104         NSLog(@"查询错误:%@", [error localizedDescription]);
105     }
106
107     return result;
108 }
109
110 @end

      代码说明:

        1.保存图片时先查找图片是否存在,如果存在则更新时间,如果不存在则插入数据(写到这感觉想在用Hibernate写东西)。

  三.Controller部分,把上面的组件进行组装

    1.MainViewController.m中的延展部分的代码如下:

 1 @interface MainViewController ()
 2
 3 //自定义组件
 4 @property (nonatomic, strong) ToolView *toolView;
 5
 6 @property (nonatomic, strong) FunctionView *functionView;
 7
 8 @property (nonatomic, strong) MoreView *moreView;
 9
10 //系统组件
11 @property (strong, nonatomic) IBOutlet UITextView *myTextView;
12
13 @property (strong, nonatomic) NSDictionary *keyBoardDic;
14
15 @property (strong, nonatomic) IBOutlet UIImageView *imageView;
16
17 @property (strong, nonatomic) NSString *sendString;
18
19 //数据model
20 @property (strong, nonatomic) ImageModelClass  *imageMode;
21
22 @property (strong, nonatomic)HistoryImage *tempImage;
23
24 @end

    2.在viewDidLoad中进行组件的初始化和实现组件的Block回调,代码如下

  1 - (void)viewDidLoad
  2 {
  3     [super viewDidLoad];
  4
  5     //从sqlite中读取数据
  6     self.imageMode = [[ImageModelClass alloc] init];
  7     self.tempImage = [[HistoryImage alloc] init];
  8
  9
 10     //实例化FunctionView
 11     self.functionView = [[FunctionView alloc] initWithFrame:CGRectMake(0, 0, 320, 216)];
 12     self.functionView.backgroundColor = [UIColor blackColor];
 13
 14     //设置资源加载的文件名
 15     self.functionView.plistFileName = @"emoticons";
 16
 17     __weak __block MainViewController *copy_self = self;
 18     //获取图片并显示
 19     [self.functionView setFunctionBlock:^(UIImage *image, NSString *imageText)
 20      {
 21          NSString *str = [NSString stringWithFormat:@"%@%@",copy_self.myTextView.text, imageText];
 22
 23         copy_self.myTextView.text = str;
 24         copy_self.imageView.image = image;
 25
 26         //把使用过的图片存入sqlite
 27          NSData *imageData = UIImagePNGRepresentation(image);
 28          [copy_self.imageMode save:imageData ImageText:imageText];
 29     }];
 30
 31
 32     //实例化MoreView
 33     self.moreView = [[MoreView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
 34     self.moreView.backgroundColor = [UIColor blackColor];
 35     [self.moreView setMoreBlock:^(NSInteger index) {
 36         NSLog(@"MoreIndex = %d",index);
 37     }];
 38
 39
 40
 41     //进行ToolView的实例化
 42     self.toolView = [[ToolView alloc] initWithFrame:CGRectZero];
 43     self.toolView.backgroundColor = [UIColor blackColor];
 44     [self.view addSubview:self.toolView];
 45
 46     //给ToolView添加约束
 47     //开启自动布局
 48     self.toolView.translatesAutoresizingMaskIntoConstraints = NO;
 49
 50     //水平约束
 51     NSArray *toolHConstraint = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_toolView]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(_toolView)];
 52     [self.view addConstraints:toolHConstraint];
 53
 54     //垂直约束
 55     NSArray *toolVConstraint = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[_toolView(44)]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(_toolView)];
 56     [self.view addConstraints:toolVConstraint];
 57
 58
 59
 60
 61     //回调toolView中的方法
 62     [self.toolView setToolIndex:^(NSInteger index)
 63     {
 64         NSLog(@"%d", index);
 65
 66         switch (index) {
 67             case 1:
 68                 [copy_self changeKeyboardToFunction];
 69                 break;
 70
 71             case 2:
 72                 [copy_self changeKeyboardToMore];
 73                 break;
 74
 75             default:
 76                 break;
 77         }
 78
 79     }];
 80
 81
 82
 83     //当键盘出来的时候通过通知来获取键盘的信息
 84     //注册为键盘的监听着
 85     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
 86     [center addObserver:self selector:@selector(keyNotification:) name:UIKeyboardWillChangeFrameNotification object:nil];
 87
 88
 89     //给键盘添加dan
 90     //TextView的键盘定制回收按钮
 91     UIToolbar * toolBar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 30)];
 92
 93     UIBarButtonItem * item1 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(tapDone:)];
 94     UIBarButtonItem * item2 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
 95     UIBarButtonItem * item3 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
 96     toolBar.items = @[item2,item1,item3];
 97
 98     self.myTextView.inputAccessoryView =toolBar;
 99
100 }

    3.当横竖屏幕切换时设置自定义键盘的高度

 1 -(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
 2 {
 3     //纵屏
 4     if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
 5         CGRect frame = self.functionView.frame;
 6         frame.size.height = 216;
 7         self.functionView.frame = frame;
 8         self.moreView.frame = frame;
 9
10     }
11     //横屏
12     if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
13         CGRect frame = self.functionView.frame;
14         frame.size.height = 150;
15         self.functionView.frame = frame;
16         self.moreView.frame = frame;
17     }
18 }

    4.当键盘出来的时候,改变toolView的位置,通过键盘的通知来实现。当横屏的时候键盘的坐标系和我们当前的Frame的坐标系不一样所以当横屏时得做一坐标系的转换,代码如下;

 1 //当键盘出来的时候改变toolView的位置(接到键盘出来的通知要做的方法)
 2 -(void) keyNotification : (NSNotification *) notification
 3 {
 4     NSLog(@"%@", notification.userInfo);
 5
 6     self.keyBoardDic = notification.userInfo;
 7     //获取键盘移动后的坐标点的坐标点
 8     CGRect rect = [self.keyBoardDic[@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
 9
10     //把键盘的坐标系改成当前我们window的坐标系
11     CGRect r1 = [self.view convertRect:rect fromView:self.view.window];
12
13     [UIView animateWithDuration:[self.keyBoardDic[UIKeyboardAnimationDurationUserInfoKey] floatValue] animations:^{
14
15         //动画曲线
16         [UIView setAnimationCurve:[self.keyBoardDic[UIKeyboardAnimationCurveUserInfoKey] doubleValue]];
17
18         CGRect frame = self.toolView.frame;
19
20         frame.origin.y = r1.origin.y - frame.size.height;
21
22         //根据键盘的高度来改变toolView的高度
23         self.toolView.frame = frame;
24     }];
25 }

    5.系统键盘和自定义键盘切换的代码如下:

 1 //切换键盘的方法
 2 -(void) changeKeyboardToFunction
 3 {
 4     if ([self.myTextView.inputView isEqual:self.functionView])
 5     {
 6         self.myTextView.inputView = nil;
 7         [self.myTextView reloadInputViews];
 8     }
 9     else
10     {
11         self.myTextView.inputView = self.functionView;
12         [self.myTextView reloadInputViews];
13     }
14
15     if (![self.myTextView isFirstResponder])
16     {
17         [self.myTextView becomeFirstResponder];
18     }
19 }

    

  以上就是上面展示效果的核心代码了,在做的时候感觉难点在于如何进行屏幕适配,尤其是当屏幕横过来的时候键盘的坐标系和我们frame的坐标系不同,得做一个转换。发表博客的目的是想起到抛砖引玉的左右,有好的东西希望大家相互交流一下。笔者水平有限难免有偏颇之处,欢迎批评指正。

时间: 2025-01-03 17:19:50

iOS开发之自定义表情键盘(组件封装与自动布局)的相关文章

iOS开发之微信聊天工具栏的封装(走过路过不要错过哦)

之前山寨了一个新浪微博(iOS开发之山寨版新浪微博小结),这几天就山寨个微信吧.之前已经把微信的视图结构简单的拖了一下(IOS开发之微信山寨版),今天就开始给微信加上具体的实现功能,那么就先从微信的聊天界面开始吧.提到封装是少不了写代码的,在封装组件的时候,为了组件的可移植性,我们就不能用storyboard来拖拽了.为了屏幕的适配,适应不同屏幕的手机,所以在封装组件的时候是少不了为我们的组件来添加约束.今天博客中的所有代码都是脱离storyboard的,这些代码在别的工程中也是可以使用的.好,

IOS开发之自定义系统弹出键盘上方的view

IOS开发之自定义系统弹出键盘上方的view 分类: IOS 2014-11-18 09:26 1304人阅读 评论(0) 收藏 举报 目录(?)[+] 这篇文章解决的一个开发中的实际问题就是:当弹出键盘时,自定义键盘上方的view.目前就我的经验来看,有两种解决方法.一个就是利用 UITextField或者UITextView的inputAccessoryView属性,另一种,就是监听键盘弹出的notification来自 己解决相关视图的位置问题. 第一种解决方法相对比较简单,第二种的方法中

IOS开发学习笔记-(2)键盘控制,键盘类型设置,alert 对话框

一.关闭键盘,放弃第一响应者,处理思路有两种 ① 使用文本框的 Did End on Exit 绑定事件 ② UIControl on Touch 事件 都去操作 sender 的  resignFirstResponder #import <UIKit/UIKit.h> @interface ViewController : UIViewController @property (weak, nonatomic) IBOutlet UITextField *txtUserName; @pro

iOS开发中自定义字体的方法

http://www.cnblogs.com/iyou/archive/2014/05/25/3751669.html 1. 首先下载你想要设置的字体库,例如设置方正启体简体 2. 添加到工程,一定要注意勾选红色框框处,默认是不勾选的  添加以后 3.在plist文件中添加 4.现在已经添加成功了,但是要使用就必须知道FontName,用以下代码可查到 NSArray *familyNames = [[NSArray alloc] initWithArray:[UIFont familyName

详解iOS开发之自定义View

iOS开发之自定义View是本文要将介绍的内容,iOS SDK中的View是UIView,我们可以很方便的自定义一个View.创建一个 Window-based Application程序,在其中添加一个Hypnosister的类,这个类选择继承UIObject.修改这个类,使他继承:UIView @interface HypnosisView : UIView 自定义View的关键是定义drawRect: 方法,因为主要是通过重载这个方法,来改变view的外观.例如,可以使用下面代码绘制一个很

【好程序员笔记分享】——iOS开发之纯代码键盘退出

-iOS培训,iOS学习-------型技术博客.期待与您交流!------------ iOS开发之纯代码键盘退出(非常简单)     iOS开发之纯代码键盘退出 前面说到了好几次关于键盘退出的,但是最近开始着手项目的时候却闷了,因为太多了,笔者确实知道有很多中方法能实现,而且令我影响最深的就是 EndEditing,但是因为即有textView,又有TextField而且他们各有不同的方法,虽然笔者现在搞懂了,但是不知道什么时候又不记得 了,而且虽然感觉很简单现在感觉很简单的样子,但是对于没

iOS开发UI篇—核心动画(UIView封装动画)(转摘)

iOS开发UI篇—核心动画(UIView封装动画) 一.UIView动画(首尾) 1.简单说明 UIKit直接将动画集成到UIView类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画支持 执行动画所需要的工作由UIView类自动完成,但仍要在希望执行动画时通知视图,为此需要将改变属性的代码放在[UIViewbeginAnimations:nil context:nil]和[UIView commitAnimations]之间 常见方法解析: + (void)setAnimati

ios开发之--仿(微信)自定义表情键盘

先附上demo:https://github.com/hgl753951/CusEmoji.git 效果图如下: 先说下具体的实现功能: 1,本地加载了一些H5的代码,直接使用webview的load方法加载出来的 2,获取web的动态高度,然后创建tableview,把webview加到一个tableview的headerview上, 3,实现cell的自定义高度,是用xib实现的 4,评论功能的实现,自定表情键盘,可以发送表情,文字等相关信息 这个demo里面有2个效果,第一个是我自己写的很

Android开发案例 - 自定义虚拟键盘

所有包含IM功能的App(如微信, 微博, QQ, 支付宝等)都提供了Emoji表情之类的虚拟键盘,  如下图:    本文只着重介绍如何实现输入法键盘和自定义虚拟键盘的流畅切换, 而不介绍如何实现虚拟键盘, 因为后者实现相对容易, 而前者若实现不好, 则会出现体验的问题, 比如输入区域的视图在切换时会跳动等问题. 知识要点: AndroidManifest.xml: activity属性 android:windowSoftInputMode  InputMethodManager Windo