iOS UIView.animateWithDuration 預設為序列執行,若需「動畫進行到一半時啟動下一個動畫」,可利用 delay 參數實現時間錯位啟動,或使用 CADisplayLink、UIViewPropertyAnimator(iOS 10+)實現更精細控制。
// 動畫 A:持續 1 秒
[UIView animateWithDuration:1.0
animations:^{
imageView1.frame = newRect1;
}
completion:^(BOOL finished){
// 完成後邏輯
}];
// 動畫 B:延遲 0.5 秒啟動,同樣持續 1 秒
[UIView animateWithDuration:1.0
delay:0.5
options:UIViewAnimationOptionCurveLinear
animations:^{
imageView2.frame = newRect2;
}
completion:^(BOOL finished){
// 完成後邏輯
}];
效果:動畫 A 執行 0.5 秒時,動畫 B 開始;兩者並行 0.5 秒後 A 結束,B 再獨自跑 0.5 秒。
UIViewPropertyAnimator *anim1 = [[UIViewPropertyAnimator alloc] initWithDuration:1.0 curve:UIViewAnimationCurveLinear animations:^{
imageView1.frame = newRect1;
}];
UIViewPropertyAnimator *anim2 = [[UIViewPropertyAnimator alloc] initWithDuration:1.0 curve:UIViewAnimationCurveLinear animations:^{
imageView2.frame = newRect2;
}];
[anim1 startAnimation];
// 0.5 秒後啟動 anim2
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[anim2 startAnimation];
});
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
- (void)tick:(CADisplayLink *)link {
CGFloat elapsed = link.timestamp - startTime;
if (elapsed > 0.5 && !anim2Started) {
[anim2 startAnimation];
anim2Started = YES;
}
if (elapsed >= totalDuration) {
[link invalidate];
}
}
| 需求 | 推薦方案 | 優點 |
|---|---|---|
| 簡單錯位啟動 | delay 參數 | 代碼少、相容性好 |
| 需暫停/繼續/調速/倒轉 | UIViewPropertyAnimator | API 豐富、支援交互式動畫 |
| 複雜多物件協同/物理模擬 | CADisplayLink / SpriteKit | 逐幀精確控制 |
delay 為相對於呼叫時刻,若在完成回呼中再啟動下一組,需自行計算累積延遲。CALayer 動畫。constraints.constant 而非 frame。善用時間錯位與現代動畫 API,能打造流暢、自然的多元素動畫編排。