[英]ios 7 - UIView animate method with delay is not delayed
我目前正在測試新的iOS 7視圖控制器轉換。 我想要的是一個自定義模式呈現過渡,將您的下一個視圖從頂部屏幕切割成幾個條帶。 每個條帶應在增量延遲后出現以產生所需效果。
所以我的代碼看起來像這樣:
- (void)presentModalWithContext:(id<UIViewControllerContextTransitioning>)context
{
UIView *inView = [context containerView];
UIView *fromView = [context viewControllerForKey:UITransitionContextFromViewControllerKey].view;
UIView *toView = [context viewControllerForKey:UITransitionContextToViewControllerKey].view;
NSTimeInterval stripTime = 1.0;
NSTimeInterval stripDelay = 1.0;
NSInteger stripCount = 10;
CGFloat stripHeight = toView.frame.size.height / stripCount;
for (NSInteger i = 0; i < stripCount; i++)
{
CGFloat offsetY = i*stripHeight;
CGRect snapRect = CGRectMake(0, offsetY, toView.frame.size.width, stripHeight);
UIView *view = [toView resizableSnapshotViewFromRect:snapRect afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
CGRect stripRect = CGRectMake(0, -(stripCount-i)*stripHeight, snapRect.size.width, snapRect.size.height);
view.frame = stripRect;
[inView insertSubview:view aboveSubview:fromView];
NSTimeInterval interval = stripDelay*(stripCount-i);
[UIView animateWithDuration:stripTime delay:interval options:0 animations:^{
CGPoint center = view.center;
center.y += stripCount*stripHeight;
view.center = center;
} completion:^(BOOL finished) {
NSLog(@"complete");
if (i == stripCount-1)
[context completeTransition:YES];
}];
}
}
我已經檢查了每個條帶的初始和最終位置,並且已經可以了。 我的interval
變量也在每個循環中正確設置。
但似乎這根本沒有延遲。 所有條帶都在一起移動,給人一種完整視圖移動的印象。
快速查看基本日志可以看到所有動畫同時執行:
2013-09-20 01:11:32.908 test_transition[7451:a0b] complete
2013-09-20 01:11:32.909 test_transition[7451:a0b] complete
2013-09-20 01:11:32.910 test_transition[7451:a0b] complete
2013-09-20 01:11:32.910 test_transition[7451:a0b] complete
2013-09-20 01:11:32.911 test_transition[7451:a0b] complete
2013-09-20 01:11:32.911 test_transition[7451:a0b] complete
2013-09-20 01:11:32.912 test_transition[7451:a0b] complete
2013-09-20 01:11:32.912 test_transition[7451:a0b] complete
2013-09-20 01:11:32.913 test_transition[7451:a0b] complete
2013-09-20 01:11:32.913 test_transition[7451:a0b] complete
有人能夠發現這里有什么問題嗎?
編輯:
似乎這是取消任何動畫延遲的以下行,即使這些動畫與正在拍攝的視圖無關:
UIView *view = [toView resizableSnapshotViewFromRect:snapRect afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
如果我將afterScreenUpdates
的參數afterScreenUpdates
為NO
,則視圖快照為null
,我得到以下錯誤日志:
Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates.
如何在快照之前渲染視圖? 我試過[toView setNeedsDisplay]
但沒有成功......
在對你的代碼進行了一些處理,並將它與我的比較,其中延遲參數被正確認可后,我仍然無法弄清楚為什么你的代碼不起作用。 無論如何,我找到了另一種方法。 我將動畫分為兩部分。 在第一部分中,我使用您的代碼創建視圖切片,將它們添加到inView,還添加到可變數組。 在第二部分中,我遞歸地調用動畫塊,沒有延遲,直到顯示最后一個條帶。 這種方法的一個限制是每個條帶動畫必須在下一個開始之前完成(因為下一個從完成塊調用),因此您無法獨立控制持續時間和延遲。 無論如何,這就是我所做的。 在呈現視圖控制器中,我只是這樣做:
-(IBAction)presntBlue:(id)sender {
BlueViewController *blue = [self.storyboard instantiateViewControllerWithIdentifier:@"Blue"];
blue.modalTransitionStyle = UIModalPresentationCustom;
blue.transitioningDelegate = self;
[self presentViewController:blue animated:YES completion:nil];
}
-(id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
RDPresentationAnimator *animator = [RDPresentationAnimator new];
animator.isPresenting = YES;
return animator;
}
在RDPresentationAnimator類中,我有這個:
@interface RDPresentationAnimator () {
NSInteger stripCount;
CGFloat stripHeight;
NSMutableArray *stripArray;
}
@end
@implementation RDPresentationAnimator
#define ANIMATION_TIME .3
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return ANIMATION_TIME;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)context {
UIView *inView = [context containerView];
UIView *toView = [context viewControllerForKey:UITransitionContextToViewControllerKey].view;
stripCount = 10;
stripHeight = toView.frame.size.height / stripCount;
stripArray = [NSMutableArray new];
for (NSInteger i = 0; i < stripCount; i++)
{
CGFloat offsetY = i*stripHeight;
CGRect snapRect = CGRectMake(0, offsetY, toView.frame.size.width, stripHeight);
UIView *view = [toView resizableSnapshotViewFromRect:snapRect afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
CGRect stripRect = CGRectMake(0, -(stripCount-i)*stripHeight, snapRect.size.width, snapRect.size.height);
view.frame = stripRect;
[inView addSubview:view];
[stripArray addObject:view];
}
[self animateStrip:stripCount - 1 withContext:context];
}
-(void)animateStrip:(NSInteger) index withContext:(id <UIViewControllerContextTransitioning>) context{
[UIView animateWithDuration:ANIMATION_TIME animations:^{
UIView *view = stripArray[index];
CGPoint center = view.center;
center.y += stripCount*stripHeight;
view.center = center;
} completion:^(BOOL finished) {
if (index >0) {
[self animateStrip:index - 1 withContext:context];
}else{
[context completeTransition:YES];
};
}];
}
我想我會添加另一個答案,它可以讓你獨立控制stripTime和stripDelay。 我從未找到使用新的UIViewControllerContextTransitioning方法使其工作的方法。 這種方式使用普通的UIView動畫,然后是no animation presentViewController。 此方法應該在縱向或橫向方向上正常工作(請注意,我使用self.view.bounds來計算stripHeight和snapRect,以便這些值對於任一方向都是正確的)。
@interface ViewController () {
NSInteger stripCount;
CGFloat stripHeight;
NSMutableArray *stripArray;
}
@end
@implementation ViewController
-(IBAction)presntBlue:(id)sender {
BlueViewController *blue = [self.storyboard instantiateViewControllerWithIdentifier:@"Blue"];
[self animateView:blue];
}
-(void)animateView:(UIViewController *) toVC; {
UIView *toView = toVC.view;
toView.frame = self.view.bounds;
[self.view addSubview:toView];
NSTimeInterval stripDelay = 0.2;
NSTimeInterval stripTime = 1.0;
stripCount = 10;
stripHeight = self.view.bounds.size.height / stripCount;
stripArray = [NSMutableArray new];
for (NSInteger i = 0; i < stripCount; i++) {
CGFloat offsetY = i*stripHeight;
CGRect snapRect = CGRectMake(0, offsetY, self.view.bounds.size.width, stripHeight);
UIView *view = [toView resizableSnapshotViewFromRect:snapRect afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
CGRect stripRect = CGRectMake(0, -(stripCount-i)*stripHeight, snapRect.size.width, snapRect.size.height);
view.frame = stripRect;
[self.view addSubview:view];
[stripArray addObject:view];
}
[toView removeFromSuperview];
for (NSInteger i = 0; i < stripCount; i++) {
NSTimeInterval interval = stripDelay*(stripCount-i);
UIView *view = stripArray[i];
[UIView animateWithDuration:stripTime delay:interval options:0 animations:^{
CGPoint center = view.center;
center.y += stripCount*stripHeight;
view.center = center;
} completion:^(BOOL finished) {
if (i == 0){
[self presentViewController:toVC animated:NO completion:nil];
}
}];
}
}
補充說明:
在animateView:方法中,我將toView添加到self.view,然后在制作條帶后將其刪除。 我這樣做是為了確保它在縱向和橫向上正常工作 - 如果我省略這兩個陳述,動畫結束時景觀動畫中會出現輕微的故障。 如果我有這兩行,我偶爾會遇到一個小故障,你可以看到整個toView的短暫閃光。 我不知道為什么這只會偶爾發生,我還沒有更新我的手機,所以我不知道這是否也會在設備上發生。
這是一個解決方案。
雖然這個問題是2年之久,但它仍然是一個相關的問題,因為它仍然存在於iOS9上。 我意識到這一點並沒有給提問者看到它已經2年的幫助,但我只是遇到了這個問題。 所以這是一個解決方案。
當您想要在視圖控制器之間進行轉換時,您可能會使用Animator對象來運行自定義UIView塊動畫代碼。 這可能是復雜的多個動畫塊,有些使用延遲值。 但是如果在轉換過程中你想捕獲或截取某些內容(無論是UIView.drawViewHierarchyInRect,UIView.snapshotViewAfterScreenUpdates還是UIView.resizableSnapshotViewFromRect),使用這些方法中的任何一種都可以解除動畫中的延遲。 對於最后兩種方法,如果你為afterScreenUpdates傳遞false,那么它不會解除延遲,但它也不會捕獲任何東西; 它必須是真實的捕捉某些東西,但將其設置為真將解除你的延遲。
使用這些方法中的任何一種都可以解除動畫塊中的延遲,並且通常會搞砸。 如果你告訴UIKit轉換將是3秒並且你有一個2秒延遲和1秒動畫的動畫塊(UIView.animateWithDuration ...),如果你的延遲被解除,那么動畫會立即運行並且過渡持續僅僅1秒,這使得UIKit失去同步,並且一般都搞砸了,因為它預計會是3秒。
這是一個解決方案:假設你是從視圖控制器A轉換到視圖控制器B.在Animator對象的代碼文件(一個繼承自NSObject並符合UIViewControllerAnimatedTransitioning協議的對象)中,你將動畫代碼放在animateTransition中(transitionContext:UIViewControllerContextTransitioning) ){...}委托方法。 如果您正在從VC A轉換到VC B,請說您希望從VC B中截取某些內容,並在轉換過程中顯示它並對其執行某些操作。 一種完美的方法是在View Controller B的viewDidLoad方法中使用drawViewHierarchyInRect方法,捕獲並存儲圖像(或從該圖像創建UIImageView並存儲UIImageView)在View Controller B的實例屬性中。您需要使用drawViewHierarchyInRect方法而不是其他兩個方法,因為那些要求內容在屏幕上可見(即已經渲染); drawViewHiearchyInRect方法捕獲offScreen內容,即使它沒有添加到視圖中。
一旦開始轉換,“查看控制器”就會被初始化,並且會調用viewDidLoad。 因此,在過渡動畫代碼中,您可以通過參考視圖控制器的屬性來獲取截屏圖像(或imageView),並在轉換過程中隨意執行任何操作。 您的延誤不會被解除。
要點:不要在過渡期間截取內容。 相反,將屏幕截圖代碼放在視圖控制器的viewDidLoad中,並將其輸出存儲在實例變量中,並在轉換中引用它。
希望這會有所幫助,我今天才剛剛遇到這個問題並且遇到了一個解決方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.