358 lines
15 KiB
Objective-C
358 lines
15 KiB
Objective-C
//
|
||
// AGEmojiKeyboardView.m
|
||
// AGEmojiKeyboard
|
||
//
|
||
// Created by Ayush on 09/05/13.
|
||
// Copyright (c) 2013 Ayush. All rights reserved.
|
||
//
|
||
|
||
#import "UMComEmojiKeyBoardView.h"
|
||
#import "UMComEmojiPageView.h"
|
||
#import "UMComResouceDefines.h"
|
||
|
||
static const CGFloat ButtonWidth = 53;
|
||
static const CGFloat ButtonHeight = 50;
|
||
|
||
#define ColumnNum 7
|
||
#define RowNum 3
|
||
|
||
static const NSUInteger DefaultRecentEmojisMaintainedCount = 50;
|
||
|
||
static NSString *const segmentRecentName = @"Recent";
|
||
NSString *const RecentUsedEmojiCharactersKey = @"RecentUsedEmojiCharactersKey";
|
||
|
||
|
||
@interface UMComEmojiKeyboardView () <UIScrollViewDelegate, AGEmojiPageViewDelegate>
|
||
|
||
@property (nonatomic) UISegmentedControl *segmentsBar;
|
||
@property (nonatomic) UIPageControl *pageControl;
|
||
@property (nonatomic) UIScrollView *emojiPagesScrollView;
|
||
@property (nonatomic) NSMutableDictionary *emojis;
|
||
@property (nonatomic) NSMutableArray *pageViews;
|
||
@property (nonatomic) NSString *category;
|
||
|
||
@end
|
||
|
||
@implementation UMComEmojiKeyboardView
|
||
|
||
- (NSDictionary *)emojis {
|
||
if (!_emojis) {
|
||
NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"UMComSDKResources.bundle/EmojisList"
|
||
ofType:@"plist"];
|
||
NSDictionary *emojis = [NSDictionary dictionaryWithContentsOfFile:plistPath];
|
||
NSString *emojiString = [emojis valueForKey:@"People"];
|
||
|
||
NSArray * emojisArray = [emojiString componentsSeparatedByString:@" "];
|
||
_emojis = [NSMutableDictionary dictionaryWithObject:emojisArray forKey:@"People"];
|
||
}
|
||
return _emojis;
|
||
}
|
||
|
||
- (NSString *)categoryNameAtIndex:(NSUInteger)index {
|
||
NSArray *categoryList = @[@"People", @"Objects", @"Nature", @"Places", @"Symbols"];
|
||
return categoryList[index];
|
||
}
|
||
|
||
- (UMComEmojiKeyboardViewCategoryImage)defaultSelectedCategory {
|
||
if ([self.dataSource respondsToSelector:@selector(defaultCategoryForEmojiKeyboardView:)]) {
|
||
return [self.dataSource defaultCategoryForEmojiKeyboardView:self];
|
||
}
|
||
return AGEmojiKeyboardViewCategoryImageFace;
|
||
}
|
||
|
||
- (NSUInteger)recentEmojisMaintainedCount {
|
||
if ([self.dataSource respondsToSelector:@selector(recentEmojisMaintainedCountForEmojiKeyboardView:)]) {
|
||
return [self.dataSource recentEmojisMaintainedCountForEmojiKeyboardView:self];
|
||
}
|
||
return DefaultRecentEmojisMaintainedCount;
|
||
}
|
||
|
||
- (NSArray *)imagesForSelectedSegments {
|
||
static NSMutableArray *array;
|
||
static dispatch_once_t onceToken;
|
||
dispatch_once(&onceToken, ^{
|
||
array = [NSMutableArray array];
|
||
for (UMComEmojiKeyboardViewCategoryImage i = AGEmojiKeyboardViewCategoryImageFace;
|
||
i <= AGEmojiKeyboardViewCategoryImageCharacters;
|
||
++i) {
|
||
[array addObject:[self.dataSource emojiKeyboardView:self imageForSelectedCategory:i]];
|
||
}
|
||
});
|
||
return array;
|
||
}
|
||
|
||
- (NSArray *)imagesForNonSelectedSegments {
|
||
static NSMutableArray *array;
|
||
static dispatch_once_t onceToken;
|
||
dispatch_once(&onceToken, ^{
|
||
array = [NSMutableArray array];
|
||
for (UMComEmojiKeyboardViewCategoryImage i = AGEmojiKeyboardViewCategoryImageFace;
|
||
i <= AGEmojiKeyboardViewCategoryImageCharacters;
|
||
++i) {
|
||
[array addObject:[self.dataSource emojiKeyboardView:self imageForNonSelectedCategory:i]];
|
||
}
|
||
});
|
||
return array;
|
||
}
|
||
|
||
- (instancetype)initWithFrame:(CGRect)frame dataSource:(id<AGEmojiKeyboardViewDataSource>)dataSource {
|
||
self = [super initWithFrame:frame];
|
||
if (self) {
|
||
// initialize category
|
||
self.backgroundColor = [UIColor whiteColor];
|
||
_dataSource = dataSource;
|
||
|
||
self.category = [self categoryNameAtIndex:self.defaultSelectedCategory];
|
||
|
||
self.pageControl = [[UIPageControl alloc] init];
|
||
self.pageControl.hidesForSinglePage = YES;
|
||
self.pageControl.currentPage = 0;
|
||
self.pageControl.backgroundColor = [UIColor clearColor];
|
||
CGSize pageControlSize = [self.pageControl sizeForNumberOfPages:3];
|
||
CGSize frameSize = CGSizeMake(CGRectGetWidth(self.bounds),
|
||
CGRectGetHeight(self.bounds) - pageControlSize.height);
|
||
NSUInteger numberOfPages = [self numberOfPagesForCategory:self.category
|
||
inFrameSize:frameSize];
|
||
self.pageControl.numberOfPages = numberOfPages;
|
||
// pageControlSize = [self.pageControl sizeForNumberOfPages:numberOfPages];
|
||
CGRect pageControlFrame = CGRectMake((CGRectGetWidth(self.bounds) - pageControlSize.width) / 2,
|
||
CGRectGetHeight(self.bounds) - pageControlSize.height,
|
||
pageControlSize.width,
|
||
pageControlSize.height);
|
||
self.pageControl.frame = CGRectIntegral(pageControlFrame);
|
||
[self.pageControl addTarget:self
|
||
action:@selector(pageControlTouched:)
|
||
forControlEvents:UIControlEventValueChanged];
|
||
self.pageControl.currentPageIndicatorTintColor = [UIColor darkGrayColor];
|
||
self.pageControl.pageIndicatorTintColor = [UIColor lightGrayColor];
|
||
[self addSubview:self.pageControl];
|
||
|
||
CGRect scrollViewFrame = CGRectMake(0,
|
||
CGRectGetHeight(self.segmentsBar.bounds),
|
||
CGRectGetWidth(self.bounds),
|
||
CGRectGetHeight(self.bounds) - CGRectGetHeight(self.segmentsBar.bounds) - pageControlSize.height);
|
||
self.emojiPagesScrollView = [[UIScrollView alloc] initWithFrame:scrollViewFrame];
|
||
self.emojiPagesScrollView.pagingEnabled = YES;
|
||
self.emojiPagesScrollView.showsHorizontalScrollIndicator = NO;
|
||
self.emojiPagesScrollView.showsVerticalScrollIndicator = NO;
|
||
self.emojiPagesScrollView.delegate = self;
|
||
|
||
[self addSubview:self.emojiPagesScrollView];
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (void)layoutSubviews {
|
||
CGSize pageControlSize = [self.pageControl sizeForNumberOfPages:3];
|
||
NSUInteger numberOfPages = [self numberOfPagesForCategory:self.category
|
||
inFrameSize:CGSizeMake(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) - CGRectGetHeight(self.segmentsBar.bounds) - pageControlSize.height)];
|
||
|
||
NSInteger currentPage = (self.pageControl.currentPage > numberOfPages) ? numberOfPages : self.pageControl.currentPage;
|
||
|
||
// if (currentPage > numberOfPages) it is set implicitly to max pageNumber available
|
||
self.pageControl.numberOfPages = numberOfPages;
|
||
pageControlSize = [self.pageControl sizeForNumberOfPages:numberOfPages];
|
||
CGRect pageControlFrame = CGRectMake((CGRectGetWidth(self.bounds) - pageControlSize.width) / 2,
|
||
CGRectGetHeight(self.bounds) - pageControlSize.height,
|
||
pageControlSize.width,
|
||
pageControlSize.height);
|
||
self.pageControl.frame = CGRectIntegral(pageControlFrame);
|
||
|
||
self.emojiPagesScrollView.frame = CGRectMake(0,
|
||
CGRectGetHeight(self.segmentsBar.bounds),
|
||
CGRectGetWidth(self.bounds),
|
||
CGRectGetHeight(self.bounds) - CGRectGetHeight(self.segmentsBar.bounds) - pageControlSize.height);
|
||
[self.emojiPagesScrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
||
self.emojiPagesScrollView.contentOffset = CGPointMake(CGRectGetWidth(self.emojiPagesScrollView.bounds) * currentPage, 0);
|
||
self.emojiPagesScrollView.contentSize = CGSizeMake(CGRectGetWidth(self.emojiPagesScrollView.bounds) * numberOfPages,
|
||
CGRectGetHeight(self.emojiPagesScrollView.bounds));
|
||
[self purgePageViews];
|
||
self.pageViews = [NSMutableArray array];
|
||
[self setPage:currentPage];
|
||
}
|
||
|
||
#pragma mark event handlers
|
||
|
||
- (void)setSelectedCategoryImageInSegmentControl:(UISegmentedControl *)segmentsBar
|
||
atIndex:(NSInteger)index {
|
||
for (int i=0; i < self.segmentsBar.numberOfSegments; ++i) {
|
||
[segmentsBar setImage:self.imagesForNonSelectedSegments[i] forSegmentAtIndex:i];
|
||
}
|
||
[segmentsBar setImage:self.imagesForSelectedSegments[index] forSegmentAtIndex:index];
|
||
}
|
||
|
||
- (void)categoryChangedViaSegmentsBar:(UISegmentedControl *)sender {
|
||
// recalculate number of pages for new category and recreate emoji pages
|
||
self.category = [self categoryNameAtIndex:sender.selectedSegmentIndex];
|
||
[self setSelectedCategoryImageInSegmentControl:sender
|
||
atIndex:sender.selectedSegmentIndex];
|
||
self.pageControl.currentPage = 0;
|
||
[self setNeedsLayout];
|
||
}
|
||
|
||
- (void)pageControlTouched:(UIPageControl *)sender {
|
||
CGRect bounds = self.emojiPagesScrollView.bounds;
|
||
bounds.origin.x = CGRectGetWidth(bounds) * sender.currentPage;
|
||
bounds.origin.y = 0;
|
||
// scrollViewDidScroll is called here. Page set at that time.
|
||
[self.emojiPagesScrollView scrollRectToVisible:bounds animated:YES];
|
||
}
|
||
|
||
// Track the contentOffset of the scroll view, and when it passes the mid
|
||
// point of the current view’s width, the views are reconfigured.
|
||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||
CGFloat pageWidth = CGRectGetWidth(scrollView.frame);
|
||
NSInteger newPageNumber = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
|
||
if (self.pageControl.currentPage == newPageNumber) {
|
||
return;
|
||
}
|
||
self.pageControl.currentPage = newPageNumber;
|
||
[self setPage:self.pageControl.currentPage];
|
||
}
|
||
|
||
#pragma mark change a page on scrollView
|
||
|
||
// Check if setting pageView for an index is required
|
||
- (BOOL)requireToSetPageViewForIndex:(NSUInteger)index {
|
||
if (index >= self.pageControl.numberOfPages) {
|
||
return NO;
|
||
}
|
||
for (UMComEmojiPageView *page in self.pageViews) {
|
||
if ((page.frame.origin.x / CGRectGetWidth(self.emojiPagesScrollView.bounds)) == index) {
|
||
return NO;
|
||
}
|
||
}
|
||
return YES;
|
||
}
|
||
|
||
// Create a pageView and add it to the scroll view.
|
||
- (UMComEmojiPageView *)synthesizeEmojiPageView {
|
||
CGRect pageViewFrame = CGRectMake(0,
|
||
0,
|
||
CGRectGetWidth(self.emojiPagesScrollView.bounds),
|
||
CGRectGetHeight(self.emojiPagesScrollView.bounds));
|
||
UMComEmojiPageView *pageView = [[UMComEmojiPageView alloc] initWithFrame: pageViewFrame
|
||
backSpaceButtonImage:UMComImageWithImageName(@"um_emoji_delete")
|
||
buttonSize:CGSizeMake(ButtonWidth, ButtonHeight)
|
||
rows:3
|
||
columns:7];
|
||
pageView.delegate = self;
|
||
[self.pageViews addObject:pageView];
|
||
[self.emojiPagesScrollView addSubview:pageView];
|
||
return pageView;
|
||
}
|
||
|
||
// return a pageView that can be used in the current scrollView.
|
||
// look for an available pageView in current pageView-s on scrollView.
|
||
// If all are in use i.e. are of current page or neighbours
|
||
// of current page, we create a new one
|
||
|
||
- (UMComEmojiPageView *)usableEmojiPageView {
|
||
UMComEmojiPageView *pageView = nil;
|
||
for (UMComEmojiPageView *page in self.pageViews) {
|
||
NSUInteger pageNumber = page.frame.origin.x / CGRectGetWidth(self.emojiPagesScrollView.bounds);
|
||
if (abs((int)(pageNumber - self.pageControl.currentPage)) > 1) {
|
||
pageView = page;
|
||
break;
|
||
}
|
||
}
|
||
if (!pageView) {
|
||
pageView = [self synthesizeEmojiPageView];
|
||
}
|
||
return pageView;
|
||
}
|
||
|
||
// Set emoji page view for given index.
|
||
- (void)setEmojiPageViewInScrollView:(UIScrollView *)scrollView atIndex:(NSUInteger)index {
|
||
|
||
if (![self requireToSetPageViewForIndex:index]) {
|
||
return;
|
||
}
|
||
|
||
UMComEmojiPageView *pageView = [self usableEmojiPageView];
|
||
|
||
// NSUInteger rows = [self numberOfRowsForFrameSize:scrollView.bounds.size];
|
||
// NSUInteger columns = [self numberOfColumnsForFrameSize:scrollView.bounds.size];
|
||
NSUInteger rows = RowNum;
|
||
NSUInteger columns = ColumnNum;
|
||
NSUInteger startingIndex = index * (rows * columns - 1);
|
||
NSUInteger endingIndex = (index + 1) * (rows * columns - 1);
|
||
NSMutableArray *buttonTexts = [self emojiTextsForCategory:self.category
|
||
fromIndex:startingIndex
|
||
toIndex:endingIndex];
|
||
[pageView setButtonTexts:buttonTexts];
|
||
pageView.frame = CGRectMake(index * CGRectGetWidth(scrollView.bounds),
|
||
0,
|
||
CGRectGetWidth(scrollView.bounds),
|
||
CGRectGetHeight(scrollView.bounds));
|
||
}
|
||
|
||
// Set the current page.
|
||
// sets neightbouring pages too, as they are viewable by part scrolling.
|
||
- (void)setPage:(NSInteger)page {
|
||
[self setEmojiPageViewInScrollView:self.emojiPagesScrollView atIndex:page - 1];
|
||
[self setEmojiPageViewInScrollView:self.emojiPagesScrollView atIndex:page];
|
||
[self setEmojiPageViewInScrollView:self.emojiPagesScrollView atIndex:page + 1];
|
||
}
|
||
|
||
- (void)purgePageViews {
|
||
for (UMComEmojiPageView *page in self.pageViews) {
|
||
page.delegate = nil;
|
||
}
|
||
self.pageViews = nil;
|
||
}
|
||
|
||
#pragma mark data methods
|
||
|
||
- (NSUInteger)numberOfColumnsForFrameSize:(CGSize)frameSize {
|
||
return (NSUInteger)floor(frameSize.width / ButtonWidth);
|
||
}
|
||
|
||
- (NSUInteger)numberOfRowsForFrameSize:(CGSize)frameSize {
|
||
return (NSUInteger)floor(frameSize.height / ButtonHeight);
|
||
}
|
||
|
||
- (NSArray *)emojiListForCategory:(NSString *)category {
|
||
return [self.emojis objectForKey:category];
|
||
}
|
||
|
||
// for a given frame size of scroll view, return the number of pages
|
||
// required to show all the emojis for a category
|
||
- (NSUInteger)numberOfPagesForCategory:(NSString *)category inFrameSize:(CGSize)frameSize {
|
||
|
||
if ([category isEqualToString:segmentRecentName]) {
|
||
return 1;
|
||
}
|
||
|
||
NSUInteger emojiCount = [[self emojiListForCategory:category] count];
|
||
NSUInteger numberOfRows = RowNum;
|
||
NSUInteger numberOfColumns = ColumnNum;
|
||
NSUInteger numberOfEmojisOnAPage = (numberOfRows * numberOfColumns) - 1;
|
||
|
||
NSUInteger numberOfPages = (NSUInteger)ceil((float)emojiCount / numberOfEmojisOnAPage);
|
||
return numberOfPages;
|
||
}
|
||
|
||
// return the emojis for a category, given a staring and an ending index
|
||
- (NSMutableArray *)emojiTextsForCategory:(NSString *)category
|
||
fromIndex:(NSUInteger)start
|
||
toIndex:(NSUInteger)end {
|
||
NSArray *emojis = [self emojiListForCategory:category];
|
||
end = ([emojis count] > end)? end : [emojis count];
|
||
NSIndexSet *index = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(start, end-start)];
|
||
return [[emojis objectsAtIndexes:index] mutableCopy];
|
||
}
|
||
|
||
#pragma mark EmojiPageViewDelegate
|
||
// add the emoji to recents
|
||
- (void)emojiPageView:(UMComEmojiPageView *)emojiPageView didUseEmoji:(NSString *)emoji {
|
||
// [self setInRecentsEmoji:emoji];
|
||
[self.delegate emojiKeyBoardView:self didUseEmoji:emoji];
|
||
}
|
||
|
||
- (void)emojiPageViewDidPressBackSpace:(UMComEmojiPageView *)emojiPageView {
|
||
[self.delegate emojiKeyBoardViewDidPressBackSpace:self];
|
||
}
|
||
|
||
@end
|