表情面板的实现

自娱自乐,记录成长脚步,大神路过请直走,也欢迎批评指正~

效果图

这里写图片描述

1、封装表情视图

思路:
  • (1)首先,处理数据:在初始化方法initViewFrame中处理数据,plist表情数据应该处理成一个二维数组,所以需要定义一个数组类型的实例变量,是大数组即存放数组的数组,每一个小数组存放的是每一页中的4 * 7 = 28个表情的字典数据。解析plist文件,遍历其中的字典,当小数组为空或者,数组元素等于28时,初始化一下数组,然后添加到大数组中去,把遍历出来的字典装进小数组中。就完成了数据的处理。

    注意:注意:处理完数据之后,就能得到表情视图的宽高,因为初始化的时候没有给定确定的宽高,因此在这里,再给定宽高

    //取出文件
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"emoticons" ofType:@"plist"];
    NSArray *emoticons = [NSArray arrayWithContentsOfFile:filePath];

    _dataArray = [NSMutableArray array];

    //小数组
    NSMutableArray *arr2D = nil;

    //遍历emoticons
    for (int i = 0; i < emoticons.count; i ++) {

        if (arr2D == nil || arr2D.count == 28) {
            arr2D = [NSMutableArray array];
            [_dataArray addObject:arr2D];
        }

        NSDictionary *item = emoticons[i];
        [arr2D addObject:item];
    }
    //设置当前视图的宽高
    self.width = _dataArray.count * kScreenWidth;
    self.height = kFace_Height * 4;

    //initViews
    [self initViews];
  • (2)然后,在View上边画出来表情:在 - (void)drawRect:(CGRect)rect方法中,通过行和列两层for循环遍历出来每个表情的字典数据,取到图片,计算坐标,通过 drawInRect方法画上去
    注意:坐标变量可以设置为宏定义。
/*宏定义
//每个单元格所占的区域的宽和高
#define kFace_Height (kScreenWidth / 7.0)
#define kFace_Width (kScreenWidth / 7.0)

//表情图片的宽和高
#define kItem_Width 30.0
#define kItem_Height 30.0
*/
    for (int i = 0; i < _dataArray.count; i ++) {
        NSArray *arr2D = _dataArray[i];

        for (int j = 0; j < arr2D.count; j ++) {
            //取到item的信息
            NSDictionary *item = arr2D[j];

            //取到图片
            NSString *imgName = item[@"png"];
            UIImage *image = [UIImage imageNamed:imgName];

            //计算坐标
            CGFloat x = i * kScreenWidth + colum *kFace_Width + (kFace_Width - kItem_Width) / 2;
            CGFloat y = row * kFace_Height + (kFace_Height - kItem_Height) / 2;

            [image drawInRect:CGRectMake(x, y, kItem_Width, kItem_Height)];

            colum ++;
            if (colum == 7) {
                row ++;
                colum = 0;
            }

            if (row == 4) {
                row = 0;
            }
        }
    }
  • (3)接着,创建放大镜视图: 开始位置先设为(0,0),设置背景图,创建放大镜上的表情视图,计算表情视图的坐标。
//创建放大镜视图
    _imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 64, 92)];
    _imgView.image = [UIImage imageNamed:@"emoticon_keyboard_magnifier.png"];
    _imgView.backgroundColor = [UIColor clearColor];

    //一开始隐藏放大镜
    _imgView.hidden = YES;

    [self addSubview:_imgView];

    //创建放大镜上的表情视图
    UIImageView *faceView = [[UIImageView alloc] initWithFrame:CGRectMake((_imgView.width - kItem_Width) / 2, 15, kItem_Width, kItem_Height)];
    faceView.backgroundColor = [UIColor clearColor];
    faceView.tag = 123;
    [_imgView addSubview:faceView];
  • (4)然后,实现放大镜移动: 在touches开始和结束的方法中,分别显示和隐藏放大镜视图;在开始和移动的方法中,都能够拿到touch对象,获取到触摸的坐标,然后自定义一个以坐标为参数的函数计算放大镜的坐标,分别计算触摸开始和移动的时候,放大镜的位置,和触摸到的是第几页第几个表情;

    注意:当在滑动表情时,scrollView也在滑动,互相有影响,因此,要在触摸开始时禁止scroolView滑动,结束时,开启滑动。

//开始接触时调用的方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    _imgView.hidden = NO;

    //禁止滑动
    if ([self.subviews isKindOfClass:[UIScrollView class]]) {
        UIScrollView *scrollView = (UIScrollView *)self.subviews;
        scrollView.scrollEnabled = YES;
    }
    //拿到触摸的坐标
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];

    [self touchPoint:point];
}
  • 接着,传值: 构建表情视图,点击的表情最后是要显示到要发微博的文本框中去的,因此要拿到表情的对应字符串名,再构建放大镜表情的时候可以拿到这个表情的表情名如:[哭],应该定义一个实例变量,在这里接收这个名字;并且,定义一个参数为上一句中的表情名的block,然后,在结束触摸事件的方法中,回调block,传值;
    注意:最后一页的表情可能不满28个,当触发的位置是空白位置时,应该把要传的参数表情名置为空,然后放大镜隐藏。
//结束触摸调用的方法
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    //隐藏放大镜
    _imgView.hidden = YES;

    //开启滑动
    if ([self.subviews isKindOfClass:[UIScrollView class]]) {
        UIScrollView *scrollView = (UIScrollView *)self.subviews;
        scrollView.scrollEnabled = NO;
    }

    //回调block
    //安全判断
    if (self.block) {
        self.block(_selectedFaceName);
    }

}
//通过point计算坐标
- (void)touchPoint:(CGPoint)point
{
    _imgView.hidden = NO;

    //页数
    NSInteger page = point.x / kScreenWidth;

    //安全判断
    if (page > _dataArray.count) {
        _imgView.hidden = YES;
        return;
    }

    //在当前界面中根据坐标计算行和列
    NSInteger row = point.y / kFace_Height;//行数
    NSInteger colum = (point.x - page * kScreenWidth) / kFace_Width;//列数

    //限定row和colum的范围
    row = MAX(0, row);
    row = MIN(row, 3);

    colum = MAX(0, colum);
    colum = MIN(colum, 6);

    //计算在当前页面中是第几个元素
    NSInteger index = row * 7 + colum;

    //通过页面取出小数组
    NSArray *arr2D = _dataArray[page];

    //安全判断(最后一页只有几个元素)
    if (index >= arr2D.count) {
        _imgView.hidden = YES;
        _selectedFaceName = nil;
        return;
    }

    //取出图片名
    NSDictionary *faceItem = arr2D[index];
    NSString *imgName = [faceItem objectForKey:@"png"];
    NSString *faceName = [faceItem objectForKey:@"chs"];

    //表情的名字
    if (![faceName isEqualToString:_selectedFaceName]) {
        //取出视图
        UIImageView *imageView = (UIImageView *)[_imgView viewWithTag:123];
        imageView.image = [UIImage imageNamed:imgName];

        //设置放大镜的坐标
        //设置中心点的横坐标与表情的中心点的横坐标相同
        CGFloat x = page * kScreenWidth + colum * kFace_Width + 0.5 * kFace_Width;
        _imgView.center = CGPointMake(x, 0);

        //设置底部坐标与表情坐标的顶部坐标相同
        CGFloat y = row * kFace_Height + 0.5 *kFace_Height;
        _imgView.bottom = y;

        _selectedFaceName = faceName;
    }
}

2、封装表情面板

思路
  • 1、.h文件中的属性和方法:表情面板是在UIView上的,因此封装的这个类继承自UIView;表情面板可以滑动,so有一个UIScrollView;表情面板下方有一个UIPageControl页面控制器;最重要的是,有自定义的表情视图;再定义一个一个带block的初始化方法:
@interface EmotionView : UIView<UIScrollViewDelegate>
{
    FaceView *_faceView;//表情视图
    UIScrollView *_scrollView;//滑动视图
    UIPageControl *_pageCtrl;//页面控制器

}
- (instancetype)initWithBlock:(SelectedBlock) block;
  • 2、初始化: 在initWithBlock的初始化方法中调用block,注意,调用self的initWithFrame方法,初始化views,注意,除了一些有固定坐标和宽高的控件之外的控件,最好在初始化的时候CGRECTZero,然后在需要的地方另外赋值frame,这样如果有隐藏的需求时,也比较方便。
-(instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        //初始化视图
        [self initViews];
    }
    return self;
}
- (instancetype)initWithBlock:(SelectedBlock) block
{
    self = [self initWithFrame:CGRectZero];
    if (self) {
        //调用blcok
        _faceView.block = block;
    }
    return self;
}
- (void)initViews
{
    self.width = kScreenWidth;

    //初始化表情视图
    _faceView = [[FaceView alloc] initWithFrame:CGRectZero];
    _faceView.backgroundColor = [UIColor clearColor];

    //初始化滑动视图
    _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, _faceView.height)];
    _scrollView.backgroundColor = [UIColor clearColor];

    //创建代理
    _scrollView.delegate = self;

    //隐藏滑动条
    _scrollView.showsHorizontalScrollIndicator = NO;
    _scrollView.showsVerticalScrollIndicator = NO;

    //分页
    _scrollView.pagingEnabled = YES;
    _scrollView.bounces = NO;

    //设置内容视图
    _scrollView.contentSize = CGSizeMake(_faceView.width, 0);

    //不剪切
    _scrollView.clipsToBounds = NO;

    [_scrollView addSubview:_faceView];
    [self addSubview:_scrollView];

    //初始化pageCtrl
    _pageCtrl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, _scrollView.bottom, kScreenWidth, 20)];
    _pageCtrl.backgroundColor = [UIColor clearColor];

    _pageCtrl.numberOfPages = _faceView.width / kScreenWidth;
    //添加点击事件
    [_pageCtrl addTarget:self action:@selector(pageCtrlAction:) forControlEvents:UIControlEventValueChanged];
    [self addSubview:_pageCtrl];

    self.height = _scrollView.height + _pageCtrl.height;
}
  • 3、pageControl和FaceView的结合: 点击页面控制器,通过currentPage可以计算出表情视图的偏移量,两者相同即可;然后滑动表情视图,触发了协议方法 scrollViewDidScroll,让页面控制器的currentPage等于偏移量的x坐标 / 屏宽
- (void)pageCtrlAction:(UIPageControl *)pageCtrl
{
    NSInteger index = pageCtrl.currentPage;
//    [_scrollView setContentOffset:CGPointMake(index * kScreenWidth, 0) animated:YES];
    _scrollView.contentOffset = CGPointMake(index * kScreenWidth, 0);                               
}

//视图滑动了
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    _pageCtrl.currentPage = _scrollView.contentOffset.x / kScreenWidth;
}

3、把表情面板嵌入到输入法的键盘中

思路
  • 点击表情面板的按钮,判断按钮的选中状态,根据选中状态,设置TextView的inputView属性是否为封装好的表情视图。在自己封装的block块中直接insertText即可插入选中的表情啦!

注意:
1、有以下情况,如果调用了表情视图,但是放弃发微博了,直接退出模态视图,那么会造成循环引用。
解决方案:创建一个 __weak属性的控制器,在block中再转为strong类型的,然后让控制器指向的输入框insertText就解决了!
2、记得reloadInputView啊!有始有终,有借有还那!~_~

//创建表情面板
        //这里会造成循环引用,
        //self --> _WriteTV
        //self --> _emotionView --> block --> _writeTV
        if (_emotionView == nil) {

            __weak WriteWeiboViewController *weakWriteVC = self;

            _emotionView = [[EmotionView alloc] initWithBlock:^(NSString *faceName) {

                //转为strong,可省略strong
                WriteWeiboViewController *writeVC = weakWriteVC;
//                NSLog(@"%@", faceName);
                if (faceName.length != 0) {
                    [writeVC->_writeTV insertText:faceName];
                }

            }];

        }

        btn.selected = !btn.selected;
        if (btn.selected) {
            _writeTV.inputView = _emotionView;
        }else {
            _writeTV.inputView = nil;
        }

        [_writeTV reloadInputViews];
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值