上一節講解了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來講明。