SDWebImage+FLAnimatedImage,给Tabbar添加动图
- 前言
PM看了N个产品后觉得点击Tabbar图片能动起来是个很好的体验,本着别人有的我们也得有,能抄一套是一套的原则,我在PM的威逼利诱下开始了探索之路。
首先说为什么要用SDWebImage而不是本地去直接加载GIF或者去写帧动画,因为PM的需求是Tabbar的icon在一些重要的活动或者节假日必须要后台去返回,日常就用本地默认的icon,所以考虑用SDWebImage+FLAnimatedImage。
为什么要在GIF动画执行完一次就停止,只因为动画一直循环播放不是PM想要的效果而已。
完工后效果:
tababr_gif.gif
导出GIF有卡段,视频链接:https://player.youku.com/embed/XNDAxNDIxMjY4NA==
- 参考
SDWebImage:https://github.com/SDWebImage/SDWebImage
FLAnimatedImage:https://github.com/Flipboard/FLAnimatedImage
- FLAnimatedImage
2. FLAnimatedImage的主要API和属性
/ **GIF封面图片 ** /
@property (nonatomic, strong, readonly) UIImage *posterImage;
/ **GIF封面图片尺寸 ** /
@property (nonatomic, assign, readonly) CGSize size;
/ **GIF循环播放次数 ** /
@property (nonatomic, assign, readonly) NSUInteger loopCount;
/ **每帧图片的显示时间集合 ** /
@property (nonatomic, strong, readonly) NSDictionary *delayTimesForIndexes;
/ **动画的帧数量 ** /
@property (nonatomic, assign, readonly) NSUInteger frameCount;
/ **当前被缓存的帧图片的总数量 ** /
@property (nonatomic, assign, readonly) NSUInteger frameCacheSizeCurrent;
// / **允许缓存多少帧图片 ** /
@property (nonatomic, assign) NSUInteger frameCacheSizeMax;
// 允/ **从缓存中获取传递下标的图片 ** /
- (UIImage *)imageLazilyCachedAtIndex:(NSUInteger)index;
/ **初始化 FLAnimatedImage ** /
- (instancetype)initWithAnimatedGIFData:(NSData *)data;
- (instancetype)initWithAnimatedGIFData:(NSData *)data optimalFrameCacheSize:(NSUInteger)optimalFrameCacheSize predrawingEnabled:(BOOL)isPredrawingEnabled NS_DESIGNATED_INITIALIZER;
+ (instancetype)animatedImageWithGIFData:(NSData *)data;
3. FLAnimatedImageView的主要API和属性
/ **GIF动画播放一次之后的回调Block ** /
@property (nonatomic, copy) void(^loopCompletionBlock)(NSUInteger loopCountRemaining);
/ **GIF动画当前显示的帧图片 ** /
@property (nonatomic, strong, readonly) UIImage *currentFrame;
/** GIF动画当前显示的帧图片索引 **/
@property (nonatomic, assign, readonly) NSUInteger currentFrameIndex;
4. FLAnimatedImage使用 在我的项目中TabbarItem是自定义的View,View上添加FLAnimatedImageView
【SDWebImage+FLAnimatedImage,给Tabbar添加动图】Item.h
#import
#import "WKTabbarModel.h"
#import "FLAnimatedImageView+WebCache.h"
#import "FLAnimatedImage.h"@interface WKTabbarItem :UIView@property (strong, nonatomic) UILabel *itemLabel;
@property (strong, nonatomic) FLAnimatedImageView *itemImageView;
/**
是否选中
*/
@property (assign, nonatomic) BOOL isSelected;
/**
tababr item数据
@param model model
@param index index
*/
- (void)setModel:(WKTabbarModel *)model
Index:(NSInteger)index;
/**
点击tabbar item放大
@param fromValue 动画的起始状态值
@param toValue 动画的结束状态值
@param height height
*/
- (void)changeShowScaleAnimationFromValue:(CGFloat)fromValue
ToValue:(CGFloat)toValue
Height:(CGFloat)height;
Item.m
#import "WKTabbarItem.h"
#import "CAAnimation+WKAnimation.h"@interface WKTabbarItem()
{
WKTabbarModel *_model;
}@property (assign, nonatomic) NSIntegerindex;
@end@implementation WKTabbarItem- (instancetype)initWithFrame:(CGRect)frame{if (self = [super initWithFrame:frame]) {[self setLayoutView];
}
return self;
}- (void)setLayoutView{_itemImageView = [FLAnimatedImageView new];
_itemImageView.contentMode = UIViewContentModeScaleAspectFit;
[self addSubview:_itemImageView];
_itemImageView.sd_layout
.topSpaceToView(self , (IS_IPHONE_10)?(10):(5))
.widthRatioToView(self , 1)
.centerXEqualToView(self )
.heightIs((IS_IPHONE_10)?(28):(26));
}//tababr model
- (void)setModel:(WKTabbarModel *)model Index:(NSInteger)index{_model = model;
_index = index;
[self initItemData];
}//tababr 图片文字赋值
- (void)initItemData{_itemLabel.text = _model.title;
if (self.isSelected) {_itemLabel.textColor = WKFontOrangeColor;
[self setSelectedImage];
}else{_itemLabel.textColor = WKLightGrayColor;
[self setNormalImage];
}
}//动画改变item
- (void)changeShowScaleAnimationFromValue:(CGFloat)fromValue ToValue:(CGFloat)toValue Height:(CGFloat)height{[self initItemData];
[WKTabbarStyleManager showScaleAnimationInView:_itemImageViewFromValue:fromValueToValue:toValue];
[UIView animateWithDuration:0.35 animations:^{if (toValue =https://www.it610.com/article/= 1) {self.mj_y = 0;
self.mj_h = height;
}else{
self.mj_y = -20;
self.mj_h = height + 20;
}
}];
}//设置选中图片
- (void)setSelectedImage{NSString *fileName = [_model.selectedImageUrl pathExtension];
if([fileName isEqualToString:@"gif"]){//gif用FLAnimatedImage加载NSArray *selImageArr = [WKUserDefaultsTool loadTabbarDownloaderSelectedImages];
NSArray *norIamgeArr = [WKUserDefaultsTool loadTabbarDownloaderNormalImages];
if (selImageArr.count >= 4 && norIamgeArr.count >= 4) { //下载保存成功后走本地保存的imageData,这样写是为了避免每次启动刷新NSData *imageData = selImageArr[_index];
[self setAnimatedImageWithData:imageData];
}else{ //第一次加载新的图片,走sd缓存[self setAnimatedImageWithData:[self setImageDataWithUrl:_model.selectedImageUrl]];
}
}else{
_itemImageView.image = (![self setImageDataWithUrl:_model.selectedImageUrl])?([UIImage imageNamed:_model.selectedPlaceImage]):([UIImage imageWithData:[self setImageDataWithUrl:_model.selectedImageUrl]]);
}
}//设置未选中图片
- (void)setNormalImage{NSArray *selImageArr = [WKUserDefaultsTool loadTabbarDownloaderSelectedImages];
NSArray *norIamgeArr = [WKUserDefaultsTool loadTabbarDownloaderNormalImages];
if (selImageArr.count >= 4 && norIamgeArr.count >= 4) { //下载保存成功后走本地保存的imageData,这样写是为了避免每次启动刷新_itemImageView.image = [UIImage imageWithData:norIamgeArr[_index]];
}else{ //第一次加载新的图片,走sd缓存
[_itemImageView sd_setImageWithURL:[NSURL URLWithString:_model.normalImageUrl] placeholderImage:[UIImage imageNamed:_model.normalPlaceImage]];
}
}/*加载选中gif动态图片
如果是本地的gif,data可是直接写成本地路径的image转data
我的项目中是sdwebimage的缓存*/
- (void)setAnimatedImageWithData:(NSData *)imageData{if (imageData) {WeakSelf(weakSelf);
FLAnimatedImage *gifImage= [FLAnimatedImage animatedImageWithGIFData:imageData];
_itemImageView.animatedImage = gifImage;
//gif播放完毕停止到倒数第二张
_itemImageView.loopCompletionBlock = ^(NSUInteger loopCountRemaining) {
weakSelf.itemImageView.image = [gifImage imageLazilyCachedAtIndex:gifImage.frameCount - 2];
};
}}//获取图片地址在sdwebimage缓存路径
- (NSData *)setImageDataWithUrl:(NSString *)url{NSString *path = [[[SDWebImageManager sharedManager] imageCache] defaultCachePathForKey:url];
NSData*imageData = https://www.it610.com/article/[NSData dataWithContentsOfFile:path];
returnimageData;
}
- APP下载后台返回的Tabbar图片
#pragma mark ==================== tabbar样式 ====================
+ (void)requestTabbarStyle{dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{AppDelegate *aDelegate= (AppDelegate *)WKApplication;
NSString *url= [NSString stringWithFormat:@"%@/%@",apiAddressDEF,bottomButtonsDEF];
NSString *encryStr= [NSString stringWithFormat:@"version=%@",APPVERSION];
NSDictionary *parmsEncry = [WKTool DictionaryFromString:encryStr];
[WKRequest requestPostWithPath:url Parameters:parmsEncry CallBack:^(id obj) {//obj[@"status"] = @"0";
if ([obj[@"status"]integerValue] == 1) {NSDictionary *dataDic = [WKTool dictionaryWithJsonString:[Encryption decrypt:obj[@"data"]]];
NSArray *dataList= dataDic[@"dataList"];
NSMutableArray *normolImgUrlArr= [NSMutableArray array];
//未选中
NSMutableArray *selectedImgUrlArr = [NSMutableArray array];
//选中
NSMutableArray *titleArr= [NSMutableArray array];
//文字// 选中
static NSArray *selCacheArr;
//获取sd缓存路径图片数组
static NSArray *selServerArr;
//获取sd第一次缓存图片数组
//非选中
static NSArray *norCacheArr;
//获取sd缓存路径图片数组
static NSArray *norServerArr;
//获取sd第一次缓存图片数组if (dataDic.allKeys > 0) {for (NSDictionary *dic in dataList) {[normolImgUrlArr addObject:dic[@"img1"]];
[selectedImgUrlArr addObject:dic[@"img2"]];
[titleArr addObject:dic[@"text"]];
}//后台更新图片,删除之前的缓存
if (![selectedImgUrlArr isEqualToArray:[WKUserDefaultsTool loadTabbarSelectedImg]]) {
[self clearImageCache];
}// 创建下载图片完成线程 group
dispatch_group_t group = dispatch_group_create();
//下载选中tabbar图片
[self tabbarThemeWithManager:aDelegate.sdWebImgManager IsSelected:YES ImageUrlArr:selectedImgUrlArr CacheBlock:^(NSArray *cacheImgArr) {selCacheArr = cacheImgArr;
} ServerBlock:^(NSArray *serverImgArr) {
selServerArr = serverImgArr;
} Group:group];
//下载非选中tabbar图片
[self tabbarThemeWithManager:aDelegate.sdWebImgManager IsSelected:NO ImageUrlArr:normolImgUrlArr CacheBlock:^(NSArray *cacheImgArr) {norCacheArr = cacheImgArr;
} ServerBlock:^(NSArray *serverImgArr) {
norServerArr = serverImgArr;
} Group:group];
// !!!!!! 下载图片完成后, 回到主线再更新tababr !!!!!!!
dispatch_group_notify(group, dispatch_get_main_queue(), ^{BOOL isSelImage = (selCacheArr.count >= 4 || selServerArr.count >= 4)?(YES):(NO);
BOOL isNorImage = (norCacheArr.count >= 4 || norServerArr.count >= 4)?(YES):(NO);
BOOL isSuccess= [[[NSUserDefaults standardUserDefaults]objectForKey:@"TabbrSuccess"] boolValue];
// 条件满足则表示全部下载完毕,避免下载的时间耗完图片可能并没有全部加载,会造成一些是本地一些是服务器图片的情况, 一次性加载刷新UI
if (isSelImage && isNorImage && !isSuccess) {[self saveTabbarStyleWithTitles:titleArr
NormolImages:normolImgUrlArr
SelectedImages:selectedImgUrlArr
ItemAnimationValue:(APPWIDTH <= 375)?(1.80):(2.0)];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"TabbrSuccess"];
}});
}else{ //清空缓存tabbar图片[self clearImageCache];
dispatch_async(dispatch_get_main_queue(), ^{
[self saveTabbarStyleWithTitles:nil NormolImages:nil SelectedImages:nil ItemAnimationValue:1.00];
});
}NSLog(@"******** %@tabbar样式 ******",dataDic);
}else if([obj[@"status"]integerValue] == 0){//清空缓存tabbar图片[self clearImageCache];
// 创建下载图片完成线程 group
dispatch_async(dispatch_get_main_queue(), ^{
[self saveTabbarStyleWithTitles:nil NormolImages:nil SelectedImages:nil ItemAnimationValue:1.00];
});
}}];
});
}/**
清除缓存
*/
+ (void)clearImageCache{[[SDImageCache sharedImageCache] clearMemory];
[[SDImageCache sharedImageCache] clearDiskOnCompletion:nil];
[WKUserDefaultsTool saveTabbarDownloaderSelectedImages:nil];
[WKUserDefaultsTool saveTabbarDownloaderNormalImages:nil];
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"TabbrSuccess"];
}/**
保存服务器并设置tabbar样式
@param titleArr 标题
@param normolImgArr 默认
@param selectedImgArr 选中
@param tabbarAnimationValue 放大动画区间
*/
+ (void)saveTabbarStyleWithTitles:(NSMutableArray *)titles NormolImages:(NSMutableArray *)normolImages SelectedImages:(NSMutableArray *)selectedImages ItemAnimationValue:(CGFloat)itemAnimationValue{[WKUserDefaultsTool saveTabbarText:titles
TabbarNormolImg:normolImages
TabbarSelectedImg:selectedImages
TabbarAnimationValue:itemAnimationValue];
AppDelegate *aDelegate= (AppDelegate *)WKApplication;
WKTabBarController *tabBarC = (WKTabBarController *)aDelegate.forceViewController.tabBarController;
[tabBarC.tabBarView setChangeWithSelectedIndex:WKVARIABLE.isTabbarIndex
ItemTitleArr:titles
ItemImageNorArr:normolImages
ItemImageSelArr:selectedImages];
}/**
服务器tabbar图片
回调cacheBlock和serverBlock的目的,用户可能处于弱网环境,在下载的时间耗完图片可能并没有全部加载,会造成一些是本地一些事服务器图片的情况
所以一次分别回调两个数组,然后计算count判断是全部下完,一次性全部显示 @param manager SDWebImageManager
@param isSelected 是否是下载选中图片(保存选中图片data,选中在某些情况会反gif,保存下来减少网络开销)
@param imageUrlArr 下载地址
@param cacheBlock 回调已下载成功的数组
@param serverBlock 回调第一次成功的数组
@param group dispatch_group_t
*/
+ (void)tabbarThemeWithManager:(SDWebImageManager *)managerIsSelected:(BOOL)isSelected ImageUrlArr:(NSArray *)imageUrlArr CacheBlock:(CacheIamgeBlock)cacheBlock ServerBlock:(ServerImageBlock)serverBlockGroup:(dispatch_group_t)group{NSMutableArray *cacheImgArr= [NSMutableArray array];
//本地保存过的
NSMutableArray *serverImgArr = [NSMutableArray array];
//下载成功过的for (NSString *imageUrlStr in imageUrlArr) {dispatch_group_enter(group);
NSString *imagePath = imageUrlStr;
NSData*imageData = https://www.it610.com/article/[self imageDataFromDiskCacheWithKey:imagePath];
if (imageData) {if (isSelected) {[cacheImgArr addObject:imageData];
[WKUserDefaultsTool saveTabbarDownloaderSelectedImages:cacheImgArr];
NSLog(@"+++++tabbar选中已保存 +++++");
}else{
[cacheImgArr addObject:imageData];
[WKUserDefaultsTool saveTabbarDownloaderNormalImages:cacheImgArr];
NSLog(@"+++++tabbar未选中已保存 +++++");
}
cacheBlock(cacheImgArr);
dispatch_group_leave(group);
} else {NSURL *url = [NSURL URLWithString:imageUrlStr];
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:url
options:SDWebImageDownloaderHighPriority
progress:nil
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {[[[SDWebImageManager sharedManager] imageCache] storeImage:image
imageData:data
forKey:url.absoluteString
toDisk:YES
completion:nil];
if (error) {
// 加载失败
NSLog(@"~~~~~~~~~~~tabbar下载失败 ~~~~~~~~~~~");
} else {
// 加载成功
NSLog(@"!!!!!!!!!tabbar下载完成 !!!!!!!!!");
[serverImgArr addObject:data];
serverBlock(serverImgArr);
}
dispatch_group_leave(group);
}];
}
}
}
至此,PM要的功能已实现,另外我的TabbarView也是自定义的,代码就不贴了,基本上就是调用自定义item的方法。其实也是觉得现在这种线程下载实现动画的方法还是太low,先放上做个笔记,过完新年来重构。
2019新年快乐,猪事顺利!
推荐阅读
- CVE-2020-16898|CVE-2020-16898 TCP/IP远程代码执行漏洞
- parallels|parallels desktop 解决网络初始化失败问题
- EffectiveObjective-C2.0|EffectiveObjective-C2.0 笔记 - 第二部分
- android第三方框架(五)ButterKnife
- 2.6|2.6 Photoshop操作步骤的撤消和重做 [Ps教程]
- 深入理解Go之generate
- 喂,你结婚我给你随了个红包
- jhipster|jhipster 升级无效问题
- 成交的种子咖啡冥想
- 一百二十三夜,请嫁给我