Animować zmianę kontrolerów widoku bez używania stosu kontrolerów nawigacji, podwidywań lub kontrolerów modalnych?

Navigationcontrollery mają stosy ViewController do zarządzania i ograniczone przejścia animacji.

Dodanie kontrolera widoku jako podglądu podrzędnego do istniejącego kontrolera widoku wymaga przekazywania zdarzeń do kontrolera widoku podrzędnego, co jest trudne do zarządzania, ładowane z niewielkimi irytacjami i ogólnie wydaje się złym hakerem podczas implementacji (Apple zaleca również, aby tego nie robić).

Prezentacja kontrolera widoku modalnego ponownie umieszcza kontroler widoku na innym, a podczas nie ma opisanych powyżej problemów z przekazywaniem zdarzeń, tak naprawdę nie "zamienia" kontrolera widoku, układa go w stosy.

Storyboardy są ograniczone do iOS 5 i są prawie idealne, ale nie mogą być używane we wszystkich projektach.

Czy ktoś może przedstawić przykład solidnego kodu na sposób zmiany kontrolerów widoku bez powyższych ograniczeń i pozwalający na animowane przejścia między nimi?

Bliski przykład, ale bez animacji: Jak używać wielu niestandardowych kontrolerów widoku iOS bez Kontroler nawigacyjny

Edit: użycie kontrolera Nav jest w porządku, ale muszą być animowane style przejścia (nie tylko efekty slajdów) wyświetlany kontroler widoku musi być całkowicie zamieniony(nie ułożony). Jeśli drugi kontroler widoku musi usunąć inny kontroler widoku ze stosu, to nie jest wystarczająco zamknięty.

Edit 2: iOS 4 powinien być podstawowym systemem operacyjnym dla tego pytania, powinienem był to wyjaśnić, wspominając o storyboardach (powyżej).

Author: Community, 2011-11-16

6 answers

EDIT: Nowa odpowiedź, która działa w dowolnej orientacji. Oryginalna odpowiedź działa tylko wtedy, gdy interfejs jest w orientacji pionowej. Jest to animacje przejścia widoku b / c, które zastępują widok w / inny widok musi występować z widokami co najmniej na poziomie poniżej pierwszego widoku dodanego do okna (np. window.rootViewController.view.anotherView).

Zaimplementowałem prostą klasę kontenerów, którą nazwałem TransitionController. Znajdziesz go pod adresem https://gist.github.com/1394947 .

Na marginesie, wolę implementacja w oddzielnej klasie b / c jest łatwiejsza do ponownego użycia. Jeśli tego nie chcesz, możesz po prostu zaimplementować tę samą logikę bezpośrednio w delegacie aplikacji, eliminując potrzebę klasy TransitionController. Logika, której potrzebujesz, byłaby jednak taka sama.

Użyj go w następujący sposób:

W aplikacji delegat

// 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;
}

Aby przejść do nowego kontrolera widoku z dowolnego kontrolera widoku

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

EDIT: oryginalna odpowiedź poniżej-działa tylko dla orientacja portait

Dla tego przykładu sformułowałem następujące założenia:

  1. Masz kontroler widoku przypisany jako rootViewController Twojego okna

  2. Gdy przełączysz się na nowy widok, chcesz zastąpić bieżący widok kontrolerem viewController posiadającym nowy widok. W dowolnym momencie tylko bieżący kontroler viewController jest żywy (np. alloc'ed).

Kod można łatwo zmodyfikować, aby działał inaczej, kluczowym punktem jest animowane przejście i pojedynczy kontroler widoku. Upewnij się, że kontroler widoku nie jest przechowywany poza przypisaniem go do window.rootViewController.

Kod do animowania przejścia w delegacie aplikacji

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

Przykład użycia w kontrolerze widoku

- (IBAction)flipToNextView
{
    AnotherViewController *anotherVC = [[AnotherVC alloc] init...];
    MyAppDelegate *appDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate;
    [appDelegate transitionToViewController:anotherVC
                             withTransition:UIViewAnimationOptionTransitionFlipFromRight];
}
 108
Author: XJones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-01 05:05:27

Możesz użyć nowego systemu zabezpieczeń ViewController firmy Apple. Aby uzyskać więcej szczegółowych informacji, zobacz wideo z sesji WWDC 2011 "Implementing UIViewController Contemption".

Nowe w iOS5, UIViewController zabezpieczenie pozwala na posiadanie nadrzędnego viewcontrollera i wielu podrzędnych viewcontrollerów, które są w nim zawarte. Tak działa Kontroler UISplitViewController. W ten sposób można układać Kontrolery widoków w rodzicu, ale dla konkretnej aplikacji używa się go tylko do Zarządzaj przejściem z jednego widocznego kontrolera viewController do drugiego. Jest to zatwierdzony przez Apple sposób robienia rzeczy, a animowanie z jednego widoku dziecka jest bezbolesne. Dodatkowo możesz korzystać ze wszystkich różnych przejść UIViewAnimationOption!

Ponadto, dzięki UIViewContainment, nie musisz się martwić, chyba że chcesz, o bałagan w zarządzaniu dziecięcymi kontrolerami widoku podczas wydarzeń orientacyjnych. Aby upewnić się, że parentViewController przekazuje informacje do przodu, wystarczy użyć następujących wskazówek zdarzenia do kontrolerów widoku potomnego.

- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{
    return YES;
}

Możesz wykonać następujące lub podobne w metodzie viewDidLoad twojego rodzica, aby skonfigurować pierwszy kontroler childViewController:

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

Następnie, gdy musisz zmienić podrzędny kontroler viewController, wywołujesz coś w następujący sposób w nadrzędnym kontrolerze 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];
                             }];
 }

Zamieściłem tutaj pełny przykładowy projekt: https://github.com/toolmanGitHub/stackedViewControllers. Ten inny projekt pokazuje jak użyj UIViewController zabezpieczenia na różnych typach wejściowych viewcontrollerów, które nie zajmują całego ekranu. Powodzenia

 67
Author: timthetoolman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-31 19:34:48

OK, wiem, że pytanie mówi bez używania kontrolera nawigacyjnego, ale nie ma powodu, aby nie. OP nie odpowiadała na komentarze na czas, żebym poszła spać. Nie głosuj na mnie. :)

Oto Jak otworzyć bieżący kontroler widoku i przełączyć na nowy kontroler widoku za pomocą kontrolera nawigacyjnego:

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];
 7
Author: Richard Brightwell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-16 04:00:07

Ponieważ właśnie spotkałem się z tym problemem i wypróbowałem wszystkie wcześniej istniejące odpowiedzi na ograniczony sukces, napiszę, jak ostatecznie go rozwiązałem:

Jak opisano w Ten post na niestandardowych segues, jest naprawdę bardzo łatwo zrobić niestandardowe segues. Są również bardzo łatwe do podłączenia w Interface Builder, utrzymują relacje w IB widoczne i nie wymagają dużego wsparcia ze strony kontrolerów widoku źródła / miejsca docelowego segue.

Post linked powyżej znajduje się kod systemu iOS 4, który zastępuje bieżący kontroler widoku górnego na stosie navigationController nowym za pomocą animacji slide-in-from-top.

W moim przypadku chciałem mieć podobną zamianę, ale z przejściem FlipFromLeft. Potrzebowałem też tylko wsparcia dla iOS 5+. Kod:

Z RAFlipReplaceSegue.h:
#import <UIKit/UIKit.h>

@interface RAFlipReplaceSegue : UIStoryboardSegue
@end
Z 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

Teraz, przeciągnij, aby ustawić dowolny inny rodzaj segue, następnie zrób to Niestandardowy segue i wpisz w nazwa zwyczajowej klasy segue, et voilà!

 3
Author: Ryan Artecona,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-13 04:52:44

Zmagałem się z tym przez długi czas, a jeden z moich problemów jest wymieniony tutaj , nie jestem pewien, czy miałeś ten problem. Ale oto, co polecam, jeśli musi działać z iOS 4.

Najpierw Utwórz nową klasę NavigationController. Tutaj wykonamy całą brudną robotę-inne klasy będą mogły "czysto" wywoływać metody instancji, takie jak pushViewController: i takie. W Twoim .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;

Tablica kontrolerów widoku potomnego będzie służyć jako magazyn dla wszystkich kontrolerów widoku w naszym stosie. Automatycznie przekierowywalibyśmy wszystkie obroty i zmiany rozmiaru kodu z widoku NavigationController do currentController.

Teraz, w naszej realizacji:

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

Teraz możesz zaimplementować swój własny custom pushViewController:, popViewController i takie, za pomocą tych wywołań metody.

Powodzenia i mam nadzieję, że to pomoże!
 2
Author: aopsfan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:54
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UINavigationController *viewController = (UINavigationController *)[storyboard instantiateViewControllerWithIdentifier:@"storyBoardIdentifier"];
viewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentViewController:viewController animated:YES completion:nil];

Spróbuj Tego Kodu.


Ten kod daje przejście z kontrolera widoku do innego kontrolera widoku, który ma kontroler nawigacji.

 -1
Author: Deepak Kumar Sahu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-07 09:04:15