您的位置:首页 > 移动开发 > IOS开发

IOS控件系列--优雅的表情键盘设计(扩展性好)

2017-09-08 09:30 246 查看
本控件效果如下图所示:



本控件设计的几个原则 :

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];


5.下面的索引点就非常简单了,直接监听滚动列表的滑动事件即可达到。

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 tota
d11e
l = 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----------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  IOS 表情键盘