上一节讲解了POPAnimation的相关内容,并说起到动画的操做其实是在POPAnimator中进行。本文将主要解析POPAnimator.函数
POPAnimator是pop动画的核心类,负责存储和管理添加进来的动画。oop
@interface POPAnimator ()
{
CADisplayLink *_displayLink; //定时器,用于渲染动画
POPAnimatorItemList _list; //存储须要执行的动画
CFMutableDictionaryRef _dict; //用于存储obj中对应的动画
NSMutableArray *_observers; //用于存储监听者
POPAnimatorItemList _pendingList; //用于临时存储添加的动画
CFRunLoopObserverRef _pendingListObserver;
CFTimeInterval _slowMotionStartTime; //如下三者是为了校订模拟器时间的属性
CFTimeInterval _slowMotionLastTime;
CFTimeInterval _slowMotionAccumulator;
CFTimeInterval _beginTime; //动画开始时间
pthread_mutex_t _lock; //用于保证线程安全的锁
BOOL _disableDisplayLink;
}
@end
复制代码
初始化方法主要建立了定时器、存储结构_dict和锁_lock。post
- (instancetype)init
{
self = [super init];
if (nil == self) return nil;
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)];
_displayLink.paused = YES;
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
_dict = POPDictionaryCreateMutableWeakPointerToStrongObject(5);
pthread_mutex_init(&_lock, NULL);
return self;
}
复制代码
- (void)render
{
CFTimeInterval time = [self _currentRenderTime];
[self renderTime:time];
}
- (void)renderTime:(CFTimeInterval)time
{
[self _renderTime:time items:_list];
}
复制代码
咱们知道当动画开始后,定时器会每隔16ms调用render方法来进行渲染动画。动画
- (void)_renderTime:(CFTimeInterval)time items:(std::list<POPAnimatorItemRef>)items
{
// begin transaction with actions disabled
[CATransaction begin];
[CATransaction setDisableActions:YES];
// notify delegate
__strong __typeof__(_delegate) delegate = _delegate;
[delegate animatorWillAnimate:self];
// lock
pthread_mutex_lock(&_lock);
// count active animations
const NSUInteger count = items.size();
if (0 == count) {
// unlock
pthread_mutex_unlock(&_lock);
} else {
// copy list into vector
std::vector<POPAnimatorItemRef> vector{ items.begin(), items.end() };
// unlock
pthread_mutex_unlock(&_lock);
for (auto item : vector) {
[self _renderTime:time item:item];
}
}
// notify observers
for (id observer in self.observers) {
[observer animatorDidAnimate:(id)self];
}
// lock
pthread_mutex_lock(&_lock);
// update display link
updateDisplayLink(self);
// unlock
pthread_mutex_unlock(&_lock);
// notify delegate and commit
[delegate animatorDidAnimate:self];
[CATransaction commit];
}
复制代码
a.[CATransaction setDisableActions:YES]
的主要做用是关闭隐式动画,避免影响动画的执行。ui
b. updateDisplayLink
方法是为了不定时器的没必要要工做,当监听者队列或动画队列中为零时,会暂停定时器的工做,直到有动画或监听者的加入为止。spa
static void updateDisplayLink(POPAnimator *self)
{
BOOL paused = (0 == self->_observers.count && self->_list.empty()) || self->_disableDisplayLink;
if (paused != self->_displayLink.paused) {
FBLogAnimInfo(paused ? @"pausing display link" : @"unpausing display link");
self->_displayLink.paused = paused;
}
}
复制代码
c. 遍历动画队列,逐个渲染动画线程
- (void)_renderTime:(CFTimeInterval)time item:(POPAnimatorItemRef)item
{
id obj = item->object;
POPAnimation *anim = item->animation;
POPAnimationState *state = POPAnimationGetState(anim);
if (nil == obj) {
// object exists not; stop animating
NSAssert(item->unretainedObject, @"object should exist");
stopAndCleanup(self, item, true, false);
} else {
// start if need
//更新POPAnimationState中的active和paused属性
state->startIfNeeded(obj, time, _slowMotionAccumulator);
// only run active, not paused animations
if (state->active && !state->paused) {
// object exists; animate
//更新动画的属性值
applyAnimationTime(obj, state, time);
FBLogAnimDebug(@"time:%f running:%@", time, item->animation);
if (state->isDone()) {
// set end value
//更新动画属性
applyAnimationToValue(obj, state);
//处理重复的动画
state->repeatCount--;
if (state->repeatForever || state->repeatCount > 0) {
if ([anim isKindOfClass:[POPPropertyAnimation class]]) {
POPPropertyAnimation *propAnim = (POPPropertyAnimation *)anim;
id oldFromValue = propAnim.fromValue;
propAnim.fromValue = propAnim.toValue;
if (state->autoreverses) {
if (state->tracing) {
[state->tracer autoreversed];
}
if (state->type == kPOPAnimationDecay) {
POPDecayAnimation *decayAnimation = (POPDecayAnimation *)propAnim;
decayAnimation.velocity = [decayAnimation reversedVelocity];
} else {
propAnim.toValue = oldFromValue;
}
} else {
if (state->type == kPOPAnimationDecay) {
POPDecayAnimation *decayAnimation = (POPDecayAnimation *)propAnim;
id originalVelocity = decayAnimation.originalVelocity;
decayAnimation.velocity = originalVelocity;
} else {
propAnim.fromValue = oldFromValue;
}
}
}
state->stop(NO, NO);
state->reset(true);
state->startIfNeeded(obj, time, _slowMotionAccumulator);
} else {
stopAndCleanup(self, item, state->removedOnCompletion, YES);
}
}
}
}
}
复制代码
在该方法中调用了不少POPAnimationState中的方法,咱们先暂时放着,先探讨下里面两个比较重要的方法:applyAnimationTime
和applyAnimationToValue
d. applyAnimationTime
和applyAnimationToValue
方法
static void applyAnimationTime(id obj, POPAnimationState *state, CFTimeInterval time) {
if (!state->advanceTime(time, obj)) {
return;
}
POPPropertyAnimationState *ps = dynamic_cast<POPPropertyAnimationState*>(state);
if (NULL != ps) {
updateAnimatable(obj, ps);
}
state->delegateApply();
}
static void applyAnimationToValue(id obj, POPAnimationState *state) {
POPPropertyAnimationState *ps = dynamic_cast<POPPropertyAnimationState*>(state);
if (NULL != ps) {
// finalize progress
ps->finalizeProgress();
// write to value, updating only if needed
updateAnimatable(obj, ps, true);
}
state->delegateApply();
}
复制代码
对比二者,咱们能够看到它们都调用了updateAnimatable
方法:
static void updateAnimatable(id obj, POPPropertyAnimationState *anim, bool shouldAvoidExtraneousWrite = false) {
// handle user-initiated stop or pause; halt animation
if (!anim->active || anim->paused)
return;
if (anim->hasValue()) { //判断是否有数据
POPAnimatablePropertyWriteBlock write = anim->property.writeBlock;
if (NULL == write)
return;
// current animation value
VectorRef currentVec = anim->currentValue();
//判断是否须要每一帧都要更新
if (!anim->additive) {
// if avoiding extraneous writes and we have a read block defined
// 若读取的数据和当前的数据一致,则不必再写一次数据
if (shouldAvoidExtraneousWrite) {
POPAnimatablePropertyReadBlock read = anim->property.readBlock;
if (read) {
// compare current animation value with object value
Vector4r currentValue = currentVec->vector4r();
Vector4r objectValue = read_values(read, obj, anim->valueCount);
if (objectValue == currentValue) {
return;
}
}
}
// update previous values; support animation convergence
anim->previous2Vec = anim->previousVec;
anim->previousVec = currentVec;
// write value
// 写入数据
write(obj, currentVec->data());
if (anim->tracing) {
[anim->tracer writePropertyValue:POPBox(currentVec, anim->valueType, true)];
}
} else {
POPAnimatablePropertyReadBlock read = anim->property.readBlock;
NSCAssert(read, @"additive requires an animatable property readBlock");
if (NULL == read) {
return;
}
// object value
Vector4r objectValue = read_values(read, obj, anim->valueCount);
// current value
Vector4r currentValue = currentVec->vector4r();
// determine animation change
if (anim->previousVec) {
Vector4r previousValue = anim->previousVec->vector4r();
currentValue -= previousValue;
}
// avoid writing no change
if (shouldAvoidExtraneousWrite && currentValue == Vector4r::Zero()) {
return;
}
// add to object value
currentValue += objectValue;
// update previous values; support animation convergence
anim->previous2Vec = anim->previousVec;
anim->previousVec = currentVec;
//写入数据
// write value
write(obj, currentValue.data());
if (anim->tracing) {
[anim->tracer writePropertyValue:POPBox(currentVec, anim->valueType, true)];
}
}
}
}
复制代码
上面的代码看似比较繁琐,但其关键之处在于POPAnimatablePropertyReadBlock
和POPAnimatablePropertyWriteBlock
二者。
typedef void (^POPAnimatablePropertyReadBlock)(id obj, CGFloat values[]);
typedef void (^POPAnimatablePropertyWriteBlock)(id obj, const CGFloat values[]);
复制代码
这两个block的声明得很简单,分别传入动画对应的obj和更新的数值,但不一样之处在于前者是从obj中读取到对应的数值,后者是将数值写入到obj属性中的。而属性对应的两个block在POPAnimationProperty.mm
文件中已经声明了。
typedef struct
{
NSString *name; //属性名
POPAnimatablePropertyReadBlock readBlock;
POPAnimatablePropertyWriteBlock writeBlock;
CGFloat threshold; //阈值
} _POPStaticAnimatablePropertyState;
typedef _POPStaticAnimatablePropertyState POPStaticAnimatablePropertyState;
static POPStaticAnimatablePropertyState _staticStates[] =
{
{kPOPLayerPositionX,
^(CALayer *obj, CGFloat values[]) {
values[0] = [(CALayer *)obj position].x;
},
^(CALayer *obj, const CGFloat values[]) {
CGPoint p = [(CALayer *)obj position];
p.x = values[0];
[obj setPosition:p];
},
kPOPThresholdPoint
},
{kPOPLayerPositionY,
^(CALayer *obj, CGFloat values[]) {
values[0] = [(CALayer *)obj position].y;
},
^(CALayer *obj, const CGFloat values[]) {
CGPoint p = [(CALayer *)obj position];
p.y = values[0];
[obj setPosition:p];
},
kPOPThresholdPoint
},
}
复制代码
咱们能够看到_staticStates声明了相关属性的readBlock和writeBlock,而获取block的方式,就是经过定义的name属性。
咱们以前提到过外部调用addAnimation方法时,实际上会调用到POPAnimator中的addAnimation方法,那么接下来咱们就看动画的整个添加逻辑。
- (void)addAnimation:(POPAnimation *)anim forObject:(id)obj key:(NSString *)key
{
if (!anim || !obj) {
return;
}
// 针对key为空的状况
if (!key) {
key = [[NSUUID UUID] UUIDString];
}
// lock
pthread_mutex_lock(&_lock);
// obj为key,获取obj中包括的动画
NSMutableDictionary *keyAnimationDict = (__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj);
// 若获取到的dict为空,则建立一个dict用于存储obj的动画
if (nil == keyAnimationDict) {
keyAnimationDict = [NSMutableDictionary dictionary];
CFDictionarySetValue(_dict, (__bridge void *)obj, (__bridge void *)keyAnimationDict);
} else {
POPAnimation *existingAnim = keyAnimationDict[key];
if (existingAnim) {
pthread_mutex_unlock(&_lock);
if (existingAnim == anim) {
return;
}
//须要移除obj中相同key的动画
[self removeAnimationForObject:obj key:key cleanupDict:NO];
pthread_mutex_lock(&_lock);
}
}
keyAnimationDict[key] = anim;
POPAnimatorItemRef item(new POPAnimatorItem(obj, key, anim));
//添加到队列中
_list.push_back(item);
_pendingList.push_back(item);
POPAnimationGetState(anim)->reset(true);
//更新定时器的状态
updateDisplayLink(self);
pthread_mutex_unlock(&_lock);
[self _scheduleProcessPendingList];
}
复制代码
该方法主要是将动画分别存储到_dict、_list和_pendingList中,这里比较重要的地方是调用了_scheduleProcessPendingList
方法
- (void)_scheduleProcessPendingList
{
// see WebKit for magic numbers, eg http://trac.webkit.org/changeset/166540
static const CFIndex CATransactionCommitRunLoopOrder = 2000000;
static const CFIndex POPAnimationApplyRunLoopOrder = CATransactionCommitRunLoopOrder - 1;
pthread_mutex_lock(&_lock);
if (!_pendingListObserver) {
__weak POPAnimator *weakSelf = self;
/监听RunLoop即将进入睡眠或退出状态时的事件
_pendingListObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting | kCFRunLoopExit, false, POPAnimationApplyRunLoopOrder, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
[weakSelf _processPendingList];
});
if (_pendingListObserver) {
CFRunLoopAddObserver(CFRunLoopGetMain(), _pendingListObserver, kCFRunLoopCommonModes); //将监听者添加到RunLoop中
}
}
pthread_mutex_unlock(&_lock);
}
复制代码
这里最重要的是监听RunLoop即将进入睡眠或退出状态时,调用了_processPendingList
方法。 为何要在RunLoop即将进入睡眠或退出状态时,再去处理_pendingList队列呢,而不是立马去处理?
若是咱们立马去处理_pendingList队列,对动画进行渲染的话,那么可能会出现卡顿的问题,由于咱们是在主线程中对动画进行渲染的。好比当咱们在滑动列表时,同时动画也在不断渲染,那么就可能出现掉帧的状况了。而若是咱们在等RunLoop处理完了当前的UI事件后,再去处理动画,那么就不存在影响到其余UI的渲染了。
- (void)_processPendingList
{
// rendering pending animations
CFTimeInterval time = [self _currentRenderTime];
[self _renderTime:(0 != _beginTime) ? _beginTime : time items:_pendingList];
pthread_mutex_lock(&_lock);
_pendingList.clear();
[self _clearPendingListObserver];
pthread_mutex_unlock(&_lock);
}
复制代码
该方法其实就是逐一对_pendingList中的动画经过_render函数逐一渲染,接下来的流程就是咱们上述所说的。
二者都是用来存储动画,但_list是贯穿动画的整个生命周期,只有当动画被执行完以后才会被移除。而_pendingList只是用于暂存动画,它只是在RunLoop还处于活动期间,存储被添加的动画,当RunLoop即将进入睡眠或退出状态时,它会处理_pendingList中的动画,动画都被处理(开始执行),_pendingList就会清空队列中的动画。
本小节主要介绍了POPAnimator是如何管理被添加进来的Animation,以及如何借助POPAnimationState来更新Animation的属性值。但具体动画是如何被更新,咱们会从下一章节从POPAnimationState来讲明。