簡體   English   中英

ios 7 - 具有延遲的UIView動畫方法不會延遲

[英]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的參數afterScreenUpdatesNO ,則視圖快照為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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM