實(shí)現(xiàn)效果
項(xiàng)目開發(fā)過程中,我們?cè)谟行╉撁嬲故局霸⑷瑁枰恍╉撁嫘Ч麃磉^渡粮坞。例如:網(wǎng)絡(luò)數(shù)據(jù)加載出來之前蚊荣,頁面的展示;復(fù)雜的業(yè)務(wù)邏輯處理完成之前的頁面顯示莫杈;上傳文件未完成之前的頁面樣式等等互例。當(dāng)然,需求不同筝闹,其展示樣式也就千變?nèi)f化媳叨。今天來實(shí)現(xiàn)一下上圖所示的功能。
本篇文章关顷,只作為個(gè)人學(xué)習(xí)使用糊秆。
具體實(shí)現(xiàn)代碼如下:
創(chuàng)建UIView的category
UIView+Animated.h文件
#import <UIKit/UIKit.h>
typedef enum {
TABViewLoadAnimationDefault = 0, //默認(rèn)沒有動(dòng)畫
TABViewLoadAnimationShort, //動(dòng)畫先變短再變長(zhǎng)
TABViewLoadAnimationLong //動(dòng)畫先變長(zhǎng)再變短
}TABViewLoadAnimationStyle; //view動(dòng)畫類型枚舉
@interface UIView (Animated)
@property (nonatomic) TABViewLoadAnimationStyle loadStyle;
@end
UIView+Animated.m文件
#import "UIView+Animated.h"
#import <objc/runtime.h>
@implementation UIView (Animated)
- (TABViewLoadAnimationStyle)loadStyle {
NSNumber *value = objc_getAssociatedObject(self, @selector(loadStyle));
return value.intValue;
}
- (void)setLoadStyle:(TABViewLoadAnimationStyle)loadStyle {
objc_setAssociatedObject(self, @selector(loadStyle), @(loadStyle), OBJC_ASSOCIATION_ASSIGN);
}
@end
創(chuàng)建UITableView的category
UITableView+Animated.h文件
#import <UIKit/UIKit.h>
typedef enum {
TABTableViewAnimationDefault = 0, //沒有動(dòng)畫,默認(rèn)
TABTableViewAnimationStart, //開始動(dòng)畫
TABTableViewAnimationEnd //結(jié)束動(dòng)畫
}TABTableViewAnimationStyle; //table動(dòng)畫狀態(tài)枚舉
@interface UITableView (Animated)
@property (nonatomic) TABTableViewAnimationStyle animatedStyle;
@end
UITableView+Animated.m文件
#import "UITableView+Animated.h"
#import <objc/runtime.h>
@implementation UITableView (Animated)
+ (void)load {
//Ensure that the exchange method executed only once.
//保證交換方法只執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//Gets the viewDidLoad method to the class,whose type is a pointer to a objc_method structure.
//獲取到這個(gè)類的viewDidLoad方法议双,它的類型是一個(gè)objc_method結(jié)構(gòu)體的指針
Method originMethod = class_getInstanceMethod([self class], @selector(setDelegate:));
//Get the method you created.
//獲取自己創(chuàng)建的方法
Method newMethod = class_getInstanceMethod([self class], @selector(tab_setDelegate:));
IMP newIMP = method_getImplementation(newMethod);
BOOL isAdd = class_addMethod([self class], @selector(tab_setDelegate:), newIMP, method_getTypeEncoding(newMethod));
if (isAdd) {
class_replaceMethod([self class], @selector(setDelegate:), newIMP, method_getTypeEncoding(newMethod));
}else {
//exchange
method_exchangeImplementations(originMethod, newMethod);
}
});
}
- (void)tab_setDelegate:(id<UITableViewDelegate>)delegate {
//Ensure that the exchange method executed only once.
//保證交換方法只執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL oldSelector = @selector(tableView:numberOfRowsInSection:);
SEL newSelector = @selector(tab_tableView:numberOfRowsInSection:);
Method oldMethod_del = class_getInstanceMethod([delegate class], oldSelector);
Method oldMethod_self = class_getInstanceMethod([self class], oldSelector);
Method newMethod = class_getInstanceMethod([self class], newSelector);
// 若未實(shí)現(xiàn)代理方法痘番,則先添加代理方法
BOOL isSuccess = class_addMethod([delegate class], oldSelector, class_getMethodImplementation([self class], newSelector), method_getTypeEncoding(newMethod));
if (isSuccess) {
class_replaceMethod([delegate class], newSelector, class_getMethodImplementation([self class], oldSelector), method_getTypeEncoding(oldMethod_self));
} else {
// 若已實(shí)現(xiàn)代理方法,則添加 hook 方法并進(jìn)行交換
BOOL isVictory = class_addMethod([delegate class], newSelector, class_getMethodImplementation([delegate class], oldSelector), method_getTypeEncoding(oldMethod_del));
if (isVictory) {
class_replaceMethod([delegate class], oldSelector, class_getMethodImplementation([self class], newSelector), method_getTypeEncoding(newMethod));
}
}
});
[self tab_setDelegate:delegate];
}
#pragma mark - TABTableViewDelegate
- (NSInteger)tab_tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView.animatedStyle == TABTableViewAnimationStart) {
return 5;
}
return [self tab_tableView:tableView numberOfRowsInSection:section];
}
#pragma mark - Getter / Setter
- (TABTableViewAnimationStyle)animatedStyle {
NSNumber *value = objc_getAssociatedObject(self, @selector(animatedStyle));
//動(dòng)畫過程中設(shè)置為不可滾動(dòng)平痰,暫時(shí)不要修改汞舱,滾動(dòng)會(huì)造成一定的問題
if (value.intValue == 1) {
self.scrollEnabled = NO;
}else {
self.scrollEnabled = YES;
}
return value.intValue;
}
- (void)setAnimatedStyle:(TABTableViewAnimationStyle)animatedStyle {
objc_setAssociatedObject(self, @selector(animatedStyle), @(animatedStyle), OBJC_ASSOCIATION_ASSIGN);
}
@end
創(chuàng)建LGJViewAnimated 繼承自NSObject
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "UIView+Animated.h"
@interface LGJViewAnimated : NSObject
+ (void)startOrEndAnimated:(UITableViewCell *)cell;
/**
加載CALayer,設(shè)置動(dòng)畫,同時(shí)啟動(dòng)
@param view 需要?jiǎng)赢嫷膙iew
@param color 動(dòng)畫顏色
*/
+ (void)initLayerWithView:(UIView *)view withColor:(UIColor *)color;
/**
根據(jù)動(dòng)畫類型設(shè)置對(duì)應(yīng)基礎(chǔ)動(dòng)畫
@param style 動(dòng)畫類型
@return 動(dòng)畫
*/
+ (LGJViewAnimated *)scaleXAnimation:(TABViewLoadAnimationStyle)style;
@end
#import "LGJViewAnimated.h"
#import "TABMethod.h"
#import "UITableView+Animated.h"
@implementation LGJViewAnimated
+ (void)startOrEndAnimated:(UITableViewCell *)cell {
UITableView *superView = (UITableView *)cell.superview;
switch (superView.animatedStyle) {
case TABTableViewAnimationStart:
//添加并開啟動(dòng)畫
for (int i = 0; i < cell.contentView.subviews.count; i++) {
UIView *v = cell.contentView.subviews[i];
if ( v.loadStyle != TABViewLoadAnimationDefault ) {
[TABViewAnimated initLayerWithView:v withColor:kBackColor];
}
}
break;
case TABTableViewAnimationEnd:
if ( cell.contentView.subviews.count > 0 ) {
//移除動(dòng)畫圖層
for (int i = 0; i < cell.contentView.subviews.count; i++) {
UIView *v = cell.contentView.subviews[i];
if ( v.layer.sublayers.count > 0 ) {
NSArray<CALayer *> *subLayers = v.layer.sublayers;
NSArray<CALayer *> *removedLayers = [subLayers filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
if ([evaluatedObject isKindOfClass:[CALayer class]]) {
//找出CALayer是你需要移除的,這里根據(jù)背景色來判斷的
CALayer *layer = (CALayer *)evaluatedObject;
if (CGColorEqualToColor(layer.backgroundColor,kBackColor.CGColor)) {
return YES;
}
return NO;
}
return NO;
}]];
[removedLayers enumerateObjectsUsingBlock:^(CALayer * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj removeFromSuperlayer];
}];
}
}
}
break;
default:
break;
}
}
+ (void)initLayerWithView:(UIView *)view withColor:(UIColor *)color {
CALayer *layer = [[CALayer alloc]init];
layer.frame = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height);
layer.backgroundColor = kBackColor.CGColor;
layer.anchorPoint = CGPointMake(0, 0);
layer.position = CGPointMake(0, 0);
// 添加一個(gè)基本動(dòng)畫
[layer addAnimation:[self scaleXAnimation:view.loadStyle] forKey:@"scaleAnimation"];
[view.layer addSublayer:layer];
}
+ (CABasicAnimation *)scaleXAnimation:(TABViewLoadAnimationStyle)style {
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.scale.x"];
anim.removedOnCompletion = NO;
anim.duration = 0.4;
anim.autoreverses = YES; //往返都有動(dòng)畫
anim.repeatCount = MAXFLOAT; //執(zhí)行次數(shù)
switch (style) {
case TABViewLoadAnimationShort:{
anim.toValue = @0.6;
}
break;
case TABViewLoadAnimationLong:{
anim.toValue = @1.6;
}
break;
default:{
anim.toValue = @0.6;
}
}
return anim;
}
@end
以上就是具體實(shí)現(xiàn)工作過程宗雇,那么怎么用呢兵拢?很簡(jiǎn)單往扔,我們只需要在需要此功能列表的cell 的layoutSubviews方法的最后加入代碼
//運(yùn)行動(dòng)畫/移除動(dòng)畫
[TABViewAnimated startOrEndAnimated:self];
在需要開始動(dòng)畫的時(shí)刻執(zhí)行
_mainTV.animatedStyle = TABTableViewAnimationStart; //開啟動(dòng)畫
在需要結(jié)束動(dòng)畫的時(shí)刻執(zhí)行
//停止動(dòng)畫
_mainTV.animatedStyle = TABTableViewAnimationEnd;
即可入挣。
另外霹购,在動(dòng)畫頁面顯示的時(shí)候盡量不要做給cell賦值的操作
//在加載動(dòng)畫的時(shí)候宦芦,即未獲得數(shù)據(jù)時(shí)嘹履,不要走加載控件數(shù)據(jù)的方法
if (_mainTV.animatedStyle != TABTableViewAnimationStart) {
[cell initWithData:dataArray[indexPath.row]];
}