eoji表情键盘 ios
2016-07-20 13:39:20 qq_33717525 阅读数 269

一、关于emoji表情

        随着iOS系统版本的升级,对原生emoji表情的支持也越来越丰富。emoji表情是unicode码中为表情符号设计的一组编码,当然,还有独立于unicode的另一套编码SBUnicode,在OS系统中,这两种编码都有很好的支持。UI系统会自动帮我们将编码转义成表情符号,例如用SBUnicode如下代码:

  UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
    label.font = [UIFont systemFontOfSize:25];
    label.text = @"\uE056";
    [self.view addSubview:label];

就会在屏幕上出现一个笑脸:

      

二、开发表情键盘的思路

        首先为了实现跨平台,无论iOS端,andorid端还是web端,都要有一个相同的标准,这个标准就可以是国际Unicode编码,我们的思路是将表情文字进行unicode编码后再进行传输,因此,有两中方式,一种是通过自定义一套表情切图,将其与unicode码一一对应,在转码的时候,我们一一遍历,转换成unicode后进行传输,这样的好处是我们可以保证所有平台所能使用的表情统一。在iOS端,可以有另一种方式,通过上面我们知道,通过SBUnicode码我们可以在客户端显示表情符号,并且这个码的排列是十分有规律的,通过这个特点,我们可以通过遍历SBUnicode码的范围进行表情的创建,省去的图片素材的麻烦。

        iOS中可用的表情unicode范围是:0xE001~0xE05A,0xE101~0xE15A,

0xE201~0xE253,0xE401~0xE44C,0xE501~0xE537。

        我们可以通过遍历的方法,将其都加入数据源数组中:

int emojiRangeArray[10] = {0xE001,0xE05A,0xE101,0xE15A,0xE201,0xE253,0xE401,0xE44C,0xE501,0xE537};
    for (int j = 0 ; j<10 ; j+=2 ) {
        
        int startIndex = emojiRangeArray[j];
        int endIndex = emojiRangeArray[j+1];
        
        for (int i = startIndex ; i<= endIndex ; i++ ) {
        //添加到数据源数组
            [dataArray addObject:[NSString stringWithFormat:@"%C", (unichar)i]];
        }
    }

键盘的摆放,可以通过collectionView来做,十分方便:

    //为了摆放分页控制器,创建一个背景view
    bgView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 200)];
    //分页控制器
    pageControlBottom = [[UIPageControl alloc]initWithFrame:CGRectMake(0, 170, [UIScreen mainScreen].bounds.size.width, 20)];
    [bgView addSubview:pageControlBottom];
    //collectionView布局
    UICollectionViewFlowLayout * layout = [[UICollectionViewFlowLayout alloc]init];
    //水平布局
    layout.scrollDirection=UICollectionViewScrollDirectionHorizontal;
    //设置每个表情按钮的大小为30*30
    layout.itemSize=CGSizeMake(30, 30);
    //计算每个分区的左右边距
    float xOffset = (kscreenWidth-7*30-10*6)/2;
    //设置分区的内容偏移
    layout.sectionInset=UIEdgeInsetsMake(10, xOffset, 10, xOffset);
    scrollView = [[UICollectionView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 160) collectionViewLayout:layout];
    //打开分页效果
    scrollView.pagingEnabled = YES;
    //设置行列间距
    layout.minimumLineSpacing=10;
    layout.minimumInteritemSpacing=5;
    
    scrollView.delegate=self;
    scrollView.dataSource=self;
    scrollView.backgroundColor = bgView.backgroundColor;
    [bgView addSubview:scrollView];

在collectionView的回调方法中,处理如下:

//每页28个表情
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    if (((dataArray.count/28)+(dataArray.count%28==0?0:1))!=section+1) {
         return 28;
    }else{
        return dataArray.count-28*((dataArray.count/28)+(dataArray.count%28==0?0:1)-1);
    }
   
}
//返回页数
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return (dataArray.count/28)+(dataArray.count%28==0?0:1);
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"biaoqing" forIndexPath:indexPath];
    for (int i=cell.contentView.subviews.count; i>0; i--) {
        [cell.contentView.subviews[i-1] removeFromSuperview];
    }
    UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 30, 30)];
    label.font = [UIFont systemFontOfSize:25];
    label.text =dataArray[indexPath.row+indexPath.section*28] ;
   
    
    [cell.contentView addSubview:label];
    return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    NSString * str = dataArray[indexPath.section*28+indexPath.row];
    //这里手动将表情符号添加到textField上
    
}
//翻页后对分页控制器进行更新
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
    CGFloat contenOffset = scrollView.contentOffset.x;
    int page = contenOffset/scrollView.frame.size.width+((int)contenOffset%(int)scrollView.frame.size.width==0?0:1);
    pageControlBottom.currentPage = page;

}

三、切换系统键盘和自定义的表情键盘

        UITextField和UITextView都会有下面这个属性和方法:

@property (nullable, readwrite, strong) UIView *inputView;   
- (void)reloadInputViews;

inputView我们可以设置textView和textField成为第一响应时的弹出附件,如果我们不设置或者设置为nil,则会弹出系统键盘,reloadInputView方法可以使我们刷新这个附件视图,通过这两个,我们可以非常轻松的实现键盘的切换,比如我们在一个出发方法中如下处理:

-(void)imageViewTap{
    if (![_publishContent isFirstResponder]) {
        return;
    }
    if (isEmoji==NO) {
        isEmoji=YES;
        //呼出表情
        _textView.inputView=bgView;
        [_textView reloadInputViews];
    }else{
        isEmoji=NO;
        _textView.inputView=nil;
        [_textView reloadInputViews];
    }

    
}

效果如下:



追注:测试上面的SBUnicode码在模拟器上可以正常显示,真机并不能识别,可以通过将表情符全部添加到一个plist文件中,通过文件读取来创建键盘的方式进行真机上的开发。plist文件地址如下:


http://pan.baidu.com/s/1o6AdkBw

2015-11-17 14:17:22 dong_vip 阅读数 2069

iOS自定义的表情键盘

一、关于emoji表情

        随着iOS系统版本的升级,对原生emoji表情的支持也越来越丰富。emoji表情是unicode码中为表情符号设计的一组编码,当然,还有独立于unicode的另一套编码SBUnicode,在iOS系统中,这两种编码都有很好的支持。UI系统会自动帮我们将编码转义成表情符号,例如用SBUnicode如下代码:

  UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
    label.font = [UIFont systemFontOfSize:25];
    label.text = @"\uE056";
    [self.view addSubview:label];

就会在屏幕上出现一个笑脸:

      

二、开发表情键盘的思路

        首先为了实现跨平台,无论iOS端,andorid端还是web端,都要有一个相同的标准,这个标准就可以是国际Unicode编码,我们的思路是将表情文字进行unicode编码后再进行传输,因此,有两中方式,一种是通过自定义一套表情切图,将其与unicode码一一对应,在转码的时候,我们一一遍历,转换成unicode后进行传输,这样的好处是我们可以保证所有平台所能使用的表情统一。在iOS端,可以有另一种方式,通过上面我们知道,通过SBUnicode码我们可以在客户端显示表情符号,并且这个码的排列是十分有规律的,通过这个特点,我们可以通过遍历SBUnicode码的范围进行表情的创建,省去的图片素材的麻烦。

        iOS中可用的表情unicode范围是:0xE001~0xE05A,0xE101~0xE15A,

0xE201~0xE253,0xE401~0xE44C,0xE501~0xE537。

        我们可以通过遍历的方法,将其都加入数据源数组中:

int emojiRangeArray[10] = {0xE001,0xE05A,0xE101,0xE15A,0xE201,0xE253,0xE401,0xE44C,0xE501,0xE537};
    for (int j = 0 ; j<10 ; j+=2 ) {
        
        int startIndex = emojiRangeArray[j];
        int endIndex = emojiRangeArray[j+1];
        
        for (int i = startIndex ; i<= endIndex ; i++ ) {
        //添加到数据源数组
            [dataArray addObject:[NSString stringWithFormat:@"%C", (unichar)i]];
        }
    }

键盘的摆放,可以通过collectionView来做,十分方便:

    //为了摆放分页控制器,创建一个背景view
    bgView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 200)];
    //分页控制器
    pageControlBottom = [[UIPageControl alloc]initWithFrame:CGRectMake(0, 170, [UIScreen mainScreen].bounds.size.width, 20)];
    [bgView addSubview:pageControlBottom];
    //collectionView布局
    UICollectionViewFlowLayout * layout = [[UICollectionViewFlowLayout alloc]init];
    //水平布局
    layout.scrollDirection=UICollectionViewScrollDirectionHorizontal;
    //设置每个表情按钮的大小为30*30
    layout.itemSize=CGSizeMake(30, 30);
    //计算每个分区的左右边距
    float xOffset = (kscreenWidth-7*30-10*6)/2;
    //设置分区的内容偏移
    layout.sectionInset=UIEdgeInsetsMake(10, xOffset, 10, xOffset);
    scrollView = [[UICollectionView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 160) collectionViewLayout:layout];
    //打开分页效果
    scrollView.pagingEnabled = YES;
    //设置行列间距
    layout.minimumLineSpacing=10;
    layout.minimumInteritemSpacing=5;
    
    scrollView.delegate=self;
    scrollView.dataSource=self;
    scrollView.backgroundColor = bgView.backgroundColor;
    [bgView addSubview:scrollView];

在collectionView的回调方法中,处理如下:

//每页28个表情
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    if (((dataArray.count/28)+(dataArray.count%28==0?0:1))!=section+1) {
         return 28;
    }else{
        return dataArray.count-28*((dataArray.count/28)+(dataArray.count%28==0?0:1)-1);
    }
   
}
//返回页数
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return (dataArray.count/28)+(dataArray.count%28==0?0:1);
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"biaoqing" forIndexPath:indexPath];
    for (int i=cell.contentView.subviews.count; i>0; i--) {
        [cell.contentView.subviews[i-1] removeFromSuperview];
    }
    UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 30, 30)];
    label.font = [UIFont systemFontOfSize:25];
    label.text =dataArray[indexPath.row+indexPath.section*28] ;
   
    
    [cell.contentView addSubview:label];
    return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    NSString * str = dataArray[indexPath.section*28+indexPath.row];
    //这里手动将表情符号添加到textField上
    
}
//翻页后对分页控制器进行更新
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
    CGFloat contenOffset = scrollView.contentOffset.x;
    int page = contenOffset/scrollView.frame.size.width+((int)contenOffset%(int)scrollView.frame.size.width==0?0:1);
    pageControlBottom.currentPage = page;

}

三、切换系统键盘和自定义的表情键盘

        UITextField和UITextView都会有下面这个属性和方法:

@property (nullable, readwrite, strong) UIView *inputView;   
- (void)reloadInputViews;

inputView我们可以设置textView和textField成为第一响应时的弹出附件,如果我们不设置或者设置为nil,则会弹出系统键盘,reloadInputView方法可以使我们刷新这个附件视图,通过这两个,我们可以非常轻松的实现键盘的切换,比如我们在一个出发方法中如下处理:

-(void)imageViewTap{
    if (![_publishContent isFirstResponder]) {
        return;
    }
    if (isEmoji==NO) {
        isEmoji=YES;
        //呼出表情
        _textView.inputView=bgView;
        [_textView reloadInputViews];
    }else{
        isEmoji=NO;
        _textView.inputView=nil;
        [_textView reloadInputViews];
    }

    
}

效果如下:

           

2016-05-31 15:53:00 weixin_34032827 阅读数 19

具体思路

  1. 通过UIKeyboardWillChangeFrameNotification通知,监听键盘的改变,同时可以得到键盘的Frame和动画的持续时间,
  2. 新建键盘顶部工具条YSComposeToolBar,默认在底部,Y值随着键盘的改变而改变,会一直显示在键盘的最上面,动画持续时间使用步骤一通知得到的时间
  3. 新建一个存放表情的YSEmoticonKeyboard(由YSEmoticonListView + YSEmoticonTabBar)整体View,替换掉原生的键盘。其中YSEmoticonListView指的是表情列表,YSEmoticonTabBar指的是表情类型,如:浪小花、默认表情、QQ表情等。
  4. YSEmoticonListView由UIScrollView+UIPageController 组成。UIScrollView存放着由YSEmoticonPageView装着很多表情(Button)的列表组成。
  5. 所有的界面控制操作,都封装在YSComposeViewController的控制器中里。

部分代码说明

1.键盘通知

打印一下键盘的UIKeyboardWillChangeFrameNotification通知,我们可以得到键盘的Frame和动画的持续时间。

{name = UIKeyboardWillChangeFrameNotification; userInfo = {
    UIKeyboardAnimationCurveUserInfoKey = 7;
    UIKeyboardAnimationDurationUserInfoKey = 0;
    UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 252}}";
    UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 441.5}";
    UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 442}";
    UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 315}, {320, 253}}";
    UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 316}, {320, 252}}";
    UIKeyboardIsLocalUserInfoKey = 1;
}}

键盘的Frame和动画的持续时间

    NSDictionary *userInfo = notification.userInfo;
    // 动画的持续时间
    double duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    // 键盘的frame
    CGRect keyboardF = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];

2.替换掉原来的键盘

self.textView.inputView == nil : 使用的是系统自带的键盘

self.textView.inputView = self.emoticonKeyboard; 指的是用表情view替换掉原来的键盘

懒加载一个View

#pragma mark - 懒加载
- (YSEmoticonKeyboard *)emoticonKeyboard
{
    if (!_emoticonKeyboard) {
        self.emoticonKeyboard = [[YSEmoticonKeyboard alloc] init];
        // 键盘的宽度
        self.emoticonKeyboard.width = self.view.width;
        self.emoticonKeyboard.height = 216;
    }
    return _emoticonKeyboard;
}

替换掉原来的键盘

/**
 *  切换键盘
 */
- (void)switchKeyboard
{
    // self.textView.inputView == nil : 使用的是系统自带的键盘
    if (self.textView.inputView == nil) { // 切换为自定义的表情键盘
        self.textView.inputView = self.emoticonKeyboard;
        // 显示键盘按钮
        self.toolbar.showKeyboardButton = YES;
    } else { // 切换为系统自带的键盘
        self.textView.inputView = nil;
        
        // 显示表情按钮
        self.toolbar.showKeyboardButton = NO;
    }
    
    // 开始切换键盘
    self.switchingKeybaord = YES;
    
    // 退出键盘
    [self.textView endEditing:YES];
    
    // 结束切换键盘
    self.switchingKeybaord = NO;
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 弹出键盘
        [self.textView becomeFirstResponder];
    });
}

 

项目结构

1.YSComposeViewController :发微博Controller

  YSComposeToolbar:键盘顶部的工具条

 

YSEmoticonKeyboard:表情键盘(整体): YSEmoticonListView + YSEmoticonTabBar

YSEmoticonListView:表情列表,由YSEmoticonPageView + 分页

YSEmonticonTabBar:表情底部TabBar,切换不同的表情

 

2.YSEmoticonListView结构,上面由一个YSEmoticonPageView,加载一页的列表,有多少页表情列表,就有多少个pageView。底部是一个分页控件UIPageControl。

 

源代码下载地址:https://github.com/jiangys/ChatKeyboard

 

2016-05-10 21:57:00 weixin_33774883 阅读数 2

GitHub下载

废话

在做表情键盘时,很多时候为了使得各个平台的表情得到统一(或者是对表情的扩展等等),因此采用自定义的图片表情,而非系统自带的表情。
前段时间刚好遇到这样的需求, 因此打算动手写一个(造轮子), 粗糙的轮子成形之后,决定从以下几个方面入手,做点小笔记。

先上图 :





正文

<h3 id="0">1. 表情建模</h3>
表情建模,个人感觉是非常非常有必要的(毕竟一个表情包含的不仅仅是图片),表情大概具有以下属性:

  1. ID (对应的表情包id)
  2. image (对应的图片)
  3. code (十六进制编码, 如:系统表情)
  4. isDelete (是不是删除按钮)
    当然,还有表情包的模型,不再赘述。

<h3 id="1">2. 自定义图片表情的排版</h3>
表情布局方式很多,比如:UIScrollView,UICollectionView等等,这里选择后者,别问为什么,两个字: 简单
但是:在设置了scrollDirection = .Horizontal之后,UICollectionViewCell的布局就变成了从上到下,从左到右的形式。如图:


这样就会导致,在分页滚动时, 最后一页可能出现半页的情况,既然已经知道了问题是怎么导致的,那么解决这样的问题也就简单了,只需要重写 UICollectionViewLayoutUICollectionViewCell 重新布局便可。

代码如下:

class EmojiLayout: UICollectionViewLayout {
    // 保存所有item属性
    private var attributes: [UICollectionViewLayoutAttributes] = []
    // screen
    private let mainRect = UIScreen.mainScreen().bounds
    // section
    private var sections: Int = 0
    // item
    private var items: Int = 0
    // column
    var maxColumn: CGFloat = 0
    // row
    var maxRow: CGFloat = 0
    // margin
    var margin: CGFloat = 0

    // MARK: - 允许重新布局
    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
        return true
    }

    // MARK: - 重新布局
    override func prepareLayout() {
        super.prepareLayout()
        
        attributes.removeAll()
        
        // 根据设置的Column Row, 计算得到每个item的大小
        let itemsize = getItemSize(maxColumn, row: maxRow, margin: margin)
        // 获取组数
        sections = self.collectionView?.numberOfSections() ?? 0
        // 遍历每组里面的所有item
        for section in 0 ..< sections {
            items = self.collectionView?.numberOfItemsInSection(section) ?? 0
            // 遍历每一个item
            for item in 0 ..< items {
                // 根据 section, item 获取每一个item的indexPath值
                let indexPath = NSIndexPath(forItem: item, inSection: section)
                // 根据indexPath值, 获取每一个item的属性
                let attribute = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
                // 通过一系列脑残计算, 得到x, y值
                let x = margin + (itemsize.width + margin) * (CGFloat(item) % maxColumn) + (CGFloat(section) * mainRect.width)
                let y = margin + (itemsize.height + margin) * CGFloat(item / Int(maxColumn))
                attribute.frame = CGRect(x: x, y: y, width: itemsize.width, height: itemsize.height)
                // 把每一个新的属性保存起来
                attributes.append(attribute)
            }
        }
    }

    // MARK: - 返回当前可见的
    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var rectAttributes: [UICollectionViewLayoutAttributes] = []
        // 遍历所有属性, 返回当前处于可见区域的item(在屏幕可见的item)
        let _ = attributes.map {
            if CGRectContainsRect(rect, $0.frame) {
                rectAttributes.append($0)
            }
        }
        return rectAttributes
    }

    // MARK: - 返回大小
    override func collectionViewContentSize() -> CGSize {
        let itemsize = getItemSize(maxColumn, row: maxRow, margin: margin)
        return CGSize(width: CGFloat(sections) * mainRect.width, height: margin + (maxRow * (itemsize.height + margin)))
    }

    // MARK: - itemSize
    // 为了使得表情不变形, 因此 height = width
    private func getItemSize(column: CGFloat, row: CGFloat, margin: CGFloat) -> CGSize {
        let width = (mainRect.width - ((column + 1) * margin)) / column
        return CGSize(width: width, height: width)
    }
}

最后结果,如图:



没错,就是这么简单。
表情展示的时候,本人的做法:表情页(每个section),通过计算得到,表情包能分成多少个section。切换表情包的时候,修改collectionView的datasource,然后reloadData
当然,做法不是唯一的。

<h3 id="2">3. 在UITextView中插入表情</h3>
UITextView中插入表情,分为2种情况:系统表情图片表情
1)系统表情
系统表情,其实就是字符串(个人感觉是字符串,不懂这样理解对否),因此不需要我们做太多的操作,可以直接给UITextViewtext属性赋值。
2)图片表情
图片表情插入到 UITextView,可以通过 attributedText 属性进行设置(这几乎是最简单的方式法了)
主要2点:
a. 为了使用方便,直接给 UITextView 扩展方法 func insertEmoji(emojiModel: EmojiModel)
b. 继承 NSTextAttachment 添加一个属性 emojiTag 用于记录该表情对应的字符串

具体操作如下:

extension UITextView {
    /// 插入表情
    ///
    /// - parameter emojiModel: 表情模型
    func insertEmoji(emojiModel: EmojiModel) {
        // 删除按钮
        if emojiModel.deleteBtn {
            deleteBackward()
            return
        }
        // 系统表情
        if let emoji = emojiModel.emoji {
            insertText(emoji)
        }
        // png 表情
        if let pngImage = emojiModel.pngImage {
            // 创建附件
            let attachment = EmojiTextAttachment()
            // 设置图片
            attachment.image = pngImage
            // 设置图片标志(这里设置图片标志,主要是为了:表情转换字符串时 操作更简单)
            attachment.emojiTag = emojiModel.chs
            // 设置附件大小 (表情跟文本的大小一致)
            attachment.bounds = CGRect(x: 0, y: -4, width: font!.lineHeight, height: font!.lineHeight)

            // 带属性的文本, 把图片设置进去
            let attritubeString = NSMutableAttributedString(attributedString: NSAttributedString(attachment: attachment))
            // 设置字体
            attritubeString.addAttribute(NSFontAttributeName, value: font!, range: NSRange(location: 0, length: 1))

            // 获取原来的文本, 替换为现在的文本, 再给textView的属性文本赋值
            let att = NSMutableAttributedString(attributedString: attributedText)
            att.replaceCharactersInRange(self.selectedRange, withAttributedString: attritubeString)
            attributedText = att

            // 移动光标
            selectedRange.location += 1

            // 重写通知, 代理方法
            NSNotificationCenter.defaultCenter().postNotificationName(UITextViewTextDidChangeNotification, object: self)
            delegate?.textViewDidChange!(self)
        }
    }
}

<h3 id="3">4. 表情转换字符串</h3>
由于服务器无法识别 NSAttributedString 这样的属性文本,因此有必要把表情转换成对应的字符串。
既然图片表情是通过 NSAttributedString 进行设置的,那么同样的道理,我们可以再通过遍历属性,找到图片表情对应的字符串。

如下:

extension NSAttributedString {
    // 遍历属性, 获取字符串
    func getPlainString() -> String {
        let plainStr = NSMutableString(string: self.string)
        var base: Int = 0
        self.enumerateAttribute(NSAttachmentAttributeName, inRange: NSMakeRange(0, self.length), options: []) { (value, range, stop) -> Void in
            if let value = value as? EmojiTextAttachment {
                if let emojiTag = value.emojiTag {
                    plainStr.replaceCharactersInRange(NSMakeRange(range.location + base, range.length), withString: emojiTag)
                    base += emojiTag.characters.count - 1
                }
            }
        }
        return plainStr as String
    }
}

<h3 id="4">5. 字符串转换表情</h3>
最后,可能有人觉得很奇怪,为什么要把表情转换成字符串,然后字符串又要转换成表情?
其实,这两者并不冲突,也不是多此一举,而是很有必要的操作,为啥呢?
如图:


字符串转换表情,本人的做法很low,并不建议这么做,倘若你也没有什么办法的话,那就勉强看一下吧(前提是,你的表情对应的字符串是这样的:[哈哈] [玫瑰] [爱心])。
直接上代码:

/// 符合"标准"的字符串
struct StringProperty {
    /// 字符串
    var value: String
    /// 位置
    var range: NSRange

    init(value: String, range: NSRange) {
        self.value = value
        self.range = range
    }
}

extension String {
    /// 字符串查找, 返回以start开始 且 以end结尾的字符串
    ///
    /// - parameter start: 开始字符串
    /// - parameter end:   结束字符串
    ///
    /// - returns: 如果存在, 返回 StringProperty 对象, 否则返回nil
    func between(start: String, _ end: String) -> StringProperty? {
        var range = NSRange()
        var flag: Bool = false
        let string = self as NSString

        for var i = 0; i < string.length; i += 1 {
            let subStr = string.substringWithRange(NSRange(location: i, length: 1))
            if start == subStr {
                range.location = i
                flag = true
                continue
            } else if flag && subStr == end {
                flag = false
                range.length = i - range.location - 1
                let strRange = NSRange(location: range.location, length: range.length + start.characters.count + end.characters.count)
                let value = string.substringWithRange(strRange)
                return StringProperty(value: value, range: strRange)
            }
        }
        return nil
    }
}

通过func between(start: String, _ end: String) -> StringProperty?方法可以获取到“有可能是表情的字符串”,接下来就是要确定这个字符串是不是表情。本人也是用了很low的办 —— “遍历表情包”
具体做法,可以看demo中的EmojiPackageManager.swift文件下的class func verificationEmojiWithString(string: String) -> EmojiModel?方法。

转载于:https://www.jianshu.com/p/e88857fa3f95

2017-03-02 17:46:07 create_pro 阅读数 319

直接上代码,需要自备表情包表情命名为”ee_1”–“ee_105” 表情按钮图片。后面有项目链接
这里写图片描述
这里写图片描述这里写图片描述

#import "ViewController.h"

#define WIDTH [UIScreen mainScreen].bounds.size.width

#define HEIGHT [UIScreen mainScreen].bounds.size.height

#define WINDOW [UIApplication sharedApplication].keyWindow

#define kColor(x,y,z) [UIColor colorWithRed:x/255.0 green:y/255.0 blue:z/255.0 alpha:1]

@interface ViewController ()@property (nonatomic,strong)UILabel * textLab;

@property (nonatomic,strong)UIScrollView * scrollView;

@property (nonatomic,strong)UIPageControl * scrollPage;

@property (nonatomic,copy)NSMutableString * attriString;

@property (nonatomic,strong)UIView * editView;

@property (nonatomic,strong)UITextView * textView;

@end

@implementation ViewController

- (void)viewDidLoad {  

  [super viewDidLoad]; 

   self.view.backgroundColor = [UIColor whiteColor];   

    _attriString = [[NSMutableString alloc]init];        

[self createUI];        

}

-(void)createUI{            

_textLab = [[UILabel alloc]initWithFrame:CGRectMake(20, 20, WIDTH-40, 200)];    _textLab.font = [UIFont systemFontOfSize:15];    _textLab.layer.borderWidth = 2;    _textLab.layer.borderColor = [UIColor blackColor].CGColor;  

_textLab.textAlignment = NSTextAlignmentCenter;                

_editView = [[UIView alloc]initWithFrame:CGRectMake(0, HEIGHT-49, WIDTH, 49)];    _editView.backgroundColor = [UIColor whiteColor];       

 UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];    

button.frame = CGRectMake(10, 0, 49, 49);    

[button setImage:[UIImage imageNamed:@"expression"] forState:UIControlStateNormal];        

[button addTarget:self action:@selector(showEmojiView) forControlEvents:UIControlEventTouchUpInside];       

 [_editView addSubview:button];        

_textView = [[UITextView alloc]initWithFrame:CGRectMake(69, 5, WIDTH-129, 39)];    _textView.delegate = self;   

 _textView.backgroundColor = kColor(238, 238, 238);    

_textView.layer.cornerRadius = 3;    

_textView.clipsToBounds = YES;           

 [_editView addSubview:_textView];        

UIButton * sendButton = [UIButton buttonWithType:UIButtonTypeCustom];    sendButton.frame = CGRectMake(WIDTH-59, 0, 49, 49);        

[sendButton setTitle:@"发送" forState:UIControlStateNormal];    

[sendButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];    [sendButton addTarget:self action:@selector(sendBtn) forControlEvents:UIControlEventTouchUpInside];        

[_editView addSubview:sendButton];                    

[self.view addSubview:_textLab];    

[self.view addSubview:_editView];                

UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapClick:)];   

 [self.view addGestureRecognizer:tap];    

}

#pragma mark 模仿发送按钮-解析表情展示到label

-(void)sendBtn{            

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@",_attriString]];        

NSString * rugxString = @"\\[\\{e:([1-9]|[1-9][0-9]|[1][0-9][0-5])\\}\\]";        NSRegularExpression * re = [NSRegularExpression regularExpressionWithPattern:rugxString options:NSRegularExpressionCaseInsensitive error:nil];       

 NSArray * resultArray = [re matchesInString:_attriString options:0 range:NSMakeRange(0, _attriString.length)];        

NSMutableArray * RangeArray = [NSMutableArray arrayWithCapacity:resultArray.count];        

for (NSTextCheckingResult * result in resultArray) {                

NSRange range = [result range];                

NSString * string = [_attriString substringWithRange:range];                

for (int i = 1; i<106; i++) {                        

if ([[NSString stringWithFormat:@"[{e:%d}]",i] isEqualToString:string]) {                                UIImage * image = [UIImage imageNamed:[NSString stringWithFormat:@"ee_%d.png",i]];                

NSTextAttachment *imageAttachment = [[NSTextAttachment alloc]init];                imageAttachment.image = image;                                

imageAttachment.bounds = CGRectMake(0, 0,_textLab.font.lineHeight, _textLab.font.lineHeight);                                

NSAttributedString *imageAttributedString = [NSAttributedString attributedStringWithAttachment:imageAttachment];                                

NSDictionary * imgDict = @{@"image":imageAttributedString,@"range":[NSValue valueWithRange:range]};                                

[RangeArray addObject:imgDict];                           

 }                    

}    

}       

 for (int i = (int)RangeArray.count-1; i>=0; i--) {                

NSRange range;        

[RangeArray[i][@"range"] getValue:&range];                

[attributedString replaceCharactersInRange:range withAttributedString:RangeArray[i][@"image"]];            

}    

_textLab.attributedText = attributedString;        

}

#pragma mark 显示表情键盘

-(void)showEmojiView{       

 [_textView endEditing:YES];        

if (!_scrollView) {        

[self createExpressionView];   

 }    

[self changeEditViewFrameWithType:YES];        

_scrollView.hidden = NO;    

_scrollPage.hidden = NO;    

}

-(void)createExpressionView{   

 _scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, HEIGHT*860/1280, WIDTH, HEIGHT*420/1280-30)];    

_scrollView.backgroundColor = [UIColor whiteColor];    

_scrollView.contentSize = CGSizeMake(WIDTH*4,HEIGHT*420/1280-30);    _scrollView.showsHorizontalScrollIndicator = NO;    

_scrollView.pagingEnabled = YES;    

_scrollView.delegate = self;        

_scrollPage = [[UIPageControl alloc]initWithFrame:CGRectMake(0,CGRectGetMaxY(_scrollView.frame),WIDTH, 30)];    _scrollPage.backgroundColor = [UIColor whiteColor];    

_scrollPage.numberOfPages = 4;    

_scrollPage.pageIndicatorTintColor = [UIColor grayColor];    _scrollPage.currentPageIndicatorTintColor = kColor(64, 151, 222);    _scrollPage.currentPage = 0;        

[_scrollPage addTarget:self action:@selector(scrollPageClick:) forControlEvents:UIControlEventValueChanged];        

for (int i = 0; i<105; i++) {                

UIButton * emojiBtn = [UIButton buttonWithType:UIButtonTypeCustom];                emojiBtn.frame = CGRectMake(i%31%8*WIDTH/8+WIDTH*(int)(i/31), i%31%32/8*(HEIGHT*420/1280-30)/4, WIDTH/8, (HEIGHT*420/1280-30)/4);        emojiBtn.backgroundColor = [UIColor clearColor];       

 [emojiBtn setImage:[UIImage imageNamed:[NSString stringWithFormat:@"ee_%d",i+1]] forState:UIControlStateNormal];                

[emojiBtn setImageEdgeInsets:UIEdgeInsetsMake((emojiBtn.frame.size.height-HEIGHT*33/1280)/4, (WIDTH/8-WIDTH*33/720)/4, (emojiBtn.frame.size.height-HEIGHT*33/1280)/4, (WIDTH/8-WIDTH*33/720)/4)];                

emojiBtn.tag = 200+i;       

 [emojiBtn addTarget:self action:@selector(emojiBtnClick:) forControlEvents:UIControlEventTouchUpInside];       

 [_scrollView addSubview:emojiBtn];                                        

if ( i % 31 == 0) {            

UIButton * delButton = [UIButton buttonWithType:UIButtonTypeCustom];                        delButton.frame = CGRectMake(7*WIDTH/8+WIDTH*(int)(i/31), 3*(HEIGHT*420/1280-30)/4, WIDTH/8, (HEIGHT*420/1280-30)/4);            

delButton.backgroundColor = [UIColor clearColor];           

 [delButton setImage:[UIImage imageNamed:@"Expression_Del"] forState:UIControlStateNormal];            

[delButton addTarget:self action:@selector(delButtonClick:) forControlEvents:UIControlEventTouchUpInside];           

 [_scrollView addSubview:delButton];      

  }            

}           

 _scrollView.hidden = YES;    

_scrollPage.hidden = YES;    

[WINDOW addSubview:_scrollView];    

[WINDOW addSubview:_scrollPage];

}

#pragma mark        emojiBtnClick按钮

-(void)emojiBtnClick:(UIButton *)emojiBtn{        

[_attriString appendFormat:@"[{e:%d}]",(int)emojiBtn.tag-199];        NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:_attriString];    

NSString * rugxString = @"\\[\\{e:([1-9]|[1-9][0-9]|[1][0-9][0-5])\\}\\]";    NSRegularExpression * re = [NSRegularExpression regularExpressionWithPattern:rugxString options:NSRegularExpressionCaseInsensitive error:nil];    

NSArray * resultArray = [re matchesInString:_attriString options:0 range:NSMakeRange(0, _attriString.length)];    

NSMutableArray * RangeArray = [NSMutableArray arrayWithCapacity:resultArray.count];        

for (NSTextCheckingResult * result in resultArray) {        

NSRange range = [result range];       

 NSString * string = [_attriString substringWithRange:range];        

for (int i = 1; i<106; i++) {            

if ([[NSString stringWithFormat:@"[{e:%d}]",i] isEqualToString:string]) {                                UIImage * image = [UIImage imageNamed:[NSString stringWithFormat:@"ee_%d.png",i]];                

NSTextAttachment *imageAttachment = [[NSTextAttachment alloc]init];                imageAttachment.image = image;                

imageAttachment.bounds = CGRectMake(0, 0,17.9, 17.9);                

NSAttributedString *imageAttributedString = [NSAttributedString attributedStringWithAttachment:imageAttachment];                

NSDictionary * imgDict = @{@"image":imageAttributedString,@"range":[NSValue valueWithRange:range]};                

[RangeArray addObject:imgDict];                           

 }        

}    

}   

 [attributedString setAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15]} range:NSMakeRange(0, attributedString.length)];        

for (int i = (int)RangeArray.count-1; i>=0; i--) {                

NSRange range;       

 [RangeArray[i][@"range"] getValue:&range];               

 [attributedString replaceCharactersInRange:range withAttributedString:RangeArray[i][@"image"]];    }        

_textView.attributedText = attributedString;    

}

#pragma mark        delButtonClick按钮

-(void)delButtonClick:(UIButton *)delBtn{        

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:_attriString];    

NSString * rugxString = @"\\[\\{e:([1-9]|[1-9][0-9]|[1][0-9][0-5])\\}\\]";    NSRegularExpression * re = [NSRegularExpression regularExpressionWithPattern:rugxString options:NSRegularExpressionCaseInsensitive error:nil];    

NSArray * resultArray = [re matchesInString:_attriString options:0 range:NSMakeRange(0, _attriString.length)];    

NSMutableArray * RangeArray = [NSMutableArray arrayWithCapacity:resultArray.count];        

for (NSTextCheckingResult * result in resultArray) {        

NSRange range = [result range];        

NSString * string = [_attriString substringWithRange:range];        

for (int i = 1; i<106; i++) {            

if ([[NSString stringWithFormat:@"[{e:%d}]",i] isEqualToString:string]) {                                UIImage * image = [UIImage imageNamed:[NSString stringWithFormat:@"ee_%d.png",i]];                

NSTextAttachment *imageAttachment = [[NSTextAttachment alloc]init];                imageAttachment.image = image;               

 imageAttachment.bounds = CGRectMake(0, 0,17.9, 17.9);                

NSAttributedString *imageAttributedString = [NSAttributedString attributedStringWithAttachment:imageAttachment];                

NSDictionary * imgDict = @{@"image":imageAttributedString,@"range":[NSValue valueWithRange:range]};               

 [RangeArray addObject:imgDict]; 

                           }        

}    

}   

 [attributedString setAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15]} range:NSMakeRange(0, attributedString.length)];        

for (int i = (int)RangeArray.count-1; i>=0; i--) {               

 NSRange range;       

 [RangeArray[i][@"range"] getValue:&range];        

if (i == RangeArray.count-1) {            

[attributedString deleteCharactersInRange:range];           

 [_attriString replaceCharactersInRange:range withString:@""];        

}else{            

[attributedString replaceCharactersInRange:range withAttributedString:RangeArray[i][@"image"]];       

 }   

 }   

 _textView.attributedText = attributedString;    

}

#pragma  mark scrollViewPage按钮

-(void)scrollPageClick:(UIPageControl *)scrollPage{        

[_scrollView setContentOffset:CGPointMake(WIDTH*scrollPage.currentPage, 0)];    }

#pragma mark scrollViewDelegate

-(void)scrollViewDidScroll:(UIScrollView *)scrollView{    

_scrollPage.currentPage = scrollView.contentOffset.x/WIDTH;

}

#pragma mark textviewDelegate

-(void)textViewDidBeginEditing:(UITextView *)textView{       

 [self changeEditViewFrameWithType:YES];        

_scrollPage.hidden = YES;    

_scrollView.hidden = YES;    

}

-(void)textViewDidChange:(UITextView *)textView{        

if ([_attriString isEqualToString:textView.text]) {    

}else{ 

               NSString * rugxString = @"\\[\\{e:([1-9]|[1-9][0-9]|[1][0-9][0-5])\\}\\]";        NSRegularExpression * re = [NSRegularExpression regularExpressionWithPattern:rugxString options:NSRegularExpressionCaseInsensitive error:nil];       

 NSArray * resultArray = [re matchesInString:_attriString options:0 range:NSMakeRange(0, _attriString.length)];                

NSMutableArray * RangeArray = [NSMutableArray arrayWithCapacity:resultArray.count];        

for (NSTextCheckingResult * result in resultArray) {           

 NSRange AttRange = [result range];            

NSString * string = [_attriString substringWithRange:AttRange];            

for (int i = 1; i<106; i++) {                

if ([[NSString stringWithFormat:@"[{e:%d}]",i] isEqualToString:string]) {                    [RangeArray addObject:[NSValue valueWithRange:AttRange]];                

}           

 }       

 }               

 NSMutableArray * textArr = [[NSMutableArray alloc]init];        

for (int i = (int)RangeArray.count-1; i>=0; i--) {            

NSRange AttRange;            

[RangeArray[i] getValue:&AttRange];           

 [textArr addObject:[_attriString substringWithRange:AttRange]];         

   [_attriString replaceCharactersInRange:AttRange withString:@" "];          

  for (int y = 0; y= 0 ; i--) {                    

    NSRange attRange;             

           [RangeArray[i] getValue:&attRange];          

              [_attriString replaceCharactersInRange:attRange withString:textArr[y]];                        y++;        }            }    }

-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{      

  NSString * rugxString = @"\\[\\{e:([1-9]|[1-9][0-9]|[1][0-9][0-5])\\}\\]";    NSRegularExpression * re = [NSRegularExpression regularExpressionWithPattern:rugxString options:NSRegularExpressionCaseInsensitive error:nil]; 

   NSArray * resultArray = [re matchesInString:_attriString options:0 range:NSMakeRange(0, _attriString.length)];    

    NSMutableArray * RangeArray = [NSMutableArray arrayWithCapacity:resultArray.count];   

 for (NSTextCheckingResult * result in resultArray) {     

   NSRange AttRange = [result range];       

 NSString * string = [_attriString substringWithRange:AttRange];   

     for (int i = 1; i<106; i++) {          

  if ([[NSString stringWithFormat:@"[{e:%d}]",i] isEqualToString:string]) {                [RangeArray addObject:[NSValue valueWithRange:AttRange]];    

        }    

    }   

 }  

  NSMutableArray * textArr = [[NSMutableArray alloc]init];    

for (int i = (int)RangeArray.count-1; i>=0; i--) {      

  NSRange AttRange;     

   [RangeArray[i] getValue:&AttRange];     

   [textArr addObject:[_attriString substringWithRange:AttRange]];   

     [_attriString replaceCharactersInRange:AttRange withString:@" "];        AttRange.length = 1;     

   for (int y = 0; y= 0 ; i--) {

NSRange attRange;

[RangeArray[i] getValue:&attRange];

if (attRange.location>range.location) {

attRange.location += range.length;

}

if (range.location == attRange.location && range.length == attRange.length) {

}else{

[_attriString replaceCharactersInRange:attRange withString:textArr[y]];

}

y++;

}

return YES;

}

-(void)tapClick:(UITapGestureRecognizer *)tap{

[_textView endEditing:YES];

[self changeEditViewFrameWithType:NO];

_scrollView.hidden = YES;

_scrollPage.hidden = YES;

}

-(void)changeEditViewFrameWithType:(BOOL)up{

if (up) {

_editView.frame = CGRectMake(0, HEIGHT - 256 - 49 , WIDTH, 49);

}else{

_editView.frame = CGRectMake(0, HEIGHT - 49 , WIDTH, 49);

}

}

@end

附上地址:https://github.com/cwos111509sina/EmojiKeyBoard.git

ios表情键盘的实现

阅读数 122

没有更多推荐了,返回首页