Анимация смены диспетчеров представлений без использования стека контроллера навигации, подсмотров или модальных контроллеров?

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

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

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

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

Может ли кто-нибудь представить пример SOLID CODE EXAMPLE на способ изменения контроллеров представлений без вышеуказанных ограничений и позволяет анимированные переходы между ними?

Близкий пример, но без анимации: Как использовать несколько пользовательских контроллеров iOS без контроллера навигации

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

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

Ответ 1

EDIT: новый ответ, который работает в любой ориентации. Первоначальный ответ работает только тогда, когда интерфейс находится в портретной ориентации. Это анимация перехода на 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];
}

EDIT: исходный ответ ниже - работает только для ориентации portait

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

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

  • При переключении на новое представление вы хотите заменить текущий 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];
}

Ответ 2

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

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

Кроме того, с UIViewContainment вам не нужно беспокоиться, если не хотите, о беспорядочности управления дочерними элементами 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 Containment для некоторых типов входных типов viewController, которые не занимают весь экран. Удачи.

Ответ 3

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

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

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];

Ответ 4

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

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

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

В моем случае я хотел, чтобы аналогичная замена segue произошла, но с переходом 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, затем сделайте его Custom segue и введите имя пользовательского класса segue, et voilà!

Ответ 5

Я долго боролся с этим, и один из моих проблем указан здесь, я не уверен, что у вас была эта проблема. Но вот то, что я бы рекомендовал, если он должен работать с 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];
}

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

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

Ответ 6

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UINavigationController *viewController = (UINavigationController *)[storyboard instantiateViewControllerWithIdentifier:@"storyBoardIdentifier"];
viewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentViewController:viewController animated:YES completion:nil];

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


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