学习HM微博项目第9天

步骤:发微博07-表情键盘01-切换键盘 -> 发微博08-表情键盘02-表情工具条 -> 发微博09-表情键盘03-加载表情数据和切换表情按钮 -> 发微博10-表情键盘04-表情分页 -> 发微博11-表情键盘05-显示表情

发微博07-表情键盘01-切换键盘

在HMComposeViewController类的HMComposeToolbarDelegate代理方法composeToolbar:didClickButton:中,完善HMComposeToolbarButtonTypeEmotion里的代码,如下:

composeToolbar:didClickButton:方法

其中,switchKeyboard的详细代码如下:

/**
 *  切换键盘
 */
- (void)switchKeyboard
{
    
//    HMLog(@"switchKeyboard");
    //self.textView.inputView == nil代表系统键盘
    if (self.textView.inputView == nil) {//切换表情键盘
        HMEmotionKeyboard *emtionKeyboard = [[HMEmotionKeyboard alloc] init];
        //设置尺寸(不设置不会显示)
        emtionKeyboard.width = self.view.width;
        emtionKeyboard.height = 216;
        self.textView.inputView = emtionKeyboard;
        
    }else{//切换系统键盘
        self.textView.inputView = nil;
    }
    
    //开始切换键盘
    self.switchingKeyboard = YES;
    
    //退出键盘
    [self.textView endEditing:YES];
//    [self.view endEditing:YES];
//    [self.view.window endEditing:YES];
//    [self.textView resignFirstResponder];
    
    
    //延迟0.1s
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        //弹出键盘
        [self.textView becomeFirstResponder];
        
        //结束切换键盘
        self.switchingKeyboard = NO;
        
    });
    
}

注意点:swichingKeyboard是记录是否正在切换键盘的布尔值,用来控制composeToolBar的位置。(详细的逻辑看源码)

APP的演示动画:

APP的演示动画

发微博08-表情键盘02-表情工具条

前文已经完成了切换键盘按钮的点击事件,现在要完善表情键盘的表情工具条。

进一步完善前文的switchKeyboard方法,代码如下:

/**
 *  切换键盘
 */
- (void)switchKeyboard
{
    //self.textView.inputView == nil 表示在使用系统键盘
    if (self.textView.inputView == nil) {//切换表情键盘
        self.textView.inputView = self.emotionKeyboard;
        
        //显示键盘按钮
        self.toolbar.showKeyboardButton = YES;
        
    }else{//切换系统键盘
        self.textView.inputView = nil;
        self.toolbar.showKeyboardButton = NO;
    }
    
    //开始切换键盘
    self.switchingKeyboard = YES;
    
    //退出键盘
    [self.textView endEditing:YES];

    //延迟0.1s
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        //弹出键盘
        [self.textView becomeFirstResponder];
        
        //结束切换键盘
        self.switchingKeyboard = NO;
    });
    
}

说明:
把HMEmotionKeyboard改成懒加载并且要用strong声明变量。如下:

一定要用strong

在HMEmotionKeyboard类初始化表情键盘的子控件(HMEmotionListView和HMEmotionTabBar),并且设置frame。
详细代码如下:

#import "HMEmotionKeyboard.h"
#import "HMEmotionTabBar.h"
#import "HMEmotionListView.h"

@interface HMEmotionKeyboard ()<HMEmotionTabBarDeleage>

@property(nonatomic,weak) HMEmotionListView *emotionView;
@property(nonatomic,weak) HMEmotionTabBar *tabBar;

@end

@implementation HMEmotionKeyboard

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        
        //初始化子控件
        
        //1.表情
        HMEmotionListView *emotionView = [[HMEmotionListView alloc] init];
        emotionView.backgroundColor = HMRandomColor;
        [self addSubview:emotionView];
        self.emotionView = emotionView;
        
        //2.tabBar
        HMEmotionTabBar *tabBar = [[HMEmotionTabBar alloc] init];
        //设置代理
        tabBar.delegate = self;
        [self addSubview:tabBar];
        self.tabBar = tabBar;

    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    //设置子控件的frame(先确定的tabBar)
    
    //1.tabBar
    self.tabBar.height = 37;
    self.tabBar.width = self.width;
    self.tabBar.x = 0;
    self.tabBar.y = self.height - self.tabBar.height;
    
    //2.表情
    self.emotionView.x = self.emotionView.y = 0;
    self.emotionView.width = self.width;
    self.emotionView.height = self.tabBar.y;
}

#pragma mark - HMEmotionTabBarDeleage
- (void)emotionTabBar:(HMEmotionTabBar *)tarBar didTabBarType:(HMEmotionTabBarButtonType)btnType
{
    switch (btnType) {
        case HMEmotionTabBarButtonTypeRecent: // 最近
            HMLog(@"最近");
            break;
            
        case HMEmotionTabBarButtonTypeDefault: // 默认
            HMLog(@"默认");
            break;
            
        case HMEmotionTabBarButtonTypeEmoji: // Emoji
            HMLog(@"Emoji");
            break;
            
        case HMEmotionTabBarButtonTypeLxh: // Lxh
            HMLog(@"Lxh");
            break;
    }
}

@end

其中,HMEmotionTabBar是表情工具条,用来存放切换各种表情内容的按钮。HMEmotionTabBarDeleage是HMEmotionTabBar所声明的代理协议,用来通知代理(HMEmotionKeyboard)对象工具条所点击的按钮类型。

APP的演示动画:

APP的演示动画

发微博09-表情键盘03-加载表情数据和切换表情按钮

新建模型HMEmotion,存放表情数据。如下:

模型HMEmotion

在HMEmotionKeyboard类,完善MEmotionTabBarDeleage代理协议的
emotionTabBar:didTabBarType:方法,如下:



#pragma mark - HMEmotionTabBarDeleage
- (void)emotionTabBar:(HMEmotionTabBar *)tarBar didTabBarType:(HMEmotionTabBarButtonType)btnType
{
    //移除contenView之前显示的控件
    [self.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    
    //根据按钮类型,切换contentView上面的控件
    switch (btnType) {
        case HMEmotionTabBarButtonTypeRecent:{ // 最近
            HMLog(@"最近");
            [self.contentView addSubview:self.recentEmotionView];//要设置frame
            
            break;
        }
        case HMEmotionTabBarButtonTypeDefault:{ // 默认
            HMLog(@"默认");
            [self.contentView addSubview:self.defaultEmotionView];
            
            break;
        }
        case HMEmotionTabBarButtonTypeEmoji:{ // Emoji
            HMLog(@"Emoji");
            [self.contentView addSubview:self.emojiEmotionView];

            break;
        }
        case HMEmotionTabBarButtonTypeLxh:{ // Lxh
            HMLog(@"Lxh");
            [self.contentView addSubview:self.lxhEmotionView];

            break;
        }
    }
    
    //重新计算子控件的frame(setNeedsLayout内部会在恰当的时刻,重新调用layoutSubviews,重新布局子控件)
    [self setNeedsLayout];
    //错误:得在layoutSubviews时就设置frame,要不然defaul按钮在响应了点击事件也会没有frame
//    UIView *chird = [self.contentView.subviews lastObject];
//    chird.frame = self.contentView.bounds;

#warning 有一个bug,表情键盘出现默认按钮没有选中,需要到代理协议修改delegate的setter方法
}

说明:
1.self.recentEmotionView、self.defaultEmotionView等都是懒加载的,如下:

懒加载

2.需要给self.recentEmotionView、self.defaultEmotionView等设置frame才能显示,要在layoutSubviews方法设置,如下:

layoutSubviews方法

通过[self setNeedsLayout]重新计算子控件的frame。

APP的演示动画:

APP的演示动画

发微博10-表情键盘04-表情分页

完善前文的HMEmotionKeyboard类中的emotionTabBar:didTabBarType:方法,如下:

#pragma mark - HMEmotionTabBarDeleage
- (void)emotionTabBar:(HMEmotionTabBar *)tarBar didTabBarType:(HMEmotionTabBarButtonType)btnType
{
    //移除listView显示的控件
    [self.showingEmotionView removeFromSuperview];
    
    //根据按钮类型,切换contentView上面的控件
    switch (btnType) {
        case HMEmotionTabBarButtonTypeRecent:{ // 最近
            HMLog(@"最近");
            [self addSubview:self.recentEmotionView];//要设置frame
//            self.showingEmotionView = self.recentEmotionView;
            
            break;
        }
        case HMEmotionTabBarButtonTypeDefault:{ // 默认
            HMLog(@"默认");
            [self addSubview:self.defaultEmotionView];
//            self.showingEmotionView = self.defaultEmotionView;

            
            break;
        }
        case HMEmotionTabBarButtonTypeEmoji:{ // Emoji
            HMLog(@"Emoji");
            [self addSubview:self.emojiEmotionView];
//            self.showingEmotionView = self.emojiEmotionView;

            break;
        }
        case HMEmotionTabBarButtonTypeLxh:{ // Lxh
            HMLog(@"Lxh");
            [self addSubview:self.lxhEmotionView];
//            self.showingEmotionView = self.lxhEmotionView;

            break;
        }
    }
    
    //设置正在显示的listView
    self.showingEmotionView = [self.subviews lastObject];
    
    //重新计算子控件的frame(setNeedsLayout内部会在恰当的时刻,重新调用layoutSubviews,重新布局子控件)
    [self setNeedsLayout];

}

说明:
1.前文是把self.recentEmotionView、self.defaultEmotionView等放在self.contentView(继承自UIView)上,而现在把它们都放在self.showingEmotionView 上,而self.showingEmotionView 的声明如下:

self.showingEmotionView

2.在layoutSubviews方法设置self.recentEmotionView、self.defaultEmotionView等的frame的代码可以删掉,由于它们与self.showingEmotionView都是继承自HMEmotionListView所以不需要额外再设置frame。

在HMEmotionListView类中,初始化子控件(scrollIView和pageControl)和设置子控件的frame,重写emotions的setter方法,设置分页,并实现pageControl的currentPage能随着页码改变,具体代码如下:

#import "HMEmotionListView.h"

#define HMEmotionsPageCount 20

@interface HMEmotionListView ()

@property(nonatomic,weak) UIScrollView *scrollView;
@property(nonatomic,weak) UIPageControl *pageControl;

@end

@implementation HMEmotionListView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        
        self.backgroundColor = [UIColor whiteColor];
    
        //1.scrollView
        UIScrollView *scrollView = [[UIScrollView alloc] init];
        scrollView.backgroundColor = [UIColor yellowColor];
        //设置分页
        scrollView.pagingEnabled = YES;
        //取消水平和垂直滚动条的显示
        scrollView.showsVerticalScrollIndicator = NO;
        scrollView.showsHorizontalScrollIndicator = NO;
        scrollView.delegate = self;
        [self addSubview:scrollView];
        self.scrollView = scrollView;
        
        //2.pageControl
        UIPageControl *pageControl = [[UIPageControl alloc] init];
        //设置圆点的图片(系统属性,不开放,用kvc)
//        UIImage*        _currentPageImage;
//        UIImage*        _pageImage;
        [pageControl setValue:[UIImage imageNamed:@"compose_keyboard_dot_normal"] forKeyPath:@"pageImage"];
        [pageControl setValue:[UIImage imageNamed:@"compose_keyboard_dot_selected"] forKeyPath:@"currentPageImage"];
        [self addSubview:pageControl];
        self.pageControl = pageControl;
    }
    return self;
}

- (void)setEmotions:(NSArray *)emotions
{
    _emotions = emotions;
//    HMLog(@"%d",emotions.count);
    //99 80 40
    
    NSUInteger count = (emotions.count + HMEmotionsPageCount -1 ) / HMEmotionsPageCount;
    
    //1.设置页数
     self.pageControl.numberOfPages = count;
    
    //2.创建用来显示每一页表情的控件
    for (int i = 0; i < count; i ++) {
        UIView *pageView = [[UIView alloc] init]; //设置frame
        pageView.backgroundColor = HMRandomColor;
        [self.scrollView addSubview:pageView];
    }
    
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    //1.pageControl
    self.pageControl.width = self.width;
    self.pageControl.height = 45;
    self.pageControl.x = 0;
    self.pageControl.y = self.height - self.pageControl.height;

    //2.scrollView
    self.scrollView.width = self.width;
    self.scrollView.height = self.pageControl.y;
    self.scrollView.x = self.scrollView.y = 0;
    
    //3.设置用来显示每一页表情的控件的frame
    NSUInteger count = self.scrollView.subviews.count;
    for (int i = 0; i< count; i++) {
        UIView *pageView = self.scrollView.subviews[i];
        pageView.width = self.scrollView.width;
        pageView.height = self.scrollView.height;
        pageView.x = i * pageView.width;
        pageView.y = 0;
    }
    
    //4.设置scrollView的contentSize
    self.scrollView.contentSize = CGSizeMake(count * self.scrollView.width, 0);

}

#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//    HMLog(@"%f",scrollView.contentOffset.x / scrollView.width);
    double pageDouble = scrollView.contentOffset.x / scrollView.width;
    self.pageControl.currentPage = (int)(pageDouble + 0.5);
    
}

@end

说明:
1.只有设置contenSize,scrollView才能滚动。

2.一般运用scrollView都需要设置这三个属性。


scrollView

3.这里,通过scrollViewDidScroll:方法监听scrollView的滚动实现pageControll切换页码。

APP的演示动画:

APP的演示动画

发微博11-表情键盘05-显示表情

前文已经设置好分页,现在需要显示表情。
完善setEmotions:方法,具体代码如下:

- (void)setEmotions:(NSArray *)emotions
{
    _emotions = emotions;
//    HMLog(@"%d",emotions.count);
    //99 80 40
    
    NSUInteger count = (emotions.count + HMEmotionsPageCount -1 ) / HMEmotionsPageCount;
    
    //1.设置页数
     self.pageControl.numberOfPages = count;
    
    //2.创建用来显示每一页表情的控件
    for (int i = 0; i < count; i ++) {
        HMPageEmotionView *pageView = [[HMPageEmotionView alloc] init]; //设置frame
//        pageView.backgroundColor = HMRandomColor;
        //计算这一页的表情图标范围
        NSRange range = NSMakeRange(0, 0);
        range.location = i * HMEmotionsPageCount;
        //剩余的表情图标个数
        //99 i = 4 location 80
        NSInteger left = emotions.count -  range.location;
        if (left >= 20) {//满足一页20个
            range.length = HMEmotionsPageCount;
        }else{
            range.length = left;
        }
        //截取表情图标数组
        pageView.emotions = [emotions subarrayWithRange:range];
        [self.scrollView addSubview:pageView];
    }
    
}

其中,HMPageEmotionView是scrollView上每一页表情的内容,其具体代码如下:

#import "HMPageEmotionView.h"
#import "HMEmotion.h"

@implementation HMPageEmotionView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        
    }
    return self;
}

- (void)setEmotions:(NSArray *)emotions
{
    _emotions = emotions;
    
//    HMLog(@"%lu",emotions.count);
    NSUInteger count = emotions.count;
    //添加按钮
    for (int i = 0; i< count; i++) {
        UIButton *btn = [[UIButton alloc] init];
//        btn.backgroundColor = HMRandomColor;
        
        HMEmotion *emotion = emotions[i];
        if (emotion.png) {
//            HMLog(@"%@",emotion.png);
            UIImage *imageTest = [UIImage imageNamed:emotion.png];
            if (imageTest) {
                [btn setImage:[UIImage imageNamed:emotion.png] forState:UIControlStateNormal];
            }else{
                HMLog(@"why is my image object nil?");
            }
//            HMLog(@"%@",btn.currentImage);
        }else if (emotion.code){
            //设置emoji
//            HMLog(@"%@",emotion.code);
            [btn setTitle:emotion.code.emoji forState:UIControlStateNormal];
            btn.titleLabel.font = [UIFont systemFontOfSize:32];
//            HMLog(@"%@",btn.currentTitle);
        }
        [self addSubview:btn];
    }
    
}
//CUICatalog: Invalid asset name supplied: (null)
// 警告原因: 尝试去加载的图片不存在

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    //设置frame
    NSUInteger count = self.emotions.count;
    //内边距(四周)
    CGFloat padding = 10;
    CGFloat btnW = (self.width - 2 * padding) / HMEmotionMaxCols;
    CGFloat btnH = (self.height -  padding) / HMEmotionMaxRows;
    for (int i = 0; i< count; i++) {
        UIButton *btn = self.subviews[i];
        //宽高
        btn.width = btnW;
        btn.height = btnH;
        //位置
        int col = i % HMEmotionMaxCols;
        btn.x = padding +  btnW * col;
        int cow = i / HMEmotionMaxCols;
        btn.y = padding + btnH *cow;
        
    }
}

说明:
在这里,btn setImage遇到一些问题,最后通过谷歌等多种途径解决,详情看iOS疑问文集的iOS button setImage 的疑问

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,139评论 25 707
  • 步骤:发微博12-表情键盘06-点击表情 -> 发微博13-表情键盘07-插入表情和封装textView -> ...
    dibadalu阅读 716评论 2 1
  • 步骤:发微博01-导航栏内容 -> 发微博02-自定义TextView -> 发微博03-完善TextView和...
    dibadalu阅读 3,138评论 1 3
  • 亲爱的各位天使,本期大师兄要大家带来的秘诀是“平衡”。 首先请大家记住一句话,营销就是去打破对方的平衡。因为对方意...
    mxxyfyz阅读 159评论 0 0
  • 整体感受 很多内容实际在船长的公众号当中已经阅读过了,刚开始有一种先入为主的想法就是,可能不会有很大帮助。但在系统...
    南卡多杰阅读 205评论 0 0