2013-09-19 3 views
0

В настоящее время я тестирую новый контроллер перехода iOS 7. Что я хочу - это особый модальный переход, представляющий ваш следующий вид, разрезанный на несколько полосок сверху. Каждая полоса должна появляться после инкрементной задержки, чтобы дать желаемый эффект.ios 7 - метод анимации UIView с задержкой не задерживается

Так что мой код выглядит следующим образом:

- (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 

ли кто-то сможет определить, что здесь не так?

EDIT:

Кажется, это следующая строка, которая отменяет задержку каких-либо анимации, даже если те не касающиеся зрения того snapshotted:

UIView *view = [toView resizableSnapshotViewFromRect:snapRect afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero]; 

Если я установить параметр 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], но без успеха ...

+0

Я сделал несколько тестов на вашем коде, и кажется, что аргумент задержки в animateWithDuration: delay: options: animation: completion: игнорируется в вашем коде. Даже если я прокомментирую цикл for и задаю i = 0, верхний срез появится в верхней части экрана сразу, независимо от того, какое значение я передаю для stripDelay. Я делаю еще несколько экспериментов, чтобы увидеть, является ли это проблемой iOS 7 или проблемой UIViewControllerContextTransitioning. – rdelmar

+1

Мои дальнейшие эксперименты показали, что параметр задержки соблюдается в нескольких моих проектах, где я использовал новые настраиваемые переходы контроллера. У меня есть та, которая является обычным нажатием, и та, которая является обычным подарком, и оба они работали (хотя, с нажатием, анимация перекрестного затухания навигации сразу же появилась). Итак, я немного озадачен тем, почему вы не работаете. – rdelmar

+0

Thx для вашей помощи @rdelmar. Это действительно странно, я должен где-то повесить, если вы сообщите, что ваши тестовые проекты работают:/Но я действительно не знаю, где, это довольно простой пример, который я дал – Yaman

ответ

1

После того как вы немного поработали над своим кодом и сравнили его с моим, где параметр задержки был соблюден правильно, я до сих пор не могу понять, почему ваш не работает , В любом случае, я нашел другой способ, который работает. Я разбиваю анимацию на две части. В первой части я создаю фрагменты представления, используя ваш код, добавляю их в 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]; 
     }; 
    }]; 
} 
+0

Привет @rdelmar. Я думал об этом обходном пути, но пока не нашел времени проверить его. Он выполняет эту работу, но это действительно раздражает отсутствие полного контроля над моей анимацией :(Я поддержал ваш ответ, но я предпочитаю, чтобы он не был проверен на данный момент, чтобы узнать, может ли кто-то выяснить, что случилось с задержкой. Thx снова для вашего помогите! – Yaman

+0

Я отредактировал свой ответ, чтобы принести новую информацию для этой проблемы. Не стесняйтесь, если хотите. – Yaman

1

Я думал, что я хотел бы добавить еще один ответ, что это даст вам независимый контроль над stripTime и stripDelay. Я никогда не нашел способ заставить его работать с использованием новых методов UIViewControllerContextTransitioning. Этот способ использует обычные анимации UIView, а затем нет анимации 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 для краткой вспышки. Я не знаю, почему это случается только иногда, и я еще не обновил свой телефон, поэтому я не знаю, происходит ли это на устройстве.

+0

Thx для вашего ответа еще раз @rdelmar, но я до сих пор не могу подтвердить этот ответ. нужна такая анимация, я просто тестирую новые функции iOS 7, поэтому моя цель здесь - понять, почему именно задержки не работают с 'UIViewControllerContextTransitioning' – Yaman

0

Вот решение.

Хотя этот вопрос составляет 2 года, он по-прежнему уместен, поскольку он все еще существует на iOS9. Я понимаю, что это не так много помогло бы аскесту, увидев, что прошло 2 года, но я только что наткнулся на это. Итак, вот решение.

Если вы хотите перейти между контроллерами представлений, вы, вероятно, собираетесь использовать объект Animator для запуска вашего пользовательского кода анимации блока UIView. Это может быть сложным с несколькими блоками анимации, а некоторые - с использованием значения задержки. Но если во время вашего перехода вы хотите захватить или снять скриншот часть чего-то (будь то UIView.drawViewHierarchyInRect, UIView.snapshotViewAfterScreenUpdates или UIView.resizableSnapshotViewFromRect), использование любого из этих методов отключит задержки в вашей анимации. Для последних двух методов, если вы передадите false для afterScreenUpdates, тогда он не отключит задержки, но он также ничего не зафиксирует; это должно быть правдой, чтобы захватить что-то, но установив его в true, вы отключите свою задержку.

Использование любого из этих методов позволит отключить задержку в вашем блоке анимации и вообще повредить вещи. Если вы сообщите UIKit, что переход будет составлять 3 секунды, и у вас есть блок анимации (UIView.animateWithDuration ...), который имеет задержку в 2 секунды и 1 сек анимации, если ваша задержка отключается, анимация запускается мгновенно и переход длится всего лишь 1 сек, что исключает синхронизацию UIKit, и материал обычно перепутался, потому что он ожидал, что это будет 3 секунды.

Вот решение: Допустим, вы переход от контроллера представления для просмотра контроллера B. В файле кода вашего Animator объекта (объект, который наследует от NSObject и соответствует протоколу UIViewControllerAnimatedTransitioning), вы кладете код анимации в animateTransition (переходContext: UIViewControllerContextTransitioning) {...} метод делегата. Если вы переходите из VC A в VC B, скажите, что вы хотите сделать снимок экрана с VC B, и показать его и сделать что-то с ним во время перехода. Один из способов, который отлично работает, - использовать метод drawViewHierarchyInRect в методе viewDidLoad View Controller B, чтобы захватить и сохранить изображение (или создать UIImageView с этого изображения и сохранить UIImageView) в свойстве экземпляра View Controller B. Вам нужно используйте метод drawViewHierarchyInRect, а не любой из двух других, потому что те, которые требуют, чтобы контент был видимым на экране (т.е. уже отображен); метод drawViewHiearchyInRect захватывает offScreen-контент, даже если он не добавлен в представление.

Как только вы начинаете переход, «просмотр контроллера» инициализируется и вызывает вызов viewDidLoad. Таким образом, в вашем коде анимации перехода вы можете захватить изображение, которое вы скриншот (или изображение), обратившись к свойству контроллера представления и сделайте все, что вы хотите с ним в своем переходе. Ваши задержки не будут отключены.

Главный пункт: Не скриншот в процессе перехода. Вместо этого поместите код скриншота в viewDidLoad контроллера представления и сохраните его вывод в переменной экземпляра и обратитесь к этому в своем переходе.

Надеюсь, что это поможет, я только сегодня столкнулся с этой проблемой и просто наткнулся на решение.

Смежные вопросы