新浪微博客户端(53)-记录用户最近点击表情
设计思路:每当用户点击一个表情,就将该表情(DJEmotion)存储到沙盒,当用户切换到“最近”栏目时,从沙盒中取出用户最近点击的表情列表,进行显示。
1. 保存DJEmotion
DJEmotion.h
#import <Foundation/Foundation.h> // 因为要保存最近使用的表情到沙盒,所以该对象须实现NSCoding协议 @interface DJEmotion : NSObject <NSCoding> /** Emotion 中文字符 */ @property (nonatomic,copy) NSString *chs; /** Emotion 对应图片 */ @property (nonatomic,copy) NSString *png; /** Emoji 二进制数字 */ @property (nonatomic,copy) NSString *code; @end
DJEmotion.m
#import "DJEmotion.h" @implementation DJEmotion /** * 此方法用于比较两个对象是否是否相等,即:== * 类似于java中的isEqual方法 * 重写该方法意味着覆盖系统的比较方式 */ - (BOOL)isEqual:(DJEmotion *)other { // 重写Emotion的比较方式,即:当Emotion对象中的chs或code相等时,就视这两个对象相等 return [self.chs isEqualToString:other.chs] || [self.code isEqualToString:other.code]; } // 解码 - (instancetype)initWithCoder:(NSCoder *)decoder { if (self = [super init]) { self.chs = [decoder decodeObjectForKey:@"chs"]; self.png = [decoder decodeObjectForKey:@"png"]; self.code = [decoder decodeObjectForKey:@"code"]; } return self; } // 编码 - (void)encodeWithCoder:(NSCoder *)encoder { [encoder encodeObject:self.chs forKey:@"chs"]; [encoder encodeObject:self.png forKey:@"png"]; [encoder encodeObject:self.code forKey:@"code"]; } @end
2. 将表情存储到沙盒中的工具类
DJEmotionTool.h
#import <Foundation/Foundation.h> @class DJEmotion; @interface DJEmotionTool : NSObject /** 保存最近点击Emotion */ + (void)saveRecentEmotion:(DJEmotion *)emotion; /** 获取存储在沙盒中最近点击的Emotion集合 */ + (NSArray *)recentEmotions; @end
DJEmotionTool.m
#import "DJEmotionTool.h" // 最近表情保存路径 #define DJEmotionPath [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"emotion.archiver"] // 类似于java里面的静态成员变量 static NSMutableArray *_recentEmotions; @implementation DJEmotionTool /** * 这个方法会在第一次使用这个类的时候调一次,且只调一次 * 类似于java中静态代码块 */ + (void)initialize { _recentEmotions = [NSKeyedUnarchiver unarchiveObjectWithFile:DJEmotionPath]; if (_recentEmotions == nil) { _recentEmotions = [NSMutableArray array]; } } /** 保存最近点击Emotion */ + (void)saveRecentEmotion:(DJEmotion *)emotion { // 移除之前存储的相同Emotion [_recentEmotions removeObject:emotion]; // 在数组的头部插入新的Emotion [_recentEmotions insertObject:emotion atIndex:0]; // 保存当前数组 [NSKeyedArchiver archiveRootObject:_recentEmotions toFile:DJEmotionPath]; } /** 获取存储在沙盒中最近点击的Emotion集合 */ + (NSArray *)recentEmotions { return _recentEmotions; } @end
3. 因为存储在沙盒的最近表情是实时变换的,所以不能使用懒加载,因此在点击最近按钮时,重新给最近按钮的ListView赋值:
DJEmotionKeyboard.m
#pragma mark - DJEmotionTabBar 的代理方法 - (void)emotionTabBar:(DJEmotionTabBar *)tabBar didSelectedButtonType:(DJEmotionTabBarButtonType)buttonType { // 1. 清空placeView中之前存在的其它View(类似于android中的ViewGroup.removeAllViews) [self.placeView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; // 2. 切换contentView switch (buttonType) { case DJEmotionTabBarButtonTypeRecent: // 最近 [self.placeView addSubview:self.recentEmotionView]; // 从DJEmotionTool中获取沙盒中存储的最近使用表情,注意!此处不能使用懒加载,因为这样会导致数据无法实时更新 self.recentEmotionView.emotions = [DJEmotionTool recentEmotions]; break; case DJEmotionTabBarButtonTypeDefault: // 默认 [self.placeView addSubview:self.defaultEmotionView]; break; case DJEmotionTabBarButtonTypeEmoji: // Emoji [self.placeView addSubview:self.emojiEmotionView]; break; case DJEmotionTabBarButtonTypeLxh: // 浪小花 [self.placeView addSubview:self.lxhEmotionView]; break; default: break; } // 3.切换完毕后通知系统重新布局,相当于再次调用layoutSubViews [self setNeedsLayout]; }
4. 当ListView收到传来的新数据时,清空之前的控件,进行重新布局(目的是为了让新点击的最近表情排在前面)。
DJEmotionListView.m
- (void)setEmotions:(NSArray *)emotions { _emotions = emotions; // 先清空scrollView里面已有的子控件,便于刷新 [self.emotionScrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; // emotion 总个数 NSUInteger emotionCount = emotions.count; // 页码总个数 NSUInteger pageNums = (emotionCount + DJEmotionPageSize - 1) / DJEmotionPageSize; // 更新pageControl 显示个数 self.emotionPageControl.numberOfPages = pageNums; // 为scrollView 添加子View for (int i = 0; i < pageNums; i++) { DJEmotionPageView *pageView = [[DJEmotionPageView alloc] init]; // pageView.backgroundColor = DJRandomColor; NSRange range; range.location = i * DJEmotionPageSize; NSUInteger leftNums = emotionCount - range.location; // 求出当前剩余Emoji个数 if (leftNums >= DJEmotionPageSize) { // 判断当前剩余Emoji个数是否满20 range.length = DJEmotionPageSize; } else { // 如果不满20,则range.length就等于剩余的个数 range.length = leftNums; } NSArray *singlePageEmotions = [emotions subarrayWithRange:range]; pageView.emotions = singlePageEmotions; [self.emotionScrollView addSubview:pageView]; } // 数据添加完毕后,通知系统进行重新布局 [self setNeedsLayout];
5. 当点击表情按钮或滑动表情按钮时,如果发出了输入通知,此时就将点击的表情按钮存储到沙盒中去。
DJEmotionPageView.m
// 发送点击广播(和android类似,区别在于android的广播是只要有上下文对象context,就可以发送) // iOS中的通知发送和接收都是通过NSNotificationCenter完成 - (void)sendBtnClickNotification:(DJEmotionButton *)btn { // 存储当前Emotion表情 [DJEmotionTool saveRecentEmotion:btn.emotion]; NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; userInfo[DJEmotionDidSelctedEmotionKey] = btn.emotion; [[NSNotificationCenter defaultCenter] postNotificationName:DJEmotionDidSelectedNotification object:nil userInfo:userInfo]; }
最终效果: