本控件效果如下图所示:
本控件设计的几个原则 :
1.扩展性好,代码不冗余。
2.逻辑清晰。
3.回调接口清晰。
4.移植性好。
首先来讨论下本控件的扩展性:
效果图给出的是3行7列。如果这个行列因子可控,起码可以应付策划哪一天突发奇想想重新设计这个表情键盘的行列,如果写死在代码中,就不太方便了。
因此这个因子可以抛出来。
抛出的代码如下:
@property(nonatomic,assign) NSInteger rowCount; //行数
@property(nonatomic,assign) NSInteger colCount; //列数
@property(nonatomic,assign) NSInteger pageCount; //页数
对应的逻辑为:
把表情键盘中的元素看作是一个硬盘的带分布索引的二维数组即可,元素地列表中位置计算代码如下:
-(void) buildElements:(NSMutableArray*) elements{
if([elements count] == 0) return;
NSInteger rowMargin = (self.frame.size.width - self.colCount * 30) / (self.colCount + 1); //行间距
NSInteger colMargin = (self.frame.size.height - self.rowCount * 30) / (self.rowCount + 1); //列间距
//计算每一个表情的位置
for(int k = 0; k < self.pageCount; k ++){
for(int i = 0; i < self.rowCount; i ++){
for(int j = 0; j < self.colCount; j ++){
UIImageView* emojiView = [[UIImageView alloc] initWithFrame:CGRectMake(rowMargin + (k * self.frame.size.width) + j * (30 + rowMargin),
colMargin + i * (30 + colMargin), 30, 30)];
NSInteger idx = self.rowCount * self.colCount * k + i * self.colCount + j;
if(idx >= [elements count]){
idx = [elements count] - 1;
break;
}
emojiView.contentMode = UIViewContentModeScaleAspectFit;
emojiView.image = [UIImage imageNamed:elements[idx]];
emojiView.tag = idx;
emojiView.userInteractionEnabled = true;
[emojiView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)]];
[self addSubview:emojiView];
}
}
}
}
为了均分滚动列表的size,上面的代码中将间距给计算了,注意计算方式。
3.为了能够滚动,可以扩展下UIScrollView,然后监听ScrollView的contentSize 来更新表情键盘的页数。这里使用了kvo来进行监听,页数的计算式如下:
//-------------------kvo 实现观察主题 ----------------
//对于滑动翻页使用kvo监听机制
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
CGPoint offset = [change[NSKeyValueChangeNewKey] CGPointValue];
CGPoint oldOffset = [change[NSKeyValueChangeOldKey] CGPointValue];
CGFloat oldOffsetX = oldOffset.x;
CGFloat deltaOfOffsetX = offset.x - oldOffsetX;
NSInteger nCurIndex = deltaOfOffsetX / SCREEN_WIDTH;
[self updatePointWithIdx:nCurIndex];
}
-(void)dealloc{
[self.emojiPanel removeObserver:self forKeyPath:@"contentOffset" context:nil];
}
//-------------------kvo 实现观察主题 end----------------
4.有些策划比较细致,注意用户体验。给每一页的最后一个表情设定为“删除”
其实这个计算在加载表情资源的时候做一个小处理就行,处理的计算逻辑为:
if(保存的表情的列表当前长度+1 % (行*列)==0 || 保存的表情的列表当前长度 -1) {
这个列表就保存上面那个删除表情。
}
计算代码如下:
NSMutableArray* emojiList = [NSMutableArray array];
NSInteger total = 54;
for(int i = 0;i < total; i ++){
if( i < 10){
[emojiList addObject:[NSString stringWithFormat:@"f00%d",i]];
}
if(i >= 10 && i < 100){
if( ([emojiList count] + 1) % 21 == 0 || i == total - 1){
[emojiList addObject:@"f_del.png"];
}else{
[emojiList addObject:[NSString stringWithFormat:@"f0%d",i]];
}
}
}
int pageCount = ceilf([emojiList count] / 21.0);
self.emojiPanel = [[ScrollView alloc] initWithFrame:CGRectMake(0, 50, SCREEN_WIDTH, 210)];
self.emojiPanel.backgroundColor = [UIColor greenColor];
self.emojiPanel.rowCount = 3;
self.emojiPanel.colCount = 7;
self.emojiPanel.pageCount = pageCount;
self.emojiPanel.pagingEnabled = true;
self.emojiPanel.contentSize = CGSizeMake(self.emojiPanel.frame.size.width * pageCount, 210);
[self.emojiPanel buildElements:emojiList];
//使用kvo监听ScrollView滑动监听事件
[self.emojiPanel addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
self.emojiPanel.emojiClick = ^(NSInteger idx){
NSLog(@"271------------:%ld",idx);
};
[self.view addSubview:self.emojiPanel];
6.回调的接口较简单,具体见代码,在此不作分析。
下面奉送全部代码:
.h文件:
#import <UIKit/UIKit.h>
//为表情添加一个回调
typedef void (^EmojiOnClickListener) (NSInteger);
/**
滑动面板,主要实现,数据分布显示
*/
@interface ScrollView : UIScrollView
@property(nonatomic,assign) NSInteger rowCount; //行数
@property(nonatomic,assign) NSInteger colCount; //列数
@property(nonatomic,assign) NSInteger pageCount; //页数
@property(nonatomic,strong) NSMutableArray* dataList;
-(void) buildElements:(NSMutableArray*) elements;
/**
普通样式的block 表情点击回调
*/
@property (nonatomic, copy) void (^onClickBlock)(NSInteger idx);
//对于typedef定义的block其实是一个匿名函数,此处声明一个全局变量保持这个函数引用
@property(nonatomic,copy) EmojiOnClickListener emojiClick;
@end
。m文件:
#import "ScrollView.h"
@implementation ScrollView
-(instancetype) initWithFrame:(CGRect)frame{
if(self = [super initWithFrame:frame]){
}
return self;
}
-(void) buildElements:(NSMutableArray*) elements{
if([elements count] == 0) return;
NSInteger rowMargin = (self.frame.size.width - self.colCount * 30) / (self.colCount + 1); //行间距
NSInteger colMargin = (self.frame.size.height - self.rowCount * 30) / (self.rowCount + 1); //列间距
//计算每一个表情的位置
for(int k = 0; k < self.pageCount; k ++){
for(int i = 0; i < self.rowCount; i ++){
for(int j = 0; j < self.colCount; j ++){
UIImageView* emojiView = [[UIImageView alloc] initWithFrame:CGRectMake(rowMargin + (k * self.frame.size.width) + j * (30 + rowMargin),
colMargin + i * (30 + colMargin), 30, 30)];
NSInteger idx = self.rowCount * self.colCount * k + i * self.colCount + j;
if(idx >= [elements count]){
idx = [elements count] - 1;
break;
}
emojiView.contentMode = UIViewContentModeScaleAspectFit;
emojiView.image = [UIImage imageNamed:elements[idx]];
emojiView.tag = idx;
emojiView.userInteractionEnabled = true;
[emojiView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)]];
[self addSubview:emojiView];
}
}
}
}
-(void) handleGesture:(UITapGestureRecognizer*) gestureRecognizer{
UIView* viewClicked=[gestureRecognizer view];
NSLog(@"66---------------:%ld",(long)viewClicked.tag);
if(self.emojiClick){
self.emojiClick(viewClicked.tag);
}
}
@end
在vc中的测试代码如下:
//测试表情键盘
-(void) testEmojiPanel{
NSMutableArray* emojiList = [NSMutableArray array];
NSInteger total = 54;
for(int i = 0;i < total; i ++){
if( i < 10){
[emojiList addObject:[NSString stringWithFormat:@"f00%d",i]];
}
if(i >= 10 && i < 100){
if( ([emojiList count] + 1) % 21 == 0 || i == total - 1){
[emojiList addObject:@"f_del.png"];
}else{
[emojiList addObject:[NSString stringWithFormat:@"f0%d",i]];
}
}
}
int pageCount = ceilf([emojiList count] / 21.0);
self.emojiPanel = [[ScrollView alloc] initWithFrame:CGRectMake(0, 50, SCREEN_WIDTH, 210)];
self.emojiPanel.backgroundColor = [UIColor greenColor];
self.emojiPanel.rowCount = 3;
self.emojiPanel.colCount = 7;
self.emojiPanel.pageCount = pageCount;
self.emojiPanel.pagingEnabled = true;
self.emojiPanel.contentSize = CGSizeMake(self.emojiPanel.frame.size.width * pageCount, 210);
[self.emojiPanel buildElements:emojiList];
//使用kvo监听ScrollView滑动监听事件
[self.emojiPanel addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
self.emojiPanel.emojiClick = ^(NSInteger idx){
NSLog(@"271------------:%ld",idx);
};
[self.view addSubview:self.emojiPanel];
//构建索引点
[self buildPagePoint];
}
//private --method 构建索引点
-(void) buildPagePoint{
if(self.emojiPanel.pageCount == 0) return;
NSInteger pointTotalWidth = 100; //假设索引点占用宽度
NSInteger pointWidth = 10;
NSInteger startOffsetX = (self.emojiPanel.frame.size.width - pointTotalWidth) / 2; //起始偏移量
NSInteger offsetX = (pointTotalWidth - self.emojiPanel.pageCount * pointWidth) / (self.emojiPanel.pageCount + 1); //偏移量
for(int i = 0; i < self.emojiPanel.pageCount; i ++){
UIImageView* pointIndexView = [[UIImageView alloc] initWithFrame:CGRectMake(startOffsetX + offsetX + i *(pointWidth + offsetX),270,
pointWidth,pointWidth)];
if(i == 0){
pointIndexView.backgroundColor = [UIColor redColor];
}else{
pointIndexView.backgroundColor = [UIColor grayColor];
}
pointIndexView.tag = 100 + i;
pointIndexView.layer.masksToBounds = true;
pointIndexView.layer.cornerRadius = pointWidth / 2;
[self.view addSubview:pointIndexView];
}
}
//这个地方俺是故意这样写滴,,^_^
-(void)updatePointWithIdx:(NSInteger) idx{
for(int i = 0 ; i < self.emojiPanel.pageCount; i ++){
if( i == idx ){
[self.view viewWithTag:(100 + i)].backgroundColor = [UIColor redColor];
}else{
[self.view viewWithTag:(100 + i)].backgroundColor = [UIColor grayColor];
}
}
}
//-------------------kvo 实现观察主题 ----------------
//对于滑动翻页使用kvo监听机制
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
CGPoint offset = [change[NSKeyValueChangeNewKey] CGPointValue];
CGPoint oldOffset = [change[NSKeyValueChangeOldKey] CGPointValue];
CGFloat oldOffsetX = oldOffset.x;
CGFloat deltaOfOffsetX = offset.x - oldOffsetX;
NSInteger nCurIndex = deltaOfOffsetX / SCREEN_WIDTH;
[self updatePointWithIdx:nCurIndex];
}
-(void)dealloc{
[self.emojiPanel removeObserver:self forKeyPath:@"contentOffset" context:nil];
}
//-------------------kvo 实现观察主题 end----------------