У меня есть несколько просмотров, между которыми я хочу провести в iOS-программе. Прямо сейчас, я переключаюсь между ними, используя модальный стиль, с крестом, распускающим анимацию. Тем не менее, я хочу иметь анимацию для разворота/скольжения, как вы видите на главном экране и т.д. Я понятия не имею, как закодировать такой переход, а стиль анимации - не доступный стиль модального перехода. Может ли кто-нибудь дать мне пример кода? Это не должна быть модальная модель или что-то еще, я просто нашел это проще всего.
Как я могу реализовать анимацию разворота/скольжения между представлениями?
Ответ 1
Так как iOS 7, если вы хотите оживить переход между двумя контроллерами представлений, вы должны использовать пользовательские переходы, как описано в видео WWDC 2013 Пользовательские переходы с использованием Просмотр контроллеров. Например, чтобы настроить презентацию нового контроллера просмотра, вы должны:
-
Контроллер представления назначения должен указать
self.modalPresentationStyle
иtransitioningDelegate
для анимации презентации:- (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { self.modalPresentationStyle = UIModalPresentationCustom; self.transitioningDelegate = self; } return self; }
-
Этот делегат (в этом примере сам контроллер представления) будет соответствовать
UIViewControllerTransitioningDelegate
и реализовать:- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { return [[PresentAnimator alloc] init]; } // in iOS 8 and later, you'd also specify a presentation controller - (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source { return [[PresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting]; }
-
Вы бы использовали аниматор, который выполнил бы необходимую анимацию:
@interface PresentAnimator : NSObject <UIViewControllerAnimatedTransitioning> @end @implementation PresentAnimator - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return 0.5; } // do whatever animation you want below - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; [[transitionContext containerView] addSubview:toViewController.view]; CGFloat width = fromViewController.view.frame.size.width; CGRect originalFrame = fromViewController.view.frame; CGRect rightFrame = originalFrame; rightFrame.origin.x += width; CGRect leftFrame = originalFrame; leftFrame.origin.x -= width / 2.0; toViewController.view.frame = rightFrame; toViewController.view.layer.shadowColor = [[UIColor blackColor] CGColor]; toViewController.view.layer.shadowRadius = 10.0; toViewController.view.layer.shadowOpacity = 0.5; [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ fromViewController.view.frame = leftFrame; toViewController.view.frame = originalFrame; toViewController.view.layer.shadowOpacity = 0.5; } completion:^(BOOL finished) { [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; }]; } @end
-
Вы также можете использовать контроллер представления, который позаботится о том, чтобы очистить иерархию представлений. В этом случае, поскольку мы полностью накладываем представление представления, мы можем удалить его из иерархии при выполнении перехода:
@interface PresentationController: UIPresentationController @end @implementation PresentationController - (BOOL)shouldRemovePresentersView { return true; } @end
-
При желании, если вы хотите, чтобы этот жест был интерактивным, вы также:
-
Создайте контроллер взаимодействия (обычно a
UIPercentDrivenInteractiveTransition
); -
Попросите
UIViewControllerAnimatedTransitioning
реализоватьinteractionControllerForPresentation
, который, очевидно, вернет вышеупомянутый контроллер взаимодействия, -
У вас есть жест (или что у вас есть), который обновляет
interactionController
-
Все это описано в вышеупомянутом Пользовательские переходы с использованием контроллеров просмотра.
Например, для настройки навигационного контроллера push/pop, см. настраиваемую анимацию перехода контроллера навигации
Ниже приведена копия моего первоначального ответа, которая предшествует пользовательским переходам.
@sooper ответ правильный, что CATransition может дать эффект, который вы ищете.
Но, кстати, если ваш фон не белый, kCATransitionPush
of CATransition
имеет странное исчезновение и исчезновение в конце перехода, которое может отвлекать (при навигации между изображениями, особенно, это придает ему слегка мерцающий эффект). Если вы страдаете от этого, я считаю, что этот простой переход будет очень изящным: вы можете подготовить свое "следующее представление" к простому экрану вправо, а затем оживить перемещение текущего вида с экрана влево, пока вы одновременно анимируйте следующий вид, чтобы перейти туда, где текущий вид был. Обратите внимание, что в моих примерах я анимация subviews в и из основного представления в одном контроллере представления, но вы, вероятно, получаете идею:
float width = self.view.frame.size.width;
float height = self.view.frame.size.height;
// my nextView hasn't been added to the main view yet, so set the frame to be off-screen
[nextView setFrame:CGRectMake(width, 0.0, width, height)];
// then add it to the main view
[self.view addSubview:nextView];
// now animate moving the current view off to the left while the next view is moved into place
[UIView animateWithDuration:0.33f
delay:0.0f
options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction
animations:^{
[nextView setFrame:currView.frame];
[currView setFrame:CGRectMake(-width, 0.0, width, height)];
}
completion:^(BOOL finished){
// do whatever post processing you want (such as resetting what is "current" and what is "next")
}];
Понятно, что вам нужно будет настроить это, так как вы настроили все свои элементы управления, но это дает очень простой переход, отсутствие затухания или что-то в этом роде, просто приятный плавный переход.
Предостережение. Во-первых, ни этот пример, ни пример CATransition
не похожи на анимацию домашнего экрана SpringBoard (о которой вы говорили), которая является непрерывной (т.е. если вы на полпути через салфетки, вы может остановиться и вернуться или что угодно). Эти переходы - это те, которые когда-то инициировали их, они просто происходят. Если вам нужно это взаимодействие в реальном времени, это тоже можно сделать, но оно отличается.
Update:
Если вы хотите использовать непрерывный жест, который отслеживает палец пользователя, вы можете использовать UIPanGestureRecognizer
, а не UISwipeGestureRecognizer
, и я думаю, что animateWithDuration
лучше, чем CATransition
в этом случае. Я изменил свой handlePanGesture
, чтобы изменить координаты frame
для координации с жестом пользователя, а затем я изменил приведенный выше код, чтобы просто завершить анимацию, когда пользователь отпустил. Работает очень хорошо. Я не думаю, что вы можете сделать это с помощью CATransition
очень легко.
Например, вы можете создать обработчик жестов на главном экране контроллера:
[self.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]];
И обработчик может выглядеть так:
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
// transform the three views by the amount of the x translation
CGPoint translate = [gesture translationInView:gesture.view];
translate.y = 0.0; // I'm just doing horizontal scrolling
prevView.frame = [self frameForPreviousViewWithTranslate:translate];
currView.frame = [self frameForCurrentViewWithTranslate:translate];
nextView.frame = [self frameForNextViewWithTranslate:translate];
// if we're done with gesture, animate frames to new locations
if (gesture.state == UIGestureRecognizerStateCancelled ||
gesture.state == UIGestureRecognizerStateEnded ||
gesture.state == UIGestureRecognizerStateFailed)
{
// figure out if we've moved (or flicked) more than 50% the way across
CGPoint velocity = [gesture velocityInView:gesture.view];
if (translate.x > 0.0 && (translate.x + velocity.x * 0.25) > (gesture.view.bounds.size.width / 2.0) && prevView)
{
// moving right (and/or flicked right)
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
prevView.frame = [self frameForCurrentViewWithTranslate:CGPointZero];
currView.frame = [self frameForNextViewWithTranslate:CGPointZero];
}
completion:^(BOOL finished) {
// do whatever you want upon completion to reflect that everything has slid to the right
// this redefines "next" to be the old "current",
// "current" to be the old "previous", and recycles
// the old "next" to be the new "previous" (you'd presumably.
// want to update the content for the new "previous" to reflect whatever should be there
UIView *tempView = nextView;
nextView = currView;
currView = prevView;
prevView = tempView;
prevView.frame = [self frameForPreviousViewWithTranslate:CGPointZero];
}];
}
else if (translate.x < 0.0 && (translate.x + velocity.x * 0.25) < -(gesture.view.frame.size.width / 2.0) && nextView)
{
// moving left (and/or flicked left)
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
nextView.frame = [self frameForCurrentViewWithTranslate:CGPointZero];
currView.frame = [self frameForPreviousViewWithTranslate:CGPointZero];
}
completion:^(BOOL finished) {
// do whatever you want upon completion to reflect that everything has slid to the left
// this redefines "previous" to be the old "current",
// "current" to be the old "next", and recycles
// the old "previous" to be the new "next". (You'd presumably.
// want to update the content for the new "next" to reflect whatever should be there
UIView *tempView = prevView;
prevView = currView;
currView = nextView;
nextView = tempView;
nextView.frame = [self frameForNextViewWithTranslate:CGPointZero];
}];
}
else
{
// return to original location
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
prevView.frame = [self frameForPreviousViewWithTranslate:CGPointZero];
currView.frame = [self frameForCurrentViewWithTranslate:CGPointZero];
nextView.frame = [self frameForNextViewWithTranslate:CGPointZero];
}
completion:NULL];
}
}
}
Это использует эти простые методы frame
, которые вы предположительно определяете для нужного UX:
- (CGRect)frameForPreviousViewWithTranslate:(CGPoint)translate
{
return CGRectMake(-self.view.bounds.size.width + translate.x, translate.y, self.view.bounds.size.width, self.view.bounds.size.height);
}
- (CGRect)frameForCurrentViewWithTranslate:(CGPoint)translate
{
return CGRectMake(translate.x, translate.y, self.view.bounds.size.width, self.view.bounds.size.height);
}
- (CGRect)frameForNextViewWithTranslate:(CGPoint)translate
{
return CGRectMake(self.view.bounds.size.width + translate.x, translate.y, self.view.bounds.size.width, self.view.bounds.size.height);
}
Ваша конкретная реализация, несомненно, будет отличаться, но, надеюсь, это иллюстрирует эту идею.
Продемонстрировав все это (дополняя и разъясняя этот старый ответ), я должен указать, что я больше не использую эту технику. В настоящее время я обычно использую UIScrollView
(при включенном "пейджинге" ) или (в iOS 6) a UIPageViewController
. Это избавляет вас от необходимости писать такого рода обработчик жестов (и пользоваться дополнительными функциями, такими как полосы прокрутки, подпрыгивание и т.д.). В реализации UIScrollView
я просто отвечаю на событие scrollViewDidScroll
, чтобы убедиться, что я лениво загружаю необходимый subview.
Ответ 2
Вы можете создать анимацию CATransition
. Вот пример того, как вы можете сдвинуть второй вид (слева) на экран, пока не выталкивает текущий вид:
UIView *theParentView = [self.view superview];
CATransition *animation = [CATransition animation];
[animation setDuration:0.3];
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromLeft];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[theParentView addSubview:yourSecondViewController.view];
[self.view removeFromSuperview];
[[theParentView layer] addAnimation:animation forKey:@"showSecondViewController"];
Ответ 3
Если вам нужен тот же эффект прокрутки страницы/салфетки, что и у трамплина при переключении страницы, то почему бы просто не использовать UIScrollView
?
CGFloat width = 320;
CGFloat height = 400;
NSInteger pages = 3;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,width,height)];
scrollView.contentSize = CGSizeMake(width*pages, height);
scrollView.pagingEnabled = YES;
И затем используйте UIPageControl
, чтобы получить эти точки.:)