Подтвердить что ты не робот

UIViewController - проблема с пользовательским отключением перехода

Резюме

У меня есть контент UIViewController, который представляет настройки UIViewController с использованием настраиваемого перехода. Презентация с presentViewController:animated:completion:.

Когда я позже увольняю настройки с помощью dismissViewControllerAnimated:completion:, презентационный контроллер внезапно возвращается к исходной позиции до представления контроллера настроек.

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

Пользовательский переход - открытие капюшона

Желаемый эффект заключается в том, что отображающий контроллер скользит по экрану, и представленный контроллер, как видно, лежит за ним, откуда он поднимается, чтобы заполнить экран. Верхняя часть отображающего контроллера остается на экране в течение всего срока использования представленного контроллера. Он находится в нижней части экрана, но выше представленного контроллера.

Вы можете себе представить, как снять капот на автомобиле (передний контроллер), чтобы увидеть двигатель сзади (представленные параметры), но капот остается видимым внизу для небольшого контекста.

Я планирую доработать это так, чтобы отображающий контроллер, по-видимому, поднялся с перспективой в 3d-режиме, но пока у меня пока что нет.

Когда настройки увольняются, исходный контроллер представления (капот) должен задвинуть назад экран, и представленный контроллер (настройки) немного опустится (закрытие крышки).

Код

Здесь используется метод, который переключает настройки на экран и выключает его (он просто вызывается UIButton). Вы заметите, что контроллер представления представления настроен как <UIViewControllerTransitioningDelegate>.

-(void) toggleSettingsViewController
{
  const BOOL settingsAreShowing = [self presentedViewController] != nil;
  if(!settingsAreShowing)
  {
    UIViewController *const settingsController = [[self storyboard] instantiateViewControllerWithIdentifier: @"STSettingsViewController"];
    [settingsController setTransitioningDelegate: self];
    [settingsController setModalPresentationStyle: UIModalPresentationCustom];
    [self presentViewController: settingsController animated: YES completion: nil];
  }
  else
  {
    [self dismissViewControllerAnimated: YES completion: nil];
  }
}

Для реализации <UIViewControllerAnimatedTransitioning> контроллер представления представления также просто возвращает себя как <UIViewControllerAnimatedTransitioning>

-(id<UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
  return self;
}

-(id<UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController *)dismissed
{
  // Test Point 1.
  return self;
}

Итак, контроллер представления будет получать animateTransition::

-(void) animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
  UIViewController *const fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
  UIViewController *const toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

  const BOOL isUnwinding = [toController presentedViewController] == fromController;
  const BOOL isPresenting = !isUnwinding;

  UIViewController * presentingController = isPresenting ? fromController : toController;
  UIViewController * presentedController = isPresenting ? toController : fromController;

  if(isPresenting)
  {
    // Add the presented controller (settings) to the view hierarchy _behind_ the presenting controller.
    [[transitionContext containerView] insertSubview: [presentedController view] belowSubview: [presentingController view]];

    // Set up the initial position of the presented settings controller. Scale it down so it seems in the distance. Alpha it down so it is dark and shadowed.
    presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
    presentedController.view.alpha = 0.7;

    [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
      // Lift up the presented controller.
      presentedController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);

      // Brighten the presented controller (out of shadow).
      presentedController.view.alpha = 1;

      // Push the presenting controller down the screen – 3d effect to be added later.
      presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);
     } completion: ^(BOOL finished){
       [transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
     }];
  }
  else
  {
    // Test Point 2.

    // !!!This line should not be needed!!!
    // It resets the presenting controller to where it ought to be anyway.
    presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);

    [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
      // Bring the presenting controller back to its original position.
      presentingController.view.layer.transform = CATransform3DIdentity;

      // Lower the presented controller again and put it back in to shade.
      presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
      presentedController.view.alpha = 0.4;
    } completion:^(BOOL finished) {
      [transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
    }];
  }
}

-(NSTimeInterval) transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
  return 0.5;
}

Проблема

В приведенном выше коде я указал !!! Эта строка не нужна!!!.

Что происходит между Тестовой точкой 1 и Контрольной точкой 2, позиция экрана контроллера представления представления reset должна быть полноэкранными границами по умолчанию. Таким образом, вместо того, чтобы быть в нижней части экрана, готовым к оживлению назад, плавно, он внезапно вскакивает на экран в положение, что оно также должно плавно анимировать!

Я пробовал различные подходы к анимации представления контроллера представления по экрану:

  • Я изменил рамку представления.
  • Я изменил преобразование вида.
  • Я изменил трехмерное преобразование уровня слоя.

Во всех случаях в Контрольной точке 1, когда делегат перехода запрашивается, контроллер представления настроен так, как я ожидал. Однако во всех случаях в тестовой точке 2 контроллер представления данных потерял правильное положение и был "очищен", чтобы иметь нормальную полноэкранную позицию, на которую я хочу оживить ее.

В приведенной выше работе я явно перемещаю контроллер представления представления туда, где он должен быть в начале анимации с !!! Эта строка не нужна!!!. Это, похоже, работает на устройстве с текущей версией iOS 7. Однако на симуляторе контроллер отображается в очищенном положении по меньшей мере для одного кадра.

Я подозрюю, что делаю что-то еще не так, и что у меня возникнут проблемы с моим обходным путем, просто маскируя еще одну проблему.

Есть идеи, что происходит? Спасибо!

4b9b3361

Ответ 1

Несколько потенциальных ошибок с увольнением модально представленных контроллеров представлений с использованием пользовательских анимаций перехода:

  • Добавьте представленный ( "to" ) вид в контейнер, а затем представите представленный вид спереди. Не добавляйте представление представления, поскольку вы можете удалить его из своего текущего супервизора.
  • При увольнении UIKit устанавливает альфа представленного представления в 0 до вызова animateTransition. Таким образом, вы захотите установить значение 1.0 или все, что было при завершении настоящего, перед отключением анимации (-ов) увольнения.
  • Аналогично для представленного преобразования представления. При увольнении он получает reset для идентификации до вызова вашего animateTransition.

Учитывая все это, я думаю, что это должно сработать:

-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController *fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *containerView = transitionContext.containerView;

    const BOOL isUnwinding = [toController presentedViewController] == fromController;
    const BOOL isPresenting = !isUnwinding;

    UIViewController *presentingController = isPresenting ? fromController : toController;
    UIViewController *presentedController = isPresenting ? toController : fromController;

    [containerView addSubview:presentingController.view];
    [containerView bringSubviewToFront:presentingController.view];

    if(isPresenting)
    {
        // Set up the initial position of the presented settings controller. Scale it down so it seems in the distance. Alpha it down so it is dark and shadowed.
        presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
        presentedController.view.alpha = 0.7;

        [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
            // Lift up the presented controller.
            presentedController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);

            // Brighten the presented controller (out of shadow).
            presentedController.view.alpha = 1;

            // Push the presenting controller down the screen – 3d effect to be added later.
            presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);
        } completion: ^(BOOL finished){
            [transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
        }];
    }
    else
    {
        presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
        presentedController.view.alpha = 0.7;

        [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
            // Bring the presenting controller back to its original position.
            presentingController.view.layer.transform = CATransform3DIdentity;

            // Lower the presented controller again and put it back in to shade.
            presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
            presentedController.view.alpha = 0.4;
        } completion:^(BOOL finished) {
            [transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
        }];
    }
}

Ответ 2

Вначале я думал об использовании CATransition, чтобы иметь настраиваемый эффект перехода, когда presentViewController:animated:completion: и dismissViewControllerAnimated:completion: контроллер просмотра. Но вы хотите показать часть View Controller, когда представлен параметр View Controller, тогда я думаю, что CATransition не поможет, потому что у вас нет полного контроля над тем, как далеко вы хотите переместить View Viewer.

Я думаю, что самый простой способ - иметь один контроллер View с двумя полноэкранными UIView. Для первого представления UIView (View Controller, то есть self.view), вы настраиваете настройку, а на втором UIView - обычный просмотр. В ViewDidLoad вы добавляете второй вид, используя [self.view addSubview:2ndView];. Позже, когда вы хотите представить представление настроек, вы можете сделать

CGRect frame = secondView.frame;
frame.origin.y = the_y_coordinate_you_like;
UIView animateWithDuration:0.2 animations:^{
    secondView.frame = frame;
}];

затем сделайте другой способ вернуть 2-ое назад.