前沿
最近公司也在搞一个群聊功能,再看到产品设计后就想到了肯定是要仿照微信进行群成员头像拼接的方式了。说时迟那时快,两三下就搞定了哈!为了方便各位码友,特将源码献上。不用客气。
先来一张示例图
7人群聊.jpg
言归正传
1、使用场景
一般情况都是在cell上面进行展示,如果频繁的进行图片拼接操作肯定非常耗性能,因此肯定要进行性能优化。
2、使用逻辑
各位明白人肯定想到用缓存的,类似于微信再不存在点击群聊详情的时候我们没有必要去重新生成新的群聊拼接图片。我这边采用了两张方式获取缓存的拼接图片。
/**
类似于微信:如果不点击图片详情则不会主动更新群图片
*/
- (NSData*)avatarDataWithGroupId:(NSString*)groupId headImgUrls:(NSString*)imgUrls;
/**
在非详情时,根据群id获取头像
*/
- (NSData*)avatarDataWithGroupId:(NSString*)groupId;
实现方法 仅供参考
- (NSData*)avatarDataWithGroupId:(NSString*)groupId headImgUrls:(NSString*)imgUrls
{
if(groupId == nil || imgUrls.length == 0)return nil;
[_db open];
NSData* resultData = nil;
// 根据请求参数查询数据
FMResultSet *resultSet = nil;
resultSet = [_db executeQuery:@"SELECT * FROM IM_GROUP_AVATAR_Table WHERE groupid = ? AND imgurls = ? ;",groupId,imgUrls];
// 遍历查询结果
while (resultSet.next) {
NSData *value = [resultSet objectForColumn:@"avatar"];
if(value){
resultData = value;
break;
}
}
[_db close];
if(resultData == nil){///这种情况下找不到对应的图片,则说明群聊的图片集合更新了
[self deleteGroupAvatarDataWithGroupId:groupId];
return nil;
}
return resultData;
}
- (NSData*)avatarDataWithGroupId:(NSString*)groupId
{
if(groupId == nil)return nil;
[_db open];
NSData* resultData = nil;
// 根据请求参数查询数据
FMResultSet *resultSet = nil;
resultSet = [_db executeQuery:@"SELECT * FROM IM_GROUP_AVATAR_Table WHERE groupid = ?;",groupId];
// 遍历查询结果
while (resultSet.next) {
NSData *value = [resultSet objectForColumn:@"avatar"];
if(value){
resultData = value;
break;
}
}
[_db close];
if(resultData == nil){
return nil;
}
return resultData;
}
///当通过groupid和imageurls寻找头像无法找到时,则说明头像更改了,则先将数据库中的无效群头像删除
- (void)deleteGroupAvatarDataWithGroupId:(NSString*)groupId
{
[_db open];
BOOL isFlag = [_db executeUpdate:@"DELETE FROM IM_GROUP_AVATAR_Table WHERE groupid = ? ;",groupId];
[_db close];
if(isFlag){
KFLog(@"已删除无效的群头像");
}
}
图片的拼接
我这里的图片加载方式是大家都用的SDWebImage,相信你不会陌生!
这里就直接贴上拼接的源码,
///给一个宽度和高度一致的大图片,用来显示画布的大小
static const CGFloat nomalViewWidth = 200;
@implementation BDCreatGroupAvatar
+ (void)createGroupAvatar:(NSArray *)group completeBlock:(void (^)(NSData *groupAvatar))completeBlock {
__block NSInteger loadCount = 0;
NSInteger avatarCount = group.count > 9 ? 9 : group.count;
CGFloat width = 0;//先默认宽带
CGFloat space = 2;//图片的间距
CGFloat x = 0;
CGFloat y = 0;
if(avatarCount == 1){
width = nomalViewWidth-2*3;
}else if (avatarCount >= 2 && avatarCount <= 4){
width = (nomalViewWidth-3*space)*0.5;
}else{
width = 1.0*(nomalViewWidth-4*space)/3;
}
NSMutableArray *resultDataArray = [NSMutableArray arrayWithCapacity:group.count];
NSMutableArray *resultFrameArray = [NSMutableArray arrayWithCapacity:group.count];
for (int i = 0; i<group.count; i++) {
NSString *avatarUrl = group[I];
if(avatarCount == 1){
x = 3;
y = 3;
}else if (avatarCount == 2){
x = (i*width)+space;
y = (nomalViewWidth-width)*0.5;
}else if (avatarCount == 3){
if(i == 0){
x = (nomalViewWidth-width)*0.5;
y = space;
}else if (i == 1){
x = space;
y = width+2*space;
}else{
x = width+2*space;
y = width+2*space;
}
}else if (avatarCount == 4){
NSInteger maxLinePerCount = 2;
x = (i%maxLinePerCount) * (width+space)+space;
y = (i/maxLinePerCount) * (width+space)+space;
}else if (avatarCount == 5){
if(i == 0){
x = (nomalViewWidth*0.5)-(space*0.5)-width;
y = (nomalViewWidth-2*width-space)*0.5;
}else if (i == 1){
x = (nomalViewWidth*0.5)+(space*0.5);
y = (nomalViewWidth-2*width-space)*0.5;
}else{
x = (i-2)*(width+space)+space;
y = (nomalViewWidth-2*width-space)*0.5+width+space;
}
}else if (avatarCount == 6){
NSInteger maxLinePerCount = 3;
CGFloat ySpace = (nomalViewWidth-2*width-space)*0.5;
x = (i%maxLinePerCount) * (width+space)+space;
y = (i/maxLinePerCount) * (width+space)+ySpace;
}else{
NSInteger maxLinePerCount = 3;
x = (i%maxLinePerCount) * (width+space)+space;
y = (i/maxLinePerCount) * (width+space)+space;
}
CGRect imageFrame = CGRectMake(x, y, width, width);
NSValue *frameValue = [NSValue valueWithCGRect:imageFrame];
[resultFrameArray addObject:frameValue];
[[SDWebImageManager sharedManager] loadImageWithURL:[NSURL URLWithString:avatarUrl] options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
loadCount ++ ;
UIImage *loadImage = nil;
if(image == nil){
loadImage = BD_PLACE_HEAD_IMAGE;
}else{
loadImage = image;
}
///将压缩后的图片进行暂存
NSData *resultData = [loadImage compressQualityWithMaxLength:1024*1024*0.5];
[resultDataArray addObject:resultData];
if (loadCount == avatarCount) { //图片全部下载完成
CGSize contentSize = CGSizeMake(nomalViewWidth, nomalViewWidth);
//1.创建上下文尺寸
UIGraphicsBeginImageContextWithOptions(contentSize, true, 0.0);
CGContextRef ref = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(ref, 1.0*239/255, 1.0*239/255, 1.0*239/255, 1);
CGContextFillRect(ref, CGRectMake(0, 0, nomalViewWidth, nomalViewWidth));
for (int i = 0; i<resultDataArray.count; i++) {
NSData *imageData = resultDataArray[I];
NSValue *frameValue = resultFrameArray[I];
CGRect imageViewFrame = frameValue.CGRectValue;
UIImage *image = [[UIImage alloc]initWithData:imageData];
[image drawInRect:imageViewFrame];
}
UIImage *resultImg = UIGraphicsGetImageFromCurrentImageContext();//从当前上下文中获得最终图片
UIGraphicsEndImageContext();//关闭上下文
NSData *thetData = [resultImg compressQualityWithMaxLength:1024*1024*0.5];
dispatch_async(dispatch_get_main_queue(), ^{
if (completeBlock) {
completeBlock(thetData);
}
});
}
}];
}
}
代码简单说明:
1、设置画布背景色
CGContextRef ref = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(ref, 1.0*239/255, 1.0*239/255, 1.0*239/255, 1);
CGContextFillRect(ref, CGRectMake(0, 0, nomalViewWidth, nomalViewWidth));
2、以上针对1至6张图片时,是一个个判断的,如果各位有好的点子可以回复告知一下哈!
3、resultDataArray和resultFrameArray里面的元素没有确保按位置匹配,这个一般应该不会要求吧,如果有要求的话,可以使用信号量,每次加载一张图片完成后再加载第二张,这样顺序就不会乱了。
以上代码有点长,但是呢已经将整个流程代码贴上了,不喜欢可以喷我哈,我只是想分享一下,如果喜欢就点个赞吧!