Анимация изменения контроллеров представления без использования стека контроллера навигации, подпредставлений или модальных контроллеров? - PullRequest
141 голосов
/ 16 ноября 2011

NavigationController имеет стеки ViewController для управления и ограниченные анимационные переходы.

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

Представление модального контроллера представления снова помещает контроллер представления поверх другого, и пока он не делаетУ него нет проблем с передачей событий, описанных выше, он на самом деле не «подменяет» контроллер представления, он укладывает его в стек.

Раскадровки ограничены iOS 5 и почти идеальны, но не могут использоваться во всехпроекты.

Может ли кто-нибудь представить ПРИМЕР СОЛИДНОГО КОДА для способа изменения контроллеров представления без вышеуказанных ограничений и допускает анимированные переходы между ними?

Пример закрытия, но без анимации: Как использовать несколько пользовательских контроллеров представления iOS безконтроллер навигации

Редактировать: использование Nav Controller - это нормально, но должны быть анимированные стили перехода (а не просто эффекты слайдов), отображаемый контроллер представления должен быть полностью заменен (не сложен).Если второй контроллер представления должен удалить другой контроллер представления из стека, то он недостаточно инкапсулирован.

Редактировать 2: iOS 4 должна быть базовой ОС для этого вопроса, я должен был уточнить, что при упоминании раскадровок (выше).

Ответы [ 6 ]

108 голосов
/ 23 ноября 2011

РЕДАКТИРОВАТЬ: Новый ответ, который работает в любой ориентации. Оригинальный ответ работает только тогда, когда интерфейс находится в портретной ориентации.Это анимация перехода вида b / c, которая заменяет вид, иначе должен быть вид, при котором представления, по крайней мере, на уровне ниже первого, добавленного в окно (например, window.rootViewController.view.anotherView).

Я реализовалпростой контейнерный класс, который я назвал TransitionController.Вы можете найти его по адресу https://gist.github.com/1394947.

Кроме того, я предпочитаю реализацию в отдельном классе b / c, которую проще использовать повторно.Если вы этого не хотите, вы можете просто реализовать ту же логику непосредственно в делегате приложения, устраняя необходимость в классе TransitionController.Однако логика, в которой вы нуждаетесь, будет такой же.

Используйте ее следующим образом:

В вашем приложении делегат

// add a property for the TransitionController

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    MyViewController *vc = [[MyViewContoller alloc] init...];
    self.transitionController = [[TransitionController alloc] initWithViewController:vc];
    self.window.rootViewController = self.transitionController;
    [self.window makeKeyAndVisible];
    return YES;
}

Для перехода на новый контроллер вида из любого контроллера вида

- (IBAction)flipToView
{
    anotherViewController *vc = [[AnotherViewController alloc] init...];
    MyAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
    [appDelegate.transitionController transitionToViewController:vc withOptions:UIViewAnimationOptionTransitionFlipFromRight];
}

РЕДАКТИРОВАТЬ: оригинальный ответ ниже - работает только для ориентации портрета

Я сделал следующие предположения для этого примера:

  1. У вас есть контроллер вида, назначенный как rootViewController вашего окна

  2. Когда вы переключаетесь на новое представление, вы хотите заменить текущий viewController на viewController, которому принадлежит новое представление.В любое время активен только текущий viewController (например, alloc'ed).

Код может быть легко изменен для работы по-другому, ключевой момент - анимированный переход и единое представлениеконтроллер.Убедитесь, что вы не сохраняете контроллер представления нигде, кроме того, чтобы назначить его window.rootViewController.

Код для перехода анимации в делегате приложения

- (void)transitionToViewController:(UIViewController *)viewController
                    withTransition:(UIViewAnimationOptions)transition
{
    [UIView transitionFromView:self.window.rootViewController.view
                        toView:viewController.view
                      duration:0.65f
                       options:transition
                    completion:^(BOOL finished){
                        self.window.rootViewController = viewController;
                    }];
}

Пример использования в контроллере вида

- (IBAction)flipToNextView
{
    AnotherViewController *anotherVC = [[AnotherVC alloc] init...];
    MyAppDelegate *appDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate;
    [appDelegate transitionToViewController:anotherVC
                             withTransition:UIViewAnimationOptionTransitionFlipFromRight];
}
67 голосов
/ 20 ноября 2011

Вы можете использовать новую систему сдерживания viewController от Apple. Для получения более подробной информации посмотрите видео сессии WWDC 2011 "Внедрение UIViewController Сдерживание".

Новое в iOS5, UIViewController Containment позволяет вам иметь родительский viewController и несколько дочерних viewController, которые содержатся в нем. Вот как работает UISplitViewController. Делая это, вы можете сложить контроллеры представления в родительском, но для вашего конкретного приложения вы просто используете родительский для управления переходом от одного видимого viewController к другому. Это одобренный Apple способ работы, и анимация с помощью одного дочернего viewController безболезненна. Кроме того, вы можете использовать все различные UIViewAnimationOption переходы!

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

- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{
    return YES;
}

Вы можете сделать следующее или подобное в методе viewDidLoad вашего родителя для установки первого childViewController:

[self addChildViewController:self.currentViewController];
[self.view addSubview:self.currentViewController.view];
[self.currentViewController didMoveToParentViewController:self];
[self.currentViewController.swapViewControllerButton setTitle:@"Swap" forState:UIControlStateNormal];

затем, когда вам нужно изменить дочерний viewController, вы вызываете что-то вроде следующего в родительском viewController:

-(void)swapViewControllers:(childViewController *)addChildViewController:aNewViewController{
     [self addChildViewController:aNewViewController];
     __weak __block ViewController *weakSelf=self;
     [self transitionFromViewController:self.currentViewController
                       toViewController:aNewViewController
                               duration:1.0
                                options:UIViewAnimationOptionTransitionCurlUp
                             animations:nil
                             completion:^(BOOL finished) {
                                   [aNewViewController didMoveToParentViewController:weakSelf];

                                   [weakSelf.currentViewController willMoveToParentViewController:nil];
                                   [weakSelf.currentViewController removeFromParentViewController];

                                   weakSelf.currentViewController=[aNewViewController autorelease];
                             }];
 }

Я разместил полный пример проекта здесь: https://github.com/toolmanGitHub/stackedViewControllers. Этот другой проект показывает, как использовать UIViewController Ограничение для некоторых различных типов ввода viewController, которые не занимают весь экран. Удачи

7 голосов
/ 16 ноября 2011

ОК, я знаю, что вопрос говорит без использования контроллера навигации, но нет причин не делать этого. ОП не отвечала на комментарии вовремя, чтобы я пошла спать. Не голосуй за меня. :)

Вот как вынуть текущий контроллер вида и перейти к новому контроллеру вида с помощью контроллера навигации:

UINavigationController *myNavigationController = self.navigationController;
[[self retain] autorelease];

[myNavigationController popViewControllerAnimated:NO];

PreferencesViewController *controller = [[PreferencesViewController alloc] initWithNibName:nil bundle:nil];

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.65];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:myNavigationController.view cache:YES];
[myNavigationController pushViewController:controller animated:NO];
[UIView commitAnimations];

[controller release];
3 голосов
/ 13 октября 2012

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

Как описано в этом посте о пользовательских сегментах , на самом деле очень легко создавать собственные сегменты. Их также очень легко подключить в Интерфейсном Разработчике, они поддерживают отношения в IB видимыми и не требуют особой поддержки со стороны контроллеров представления исходного / конечного объекта.

В сообщении, указанном выше, приведен код iOS 4 для замены текущего контроллера вида сверху в стеке navigationController новым, использующего анимацию скольжения сверху.

В моем случае я хотел, чтобы произошла аналогичная замена, но с переходом FlipFromLeft. Мне также нужна была только поддержка iOS 5+. Код:

Из RAFlipReplaceSegue.h:

#import <UIKit/UIKit.h>

@interface RAFlipReplaceSegue : UIStoryboardSegue
@end

Из RAFlipReplaceSegue.m:

#import "RAFlipReplaceSegue.h"

@implementation RAFlipReplaceSegue

-(void) perform
{
    UIViewController *destVC = self.destinationViewController;
    UIViewController *sourceVC = self.sourceViewController;
    [destVC viewWillAppear:YES];

    destVC.view.frame = sourceVC.view.frame;

    [UIView transitionFromView:sourceVC.view
                        toView:destVC.view
                      duration:0.7
                       options:UIViewAnimationOptionTransitionFlipFromLeft
                    completion:^(BOOL finished)
                    {
                        [destVC viewDidAppear:YES];

                        UINavigationController *nav = sourceVC.navigationController;
                        [nav popViewControllerAnimated:NO];
                        [nav pushViewController:destVC animated:NO];
                    }
     ];
}

@end

Теперь, перетащите, чтобы настроить любой другой вид перехода, затем сделайте его собственным, и введите имя пользовательского класса segue, et voilà!

2 голосов
/ 23 ноября 2011

Я долго боролся с этим, и одна из моих проблем перечислена здесь , я не уверен, что у вас возникла эта проблема.Но вот что я бы порекомендовал, если он должен работать с iOS 4.

Во-первых, создайте новый класс NavigationController.Здесь мы выполним всю грязную работу - другие классы смогут «чисто» вызывать методы экземпляра, такие как pushViewController: и подобные.В вашем .h:

@interface NavigationController : UIViewController {
    NSMutableArray *childViewControllers;
    UIViewController *currentViewController;
}

- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL))completion;
- (void)addChildViewController:(UIViewController *)childController;
- (void)removeChildViewController:(UIViewController *)childController;

массив контроллеров дочерних представлений будет служить хранилищем для всех контроллеров представлений в нашем стеке.Мы автоматически перенаправили бы весь код поворота и изменения размера из вида NavigationController в currentController.

Теперь в нашей реализации:

- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL))completion
{
    currentViewController = [toViewController retain];
    // Put any auto- and manual-resizing handling code here

    [UIView animateWithDuration:duration animations:animations completion:completion];

    [fromViewController.view removeFromSuperview];
}

- (void)addChildViewController:(UIViewController *)childController {
    [childViewControllers addObject:childController];
}

- (void)removeChildViewController:(UIViewController *)childController {
    [childViewControllers removeObject:childController];
}

Теперь вы можете реализовать свой собственныйcustom pushViewController:, popViewController и т. д. с использованием этих вызовов методов.

Удачи, и я надеюсь, что это поможет!

0 голосов
/ 07 июня 2016
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UINavigationController *viewController = (UINavigationController *)[storyboard instantiateViewControllerWithIdentifier:@"storyBoardIdentifier"];
viewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentViewController:viewController animated:YES completion:nil];

Попробуйте этот код.


Этот код дает переход от контроллера вида к другому контроллеру вида, который имеет контроллер навигации.

...