Контроллер пользовательского представления iOS 8: изменение размера представляемого VC во время анимации

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

Я пытался приблизиться к этому, используя пользовательский переход контроллера представления. Когда карта нажата, я запускаю пользовательский переход контроллера вида с UIModalPresentationCustomи я установил делегат перехода, который сам продает пользовательский аниматор и пользовательский UIPresentationController. В animateTransition:Я добавляю представление нового контроллера представления в представление контейнера, устанавливая рамку на рамку карты изначально (так что похоже, что карта все еще там и не изменилась). Затем я пытаюсь выполнить анимацию, в которой кадр представленного вида увеличивается в размере и изменяется в позиции, чтобы он переместился в свою конечную позицию.

Вот часть моего кода, который делает то, что я описал выше - я пытаюсь сделать его кратким и приятным, но я могу предоставить больше информации, если это необходимо:

Переходный делегат

-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {

    // NOWAnimationDelegate is my own custom protocol which defines the method for asking the presenting VC for the tapped card's frame.
    UIViewController<NOWAnimationDelegate> *fromVC = (UIViewController<NOWAnimationDelegate> *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *finalVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];

    // Ask the presenting view controller for the frame of the tapped card - this method works.
    toView.frame = [fromVC rectForSelectedCard];

    [transitionContext.containerView addSubview:toView];

    CGRect finalRect = [transitionContext finalFrameForViewController:finalVC];

    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        toView.frame = finalRect;
    }completion:^(BOOL finished) {
        [transitionContext completeTransition:YES];
    }];
}

Пользовательский UIPresentationController

-(CGSize)sizeForChildContentContainer:(id<UIContentContainer>)container withParentContainerSize:(CGSize)parentSize {
    return CGSizeMake(0.875*parentSize.width, 0.875*parentSize.height);
}

-(CGRect)frameOfPresentedViewInContainerView {
    CGRect presentedViewFrame = CGRectZero;
    CGRect containerBounds = self.containerView.bounds;

    presentedViewFrame.size = [self sizeForChildContentContainer:(UIView<UIContentContainer> *)self.presentedView withParentContainerSize:containerBounds.size];
    presentedViewFrame.origin.x = (containerBounds.size.width - presentedViewFrame.size.width)/2;
    presentedViewFrame.origin.y = (containerBounds.size.height - presentedViewFrame.size.height)/2 + 10;

    return presentedViewFrame;
}

Я обнаружил, что новое представление автоматически устанавливается в его окончательный размер сразу же в начале анимации, а затем анимация - это просто новое представление, анимируемое вверх. Используя точки останова, я заметил, что frameOfPresentedViewInContainerView называется во время [transitionContext.containerView addSubview:toView] вызов, который, вероятно, объяснил бы, почему это происходит - frameOfPresentedViewInContainerView возвращает "Прямоугольник кадра для назначения представляемому представлению в конце анимации" в соответствии с документацией UIPresentationController.

Тем не менее, я не уверен, как поступить, или если это вообще возможно. Все примеры пользовательских переходов контроллера представления, которые я видел, имели конечный размер представленного контроллера представления, статический и неизменный во время анимации. Есть ли способ выполнить пользовательский переход контроллера представления с изменяющимся размером представленного представления во время анимации, или мне нужно подходить к этому по-другому?

3 ответа

По сути, вам нужно анимировать ваш toView.transform, используя CGAffineTransform в вашем Transition Delegate. Шаги, чтобы сделать:

  1. Перед анимацией установите ваш toView.frame = ваш кадр, когда он не отображается
  2. Создайте, скажем, CGAffineTransformMakeScale для масштабирования от скрытого кадра до желаемого конечного представленного кадра
  3. В блоке анимации установите toView.transform для преобразования, созданного на шаге 2

Как упоминает Адитья, CGAffineTransform - это то, что нужно.

Теперь у меня есть это работает с поддержкой ширины:

    CGFloat targetscale=initialHeight/finalHeight;
    CGFloat targetyoffset=(finalHeight-(finalHeight*targetscale))/2;
    int targety=roundf(initialPosition-targetyoffset);

    CGAffineTransform move=CGAffineTransformMakeTranslation(0, targety);
    CGAffineTransform scale=CGAffineTransformMakeScale(1,targetscale);
    toView.transform=CGAffineTransformConcat(scale,move);

Это позиционирует входящее представление с учетом определенного масштаба, а затем выполняет оба преобразования одновременно, так что представление масштабируется и перемещается к конечной позиции и размеру.

Вы тогда просто установите

toView.transform=CGAffineTransformIdentity;

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

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

+(CGAffineTransform)transformView:(UIView*)view toTargetRect:(CGRect)rect{

  CGFloat targetscale=rect.size.height/view.frame.size.height;

  CGFloat targetxoffset=(view.frame.size.width-(view.frame.size.width*targetscale))/2;
  int targetx=roundf(rect.origin.x-view.frame.origin.x-targetxoffset);

  CGFloat targetyoffset=(view.frame.size.height-(view.frame.size.height*targetscale))/2;
  int targety=roundf(rect.origin.y-view.frame.origin.y-targetyoffset);

  CGAffineTransform move=CGAffineTransformMakeTranslation(targetx, targety);
  CGAffineTransform scale=CGAffineTransformMakeScale(targetscale,targetscale);

  return CGAffineTransformConcat(scale, move);

}

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

CGRect cellRect=[_tableView rectForRowAtIndexPath:[_tableView indexPathForSelectedRow]];

cellRect=[self.tableView convertRect:cellRect toView:presenting.view];

надеюсь это поможет!

Apple предоставляет пример приложения wwdc " LookInside ", сопровождающий доклад 228: "Взгляд в контроллеры презентаций". Это показывает 2 пользовательских представления, одно из которых анимирует размер представленного контроллера представления. Взгляд в этот код должен помочь вам;)

Другие вопросы по тегам