学习HM微博项目第9天

步骤:发微博07-表情键盘01-切换键盘 -> 发微博08-表情键盘02-表情工具条 -> 发微博09-表情键盘03-加载表情数据和切换表情按钮 -> 发微博1

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

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

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

其中,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.1sdispatch_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的演示动画:

发微博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.1sdispatch_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声明变量。如下:

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

#import "HMEmotionKeyboard.h"
#import "HMEmotionTabBar.h"
#import "HMEmotionListView.h"@interface HMEmotionKeyboard ()@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.tabBarHMEmotionTabBar *tabBar = [[HMEmotionTabBar alloc] init];//设置代理tabBar.delegate = self;[self addSubview:tabBar];self.tabBar = tabBar;}return self;
}- (void)layoutSubviews
{[super layoutSubviews];//设置子控件的frame(先确定的tabBar)//1.tabBarself.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: // EmojiHMLog(@"Emoji");break;case HMEmotionTabBarButtonTypeLxh: // LxhHMLog(@"Lxh");break;}
}@end

 

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

APP的演示动画:

 

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

新建模型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];//要设置framebreak;}case HMEmotionTabBarButtonTypeDefault:{ // 默认HMLog(@"默认");[self.contentView addSubview:self.defaultEmotionView];break;}case HMEmotionTabBarButtonTypeEmoji:{ // EmojiHMLog(@"Emoji");[self.contentView addSubview:self.emojiEmotionView];break;}case HMEmotionTabBarButtonTypeLxh:{ // LxhHMLog(@"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方法设置,如下:

 

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

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:{ // EmojiHMLog(@"Emoji");[self addSubview:self.emojiEmotionView];
//            self.showingEmotionView = self.emojiEmotionView;break;}case HMEmotionTabBarButtonTypeLxh:{ // LxhHMLog(@"Lxh");[self addSubview:self.lxhEmotionView];
//            self.showingEmotionView = self.lxhEmotionView;break;}}//设置正在显示的listViewself.showingEmotionView = [self.subviews lastObject];//重新计算子控件的frame(setNeedsLayout内部会在恰当的时刻,重新调用layoutSubviews,重新布局子控件)[self setNeedsLayout];}

说明:
1.前文是把self.recentEmotionView、self.defaultEmotionView等放在self.contentView(继承自UIView)上,而现在把它们都放在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.scrollViewUIScrollView *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.pageControlUIPageControl *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 40NSUInteger count = (emotions.count + HMEmotionsPageCount -1 ) / HMEmotionsPageCount;//1.设置页数self.pageControl.numberOfPages = count;//2.创建用来显示每一页表情的控件for (int i = 0; i < count; i ++) {UIView *pageView = [[UIView alloc] init]; //设置framepageView.backgroundColor = HMRandomColor;[self.scrollView addSubview:pageView];}}- (void)layoutSubviews
{[super layoutSubviews];//1.pageControlself.pageControl.width = self.width;self.pageControl.height = 45;self.pageControl.x = 0;self.pageControl.y = self.height - self.pageControl.height;//2.scrollViewself.scrollView.width = self.width;self.scrollView.height = self.pageControl.y;self.scrollView.x = self.scrollView.y = 0;//3.设置用来显示每一页表情的控件的frameNSUInteger 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的contentSizeself.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都需要设置这三个属性。

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

APP的演示动画:

 

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

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

- (void)setEmotions:(NSArray *)emotions
{_emotions = emotions;
//    HMLog(@"%d",emotions.count);//99 80 40NSUInteger 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 80NSInteger 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];//设置frameNSUInteger 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;}
}