iOS开发-聊天emoji表情与自定义动图表情左右滑动控件
之前开发中遇到需要实现聊天emoji表情与自定义动图表情左右滑动控件。使用UICollectionView实现。
一、效果图
二、实现代码
UICollectionView是一种类似于UITableView但又比UITableView功能更强大、更灵活的视图,这是源于它将UICollectionView对cell的布局交给了UICollectionViewLayout,而且允许用户自定义layout来进行布局。
2.1 UICollectionView初始化
INEmotionView.h
@interface INEmotionView : UIView
@property (nonatomic, weak) id delegate;
@property (nonatomic, strong) INEmotionFlowLayout *flowLayout;
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) UIPageControl *pageControl;
- (id)initWithFrame:(CGRect)frame;
@end
INEmotionView.m
#import "INEmotionView.h"
#import "UIColor+Addition.h"
static CGFloat kCollectionHeight = 260;
@implementation INEmotionView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor colorWithHexString:@"efeff4"];
self.flowLayout =[[INEmotionFlowLayout alloc] init];
self.flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.collectionView = [[UICollectionView alloc]initWithFrame:frame collectionViewLayout:self.flowLayout];
self.collectionView.backgroundColor = [UIColor clearColor];
self.collectionView.scrollEnabled = YES;
self.collectionView.pagingEnabled = YES;
self.collectionView.showsVerticalScrollIndicator = NO;
self.collectionView.showsHorizontalScrollIndicator = NO;
self.collectionView.userInteractionEnabled = YES;
self.collectionView.exclusiveTouch = YES;
[self addSubview:self.collectionView];
self.pageControl = [[UIPageControl alloc]initWithFrame:CGRectZero];
self.pageControl.backgroundColor = [UIColor clearColor];
self.pageControl.currentPage = 0;
self.pageControl.numberOfPages = 0;
self.pageControl.currentPageIndicatorTintColor = [UIColor redColor];
self.pageControl.pageIndicatorTintColor = [UIColor greenColor];
[self addSubview:self.pageControl];
}
return self;
}
- (id)init {
return [self initWithFrame:CGRectZero];
}
- (void)layoutSubviews {
[super layoutSubviews];
self.collectionView.frame = CGRectMake(0.0, (CGRectGetHeight(self.bounds) - kCollectionHeight)/2, CGRectGetWidth(self.bounds), kCollectionHeight);
self.pageControl.frame = CGRectMake(0.0, CGRectGetMaxY(self.collectionView.frame), CGRectGetWidth(self.bounds), 50);
}
- (void)setDelegate:(id)delegate {
_delegate = delegate;
self.collectionView.delegate = delegate;
self.collectionView.dataSource = delegate;
}
@end
2.2 UICollectionView实现控件
emoji表情与自定义动图表情左右切换,需要根据拆分多个section,UICollectionView的datasource
我这里使用的是5个分组,每个分组的数量如下。
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 5;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
NSInteger sectionNumber = 60;
switch (section) {
case 0:
sectionNumber = 72;
break;
case 1:
sectionNumber = 8;
break;
case 2:
sectionNumber = 16;
break;
case 3:
sectionNumber = 24;
break;
case 4:
sectionNumber = 32;
break;
default:
break;
}
return sectionNumber;
}
UICollectionView左右切换,需要将pagingEnabled设置为YES。
界面上用到了UIPageControl,由于UICollectionView继承UIScrollView。不同section的页码不一样,所以在scrollViewDidEndDecelerating方法中更改UIPageControl的numberOfPages与currentPage。
/** 手指滑动屏幕时,视图停止滚动会调用此方法 */
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
NSInteger collectionPage = scrollView.contentOffset.x/scrollView.frame.size.width;
NSInteger aIndex = 0;
NSInteger sectionIndex = 0;
for (NSInteger index = 0; index < self.sectionPageNumbers.count; index ++) {
NSString *sectionPage = [self.sectionPageNumbers objectAtIndex:index];
aIndex = aIndex+[sectionPage integerValue];
if (collectionPage >= 0 && collectionPage < aIndex) {
sectionIndex = index;
break;
}
}
NSString *sectionCount = [self.sectionPageNumbers objectAtIndex:sectionIndex];
NSLog(@"sectionCount:%@",sectionCount);
NSInteger preCount = 0;
for (NSInteger i = 0; i < sectionIndex; i++) {
NSString *sectionPage = [self.sectionPageNumbers objectAtIndex:i];
preCount = preCount + sectionPage.integerValue;
}
NSInteger sectionPageCount = sectionCount.integerValue;
NSInteger sectionCurPage = collectionPage - preCount;
NSLog(@"sectionPageCount:%ld",(long)sectionPageCount);
NSLog(@"sectionCurPage:%ld",(long)sectionCurPage);
self.emojiView.pageControl.numberOfPages = sectionPageCount;
self.emojiView.pageControl.currentPage = sectionCurPage;
}
整体使用UICollectionView代理delegate方法代码如下
#import "INEmotionPresenter.h"
#define kCustomEmotionScreenWidth [UIScreen mainScreen].bounds.size.width
@interface INCollectionSectionPageRange : NSObject
@property (nonatomic, assign) NSString *beginNumber;
@property (nonatomic, assign) NSString *endNumber;
@end
@implementation INCollectionSectionPageRange
@end
@implementation INEmotionPresenter
#pragma mark - 注册cell
/**
注册cell
*/
- (void)registerCollectionCell {
[self.emojiView.collectionView registerClass:[INEmotionSystemEmojiCell class] forCellWithReuseIdentifier:kEmotionEmojiSystemIdentifier];
[self.emojiView.collectionView registerClass:[INEmotionCustomEmojiCell class] forCellWithReuseIdentifier:kEmotionEmojiCustomIdentifier];
}
#pragma mark - UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
UIEdgeInsets contentInset = collectionView.contentInset;
NSInteger columnCount = 4;
NSInteger rowCount = 2;
if (indexPath.section == 0) {
columnCount = 8;
rowCount = 3;
}
CGFloat w = (CGRectGetWidth(collectionView.bounds) - contentInset.left - contentInset.right)/ columnCount;
CGFloat h = (CGRectGetHeight(collectionView.bounds) - contentInset.top - contentInset.bottom)/rowCount;
return CGSizeMake(w, h);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return 0.0;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
return 0.0;
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 5;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
NSInteger sectionNumber = 60;
switch (section) {
case 0:
sectionNumber = 72;
break;
case 1:
sectionNumber = 8;
break;
case 2:
sectionNumber = 16;
break;
case 3:
sectionNumber = 24;
break;
case 4:
sectionNumber = 32;
break;
default:
break;
}
return sectionNumber;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
//emoji表情
INEmotionSystemEmojiCell *cell = (INEmotionSystemEmojiCell *)[collectionView dequeueReusableCellWithReuseIdentifier:kEmotionEmojiSystemIdentifier forIndexPath:indexPath];
cell.emojiLabel.text = @"";
cell.emojiLabel.text = [NSString stringWithFormat:@"%ld",(long)indexPath.item];
cell.cellNumber = [NSString stringWithFormat:@"%ld",(long)indexPath.item];
return cell;
}
//自定义表情贴图
INEmotionCustomEmojiCell *cell = (INEmotionCustomEmojiCell *)[collectionView dequeueReusableCellWithReuseIdentifier:kEmotionEmojiCustomIdentifier forIndexPath:indexPath];
cell.cellNumber = [NSString stringWithFormat:@"%ld",(long)indexPath.item];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
NSInteger collectionPage = scrollView.contentOffset.x/scrollView.frame.size.width;
NSInteger aIndex = 0;
NSInteger sectionIndex = 0;
for (NSInteger index = 0; index < self.sectionPageNumbers.count; index ++) {
NSString *sectionPage = [self.sectionPageNumbers objectAtIndex:index];
aIndex = aIndex+[sectionPage integerValue];
if (collectionPage >= 0 && collectionPage < aIndex) {
sectionIndex = index;
break;
}
}
NSString *sectionCount = [self.sectionPageNumbers objectAtIndex:sectionIndex];
NSLog(@"sectionCount:%@",sectionCount);
NSInteger preCount = 0;
for (NSInteger i = 0; i < sectionIndex; i++) {
NSString *sectionPage = [self.sectionPageNumbers objectAtIndex:i];
preCount = preCount + sectionPage.integerValue;
}
NSInteger sectionPageCount = sectionCount.integerValue;
NSInteger sectionCurPage = collectionPage - preCount;
NSLog(@"sectionPageCount:%ld",(long)sectionPageCount);
NSLog(@"sectionCurPage:%ld",(long)sectionCurPage);
self.emojiView.pageControl.numberOfPages = sectionPageCount;
self.emojiView.pageControl.currentPage = sectionCurPage;
}
#pragma mark - SETTER/GETTER
- (NSMutableArray *)sectionPageNumbers {
if (!_sectionPageNumbers) {
_sectionPageNumbers = [NSMutableArray arrayWithCapacity:0];
[_sectionPageNumbers addObject:@"3"];
[_sectionPageNumbers addObject:@"1"];
[_sectionPageNumbers addObject:@"2"];
[_sectionPageNumbers addObject:@"3"];
[_sectionPageNumbers addObject:@"4"];
}
return _sectionPageNumbers;
}
- (INEmotionConfig *)emojiConfig {
if (!_emojiConfig) {
_emojiConfig = [[INEmotionConfig alloc] init];
}
return _emojiConfig;
}
- (INEmotionInteractor *)emojiInteractor {
if (!_emojiInteractor) {
_emojiInteractor = [[INEmotionInteractor alloc] init];
}
return _emojiInteractor;
}
- (INEmotionView *)emojiView {
if (!_emojiView) {
_emojiView = [[INEmotionView alloc] initWithFrame:CGRectZero];
}
return _emojiView;
}
@end
2.3 实现表情的排列UICollectionViewFlowLayout
由于emoji表情需要3行8列,自定义贴图表情需要2行4列排列。我这里实现一下UICollectionViewFlowLayout
要在layoutAttributesForItemAtIndexPath中区分,如果section为0,则为3行8列;否则为2行4列。
INEmotionFlowLayout.h
#import <UIKit/UIKit.h>
@interface INEmotionFlowLayout : UICollectionViewFlowLayout
- (UICollectionViewLayoutAttributes *)customLayoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;
@end
INEmotionFlowLayout.m
#import "INEmotionFlowLayout.h"
#define kLayScreenWidth [UIScreen mainScreen].bounds.size.width
@interface INEmotionFlowLayout () <UICollectionViewDelegateFlowLayout>
@property (strong, nonatomic) NSMutableArray *allAttributes;
@property (nonatomic, assign) NSInteger currentRow;
@property (nonatomic, assign) NSInteger currentCol;
@end
@implementation INEmotionFlowLayout
-(instancetype)init
{
if (self = [super init])
{
}
return self;
}
- (void)prepareLayout
{
[super prepareLayout];
self.allAttributes = [NSMutableArray array];
NSInteger sections = [self.collectionView numberOfSections];
for (int i = 0; i < sections; i++)
{
NSMutableArray * tmpArray = [NSMutableArray array];
NSUInteger count = [self.collectionView numberOfItemsInSection:i];
for (NSUInteger j = 0; j<count; j++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:j inSection:i];
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
[tmpArray addObject:attributes];
}
[self.allAttributes addObject:tmpArray];
}
}
- (CGSize)collectionViewContentSize
{
return [super collectionViewContentSize];
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger item = indexPath.item;
NSUInteger x;
NSUInteger y;
[self targetPositionWithItem:indexPath resultX:&x resultY:&y];
NSUInteger item2 = [self originItemAtX:x y:y indexPath:indexPath];
NSIndexPath *theNewIndexPath = [NSIndexPath indexPathForItem:item2 inSection:indexPath.section];
UICollectionViewLayoutAttributes *theNewAttr = [super layoutAttributesForItemAtIndexPath:theNewIndexPath];
theNewAttr.indexPath = indexPath;
return theNewAttr;
}
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
NSMutableArray *tmp = [NSMutableArray array];
for (UICollectionViewLayoutAttributes *attr in attributes) {
for (NSMutableArray *attributes in self.allAttributes)
{
for (UICollectionViewLayoutAttributes *attr2 in attributes) {
if (attr.indexPath.item == attr2.indexPath.item) {
[tmp addObject:attr2];
break;
}
}
}
}
return tmp;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
// 根据 item 计算目标item的位置
// x 横向偏移 y 竖向偏移
- (void)targetPositionWithItem:(NSIndexPath *)indexPath
resultX:(NSUInteger *)x
resultY:(NSUInteger *)y
{
NSInteger columnCount = 4;
NSInteger rowCount = 2;
if (indexPath.section == 0) {
columnCount = 8;
rowCount = 3;
}
NSInteger item = indexPath.item;
NSUInteger page = item/(columnCount*rowCount);
NSUInteger theX = item % columnCount + page * columnCount;
NSUInteger theY = item / columnCount - page * rowCount;
if (x != NULL) {
*x = theX;
}
if (y != NULL) {
*y = theY;
}
}
// 根据偏移量计算item
- (NSUInteger)originItemAtX:(NSUInteger)x
y:(NSUInteger)y
indexPath:(NSIndexPath *)indexPath
{
NSInteger columnCount = 4;
NSInteger rowCount = 2;
if (indexPath.section == 0) {
columnCount = 8;
rowCount = 3;
}
NSUInteger item = x * rowCount + y;
return item;
}
- (UICollectionViewLayoutAttributes *)customLayoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
return [self layoutAttributesForItemAtIndexPath:indexPath];
}
@end
2.4 emoji表情的UICollectionViewCell
继承UICollectionViewCell实现INEmotionSystemEmojiCell表情
INEmotionSystemEmojiCell.h
#import <UIKit/UIKit.h>
#import "UIColor+Addition.h"
@interface INEmotionSystemEmojiCell : UICollectionViewCell
@property (nonatomic, strong) UIImageView *emojiImageView;
@property (nonatomic, strong) UILabel *emojiLabel;
/**
cell的序号
*/
@property (nonatomic, strong) NSString *cellNumber;
/**
按照序号,从小到大
@param cell cell
@return 排序
*/
- (NSComparisonResult)sortAscCells:(INEmotionSystemEmojiCell *)cell;
@end
INEmotionSystemEmojiCell.m
#import "INEmotionSystemEmojiCell.h"
static CGFloat kEmotionEmojiSize = 40.0;
@implementation INEmotionSystemEmojiCell
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.emojiImageView = [[UIImageView alloc] initWithFrame:CGRectMake((CGRectGetWidth(self.bounds) - kEmotionEmojiSize)/2, (CGRectGetHeight(self.bounds) - kEmotionEmojiSize)/2, kEmotionEmojiSize, kEmotionEmojiSize)];
self.emojiImageView.backgroundColor = [UIColor randomColor];
self.emojiImageView.layer.cornerRadius = kEmotionEmojiSize/2;
self.emojiImageView.layer.masksToBounds = YES;
[self addSubview:self.emojiImageView];
self.emojiLabel = [[UILabel alloc] initWithFrame:CGRectMake((CGRectGetWidth(self.bounds) - kEmotionEmojiSize)/2, (CGRectGetHeight(self.bounds) - kEmotionEmojiSize)/2, kEmotionEmojiSize, kEmotionEmojiSize)];
self.emojiLabel.textColor = [UIColor grayColor];
self.emojiLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:self.emojiLabel];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.emojiImageView.frame = CGRectMake((CGRectGetWidth(self.bounds) - kEmotionEmojiSize)/2, (CGRectGetHeight(self.bounds) - kEmotionEmojiSize)/2, kEmotionEmojiSize, kEmotionEmojiSize);
self.emojiLabel.frame = CGRectMake((CGRectGetWidth(self.bounds) - kEmotionEmojiSize)/2, (CGRectGetHeight(self.bounds) - kEmotionEmojiSize)/2, kEmotionEmojiSize, kEmotionEmojiSize);
}
/**
按照序号,从小到大
@param cell cell
@return 排序
*/
- (NSComparisonResult)sortAscCells:(INEmotionSystemEmojiCell *)cell {
//先按照时间排序
NSComparisonResult result = [self.cellNumber compare:cell.cellNumber];
return result;
}
@end
2.5 自定义贴图表情的UICollectionViewCell
INEmotionCustomEmojiCell.h
#import <UIKit/UIKit.h>
#import "UIColor+Addition.h"
@interface INEmotionCustomEmojiCell : UICollectionViewCell
@property (nonatomic, strong) UIImageView *emojiImageView;
/**
cell的序号
*/
@property (nonatomic, strong) NSString *cellNumber;
/**
按照序号,从小到大
@param cell cell
@return 排序
*/
- (NSComparisonResult)sortAscCells:(INEmotionCustomEmojiCell *)cell;
@end
INEmotionCustomEmojiCell.m
#import "INEmotionCustomEmojiCell.h"
static CGFloat kCustomEmotionEmojiSize = 80.0;
@implementation INEmotionCustomEmojiCell
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.emojiImageView = [[UIImageView alloc] initWithFrame:CGRectMake((CGRectGetWidth(self.bounds) - kCustomEmotionEmojiSize)/2, (CGRectGetHeight(self.bounds) - kCustomEmotionEmojiSize)/2, kCustomEmotionEmojiSize, kCustomEmotionEmojiSize)];
self.emojiImageView.backgroundColor = [UIColor randomColor];
self.emojiImageView.layer.cornerRadius = 4;
self.emojiImageView.layer.masksToBounds = YES;
[self addSubview:self.emojiImageView];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.emojiImageView.frame = CGRectMake((CGRectGetWidth(self.bounds) - kCustomEmotionEmojiSize)/2, (CGRectGetHeight(self.bounds) - kCustomEmotionEmojiSize)/2, kCustomEmotionEmojiSize, kCustomEmotionEmojiSize);
}
/**
按照序号,从小到大
@param cell cell
@return 排序
*/
- (NSComparisonResult)sortAscCells:(INEmotionCustomEmojiCell *)cell {
//先按照时间排序
NSComparisonResult result = [self.cellNumber compare:cell.cellNumber];
return result;
}
@end
至此整个效果的代码实现完了。
三、小结
iOS开发-聊天emoji表情与自定义动图表情左右滑动控件.使用UICollectionView实现。
学习记录,每天不停进步。