天天看點

iOS 自定義鍵盤+表情布局實作

本節主要内容:

1.自定義鍵盤--将工具欄添加到鍵盤上,并實作切換
2.實作進入到系統相冊選取圖檔
3.實作表情布局
4.實作系統鍵盤和表情視圖之間的切換
5.表情消息的追加
           

一、*自定義鍵盤

建立子視圖,為系統鍵盤添加工具欄。例子中建立了textView和一個帶有5個button的編輯工具欄

#pragma mark - 建立子視圖
- (void)creactSubView{

    //1 添加_textView
    _textView = [[UITextView alloc] initWithFrame:CGRectMake(, , kScreenWidth, kScreenHeight-)];
    _textView.backgroundColor = [UIColor clearColor];
    _textView.font = [UIFont systemFontOfSize:];
    [_textView becomeFirstResponder];
    [self.view addSubview:_textView];

    //2 編輯欄
    _editorView = [[UIView alloc] initWithFrame:CGRectMake(, _textView.bottom, kScreenWidth, )];
    _editorView.backgroundColor = [UIColor clearColor];
    //***将_editorView添加到系統鍵盤上,利用下面的inputAccessoryView方法
    _textView.inputAccessoryView = _editorView;
    //添加5個按鈕
    NSArray *imgArr = @[
                        @"compose_toolbar_1.png",
                        @"compose_toolbar_3.png",
                        @"compose_toolbar_4.png",
                        @"compose_toolbar_5.png",
                        @"compose_toolbar_6.png"
                        ];
    CGFloat btnWidth = kScreenWidth/;

    for (int i = ; i<imgArr.count; i++) {
        ThemeButton *button = [[ThemeButton alloc] initWithFrame:CGRectMake(btnWidth*i, , btnWidth, )];

        button.imageName = imgArr[i];

        [button addTarget:self action:@selector(pressAction:) forControlEvents:UIControlEventTouchUpInside];
        button.tag = +i;

        [_editorView addSubview:button];
    }
}
           

二、*進入系統相冊選取圖檔

按鈕1的響應事件—實作進入系統相冊選取圖檔(單張)

switch (button.tag - ) {
        case :
            NSLog(@"點選第一個按鈕");
        {
            //由底部彈出的彈窗
            UIAlertController *alter = [UIAlertController alertControllerWithTitle:@"選取圖檔" message:nil preferredStyle:UIAlertControllerStyleActionSheet];

            UIAlertAction *action = [UIAlertAction actionWithTitle:@"相冊" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

                //選取相冊
                UIImagePickerController *picker = [[UIImagePickerController alloc] init];

                picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
                picker.delegate = self;
                //模态視圖 彈出相冊
                [self presentViewController:picker animated:YES completion:nil];
            }];

            [alter addAction:action];

            [self presentViewController:alter animated:YES completion:nil];

        }
            break;
           

三、*表情布局

要實作的效果:

所有表情分成幾頁,每頁按照4行7列進行排列,點選任意表情會出現該表情的放大鏡,放大鏡上有該表情和文字。能夠實作頁面的滑動。
           

思路:

1.資料處理

從plist檔案中取出所有檔案,将其存放進一個大的數組中,将這個大數組分割成28個資料一組的小數組,小數組的數量将會對應頁面的數量。而在每個小數組裡面,這28個資料将會按照4行7列進行排列。
           

2.頁面處理

由外至内視圖分别是:UIView->UIScrolleView->UIView->UIImageView(表情視圖和選中視圖)
           

代碼:

FaceView

.h檔案

#import <UIKit/UIKit.h>
#import "UIViewExt.h"

@interface FaceView : UIView
@property (nonatomic, strong) NSMutableArray *items;//表情大數組
@property (nonatomic, strong) UIImageView *selectImgView;//選中的放大鏡視圖
@property (nonatomic, assign) NSString *selectImgName;//選中的表情名字
@property (nonatomic, strong) UILabel *label;
@end
           

.m檔案

#import "FaceView.h"
#import "Header.h"

#define imgSize 30
@implementation FaceView

- (instancetype) initWithFrame:(CGRect)frame {

    if ([super initWithFrame:frame]) {

        [self loadPlist];
        _label = [[UILabel alloc] initWithFrame:CGRectMake(, , , )];
        _label.font = [UIFont systemFontOfSize:];
    }
    return self;
}

//整理表情
- (void) loadPlist {

    //擷取plist檔案
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"emoticons.plist" ofType:nil];
    //所有表情數組
    NSArray *array = [NSArray arrayWithContentsOfFile:filePath];
    //大數組
   _items = [NSMutableArray array];
    //小數組
    NSMutableArray *items2D = nil;
    for (int i = ; i < array.count; i++) {

        //建立小數組
        if (items2D == nil || items2D.count == ) {
            items2D = [NSMutableArray array];
            [_items addObject:items2D];
        }

        NSDictionary *faceDic = [array objectAtIndex:i];
        [items2D addObject:faceDic];
    }
    //内部寫死寬高
    self.width = kScreenWidth * _items.count;
    self.height =  * itemSize;
}

//繪制
- (void)drawRect:(CGRect)rect {
    //确定行和列
    CGFloat row = ;
    CGFloat culom = ;
    //拿表情
    for (int i = ; i < _items.count; i++) {

        NSMutableArray *item2D = _items[i];

        for (int j = ; j < item2D.count; j++) {

            NSDictionary *dic = item2D[j];
            //圖檔名字
            NSString *imageName = [dic objectForKey:@"png"];
            UIImage *img = [UIImage imageNamed:imageName];

            CGFloat x = culom * itemSize + (itemSize-imgSize)/ + kScreenWidth * i;
            CGFloat y = row * itemSize + (itemSize - imgSize)/;
            //繪制
            [img drawInRect:CGRectMake(x, y, imgSize, imgSize)];

            //更新行和列
            culom++;
            if (culom == ) {
                culom = ;
                row++;
            }
            if (row == ) {
                row = ;
            }   
        }
    }
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    //放大鏡不隐藏
    _selectImgView.hidden = NO;

    //禁止滑動
    if ([self.superview isKindOfClass:[UIScrollView class]]) {
        UIScrollView *scrollV = (UIScrollView *) self.superview;
        scrollV.scrollEnabled = NO;
    }

    //拿到觸摸的坐标
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];

    [self touchFace:point];


}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];

    [self touchFace:point];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    //隐藏放大鏡
    _selectImgView.hidden = YES;

    //開啟滑動
    if ([self.superview isKindOfClass:[UIScrollView class]]) {
        UIScrollView *scrollV = (UIScrollView *)self.superview;
        scrollV.scrollEnabled = YES;
    }

    //回調block

}

//尋找點選的表情
- (void) touchFace:(CGPoint) point {
    //1.目前頁
    NSInteger page = point.x/kScreenWidth;
    //容錯處理
    if (page>=_items.count || page < ) {
        return;
    }
    //确定小數組的索引
    /**根據這個計算
     CGFloat x = culom *itemSize + (itemSize-imgSize)/2 + kScreenWidth*i;
     CGFloat y = row *itemSize +(itemSize-imgSize)/2;
     */
    NSInteger culom = (point.x - ((itemSize-imgSize)/ + kScreenWidth*page))/itemSize;//列
    NSInteger row = (point.y - (itemSize-imgSize)/)/itemSize;//行
    //容錯處理
    if (culom >) culom =;
    if (culom <) culom =;
    if (row >) row =;
    if (row <) row =;

    //根據目前頁面坐标計算行和咧
    NSInteger index = row*+culom;
    //處理最後一頁數組越界
    if (page == _items.count-) {
        //通過頁面擷取小數組
        NSArray *item2D = _items[page];

        if (index>item2D.count-) {
            return;
        }
    }
    NSArray *item2D = _items[page];
    NSDictionary *faceDic = item2D[index];
    //表情圖檔名
    NSString *imgName = faceDic[@"png"];
    //表情中文名
    NSString *faceName = faceDic[@"chs"];


    if (![self.selectImgName isEqualToString:faceName]) {
        //确定放大鏡的位置
        CGFloat bottom = row *itemSize +itemSize/;
        CGFloat x = culom *itemSize + itemSize/ + page *kScreenWidth;
        self.selectImgView.center = CGPointMake(x, );
        self.selectImgView.bottom = bottom;

        //指派-選中的表情圖檔顯示
        UIImageView *imgView =[self.selectImgView viewWithTag:];
        imgView.image = [UIImage imageNamed:imgName];

        self.selectImgName = faceName;
         [_selectImgView addSubview:_label];

        //選中的表情名
        _label.text = _selectImgName;

    }

    //發送通知--參數:選中的圖檔名faceName
    NSDictionary *userInfo = @{@"imageName" : faceName};
    [[NSNotificationCenter defaultCenter] postNotificationName:@"imageNameNo"
                                                        object:nil
                                                      userInfo:userInfo];
}

//選中視圖
- (UIImageView *) selectImgView {

    if (_selectImgView == nil) {
        //建立選中視圖
        _selectImgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"emoticon_keyboard_magnifier.png"]];
        [_selectImgView sizeToFit];
        [self addSubview:_selectImgView];
        //選中視圖中的表情
        UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake((_selectImgView.width-imgSize)/, , imgSize, imgSize)];
        imgView.tag = ;
        [_selectImgView addSubview:imgView];
    }
    return _selectImgView;
}

@end
           

FaceFnel

.h檔案

#import <UIKit/UIKit.h>
#import "FaceView.h"

@interface FaceFnel : UIView <UIScrollViewDelegate>

@end
           

.m檔案

#import "FaceFnel.h"
#import "Header.h"

@implementation FaceFnel
/**
 faceView---表情視圖
 scroll----滑動視圖
 page---分頁控件
 */
- (instancetype) initWithFrame:(CGRect)frame {

    if ([super initWithFrame:frame]) {

        [self createSubView];
    }
    return  self;
}

- (void) createSubView {

    //建立faceView
    FaceView *faceView = [[FaceView alloc] initWithFrame:CGRectMake(, , , )];
    faceView.backgroundColor = [UIColor clearColor];

    //建立scrollView
    UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(, , kScreenWidth, faceView.height)];
    scrollView.contentSize = CGSizeMake(faceView.width, faceView.height);
    scrollView.showsHorizontalScrollIndicator = NO;
    scrollView.showsVerticalScrollIndicator = NO;
    //裁剪
    scrollView.clipsToBounds = NO;
    scrollView.pagingEnabled = YES;
    scrollView.delegate = self;
    [scrollView addSubview:faceView];
    [self addSubview:scrollView];

    //建立分頁控件
    UIPageControl *pageC = [[UIPageControl alloc] initWithFrame:CGRectMake(, faceView.bottom, kScreenWidth, )];
    pageC.numberOfPages = faceView.items.count;
    pageC.tag = ;

    //目前視圖的寬高計算
    self.width = kScreenWidth;
    self.height = faceView.height + pageC.height;

    [self addSubview:pageC];
}

#pragma mark -滑動視圖代理方法
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {

    //計算index--index代表圓點的下标
    NSInteger index = scrollView.contentOffset.x/kScreenWidth;

    UIPageControl *pageC = (UIPageControl *) [self viewWithTag:];

    pageC.currentPage = index;
}

- (void) drawRect:(CGRect)rect {

    UIImage *img = [UIImage imageNamed:@"emoticon_keyboard_background.png"];
    [img drawInRect:rect];
}

@end
           
#pragma mark - 圖檔的代理方法
- (void)imagePickerController:(UIImagePickerController *) picker didFinishPickingMediaWithInfo:(nonnull NSDictionary<NSString *,id> *)info {

    [picker dismissViewControllerAnimated:YES completion:nil];
    [_textView becomeFirstResponder];

    UIImage *img = info[@"UIImagePickerControllerOriginalImage"];

    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(, -, , )];
    imageView.image = img;
    _sendImg = img;
    [_editorView addSubview:imageView];
}
           

四、系統鍵盤與表情視圖之間互相切換

case :
        {
//            [self loadImage];
            //按鈕取反
            button.selected = !button.selected;

            if (button.selected == YES) {
               //收起鍵盤
                [_textView resignFirstResponder];
                //顯示表情視圖
                _textView.inputView = _faceview;
                //彈出鍵盤
                [_textView becomeFirstResponder];
            } else {

                [_textView resignFirstResponder];
                //顯示系統鍵盤
                _textView.inputView = nil;
                [_textView becomeFirstResponder];
            }
        }
            break;
           

五、消息追加

這裡的思路是将圖檔的中文名作為參數,利用通知傳過來,将其複制給_textView.text上。
           
//發送通知
 NSDictionary *userInfo = @{@"imageName" : faceName};
    [[NSNotificationCenter defaultCenter] postNotificationName:@"imageNameNo" object:nil userInfo:userInfo];
           
//接收通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ImgaeNameChange:) name:@"imageNameNo" object:nil];
           
#pragma mark - 通知的實作
- (void) ImgaeNameChange:(NSNotification *) noti {

    NSString *text = [noti.userInfo objectForKey:@"imageName"];
    //追加
    _textView.text = [_textView.text stringByAppendingString:text];
}
           
//移除通知
- (void) dealloc {

    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"imageNameNo" object:nil];
}