Контроллер пользовательского представления 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. Шаги, чтобы сделать:
- Перед анимацией установите ваш toView.frame = ваш кадр, когда он не отображается
- Создайте, скажем, CGAffineTransformMakeScale для масштабирования от скрытого кадра до желаемого конечного представленного кадра
- В блоке анимации установите 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 пользовательских представления, одно из которых анимирует размер представленного контроллера представления. Взгляд в этот код должен помочь вам;)