webentwicklung-frage-antwort-db.com.de

Animieren Sie Ansichtswechsel-Controller ohne Verwendung von Navigations-Controller-Stacks, Subviews oder modalen Controllern?

NavigationController verfügen über zu verwaltende ViewController-Stapel und beschränkte Animationsübergänge.

Das Hinzufügen eines Ansichtscontrollers als Unteransicht zu einem vorhandenen Ansichtscontroller erfordert das Übergeben von Ereignissen an den Unteransichtscontroller. Dies ist mühsam zu handhaben, mit wenig Ärger beladen und fühlt sich bei der Implementierung im Allgemeinen wie ein schlechter Hacker an (Apple rät auch davon ab Dies tun).

Durch das erneute Präsentieren eines modalen Ansichtscontrollers wird ein Ansichtscontroller über einem anderen platziert, und obwohl das oben beschriebene Problem der Ereignisübergabe nicht aufgetreten ist, wird der Ansichtscontroller nicht wirklich 'ausgetauscht', sondern gestapelt.

Storyboards sind auf iOS 5 beschränkt und nahezu ideal, können jedoch nicht in allen Projekten verwendet werden.

Kann jemand ein SOLID CODE-BEISPIEL vorlegen, um die Ansichts-Controller ohne die oben genannten Einschränkungen zu ändern und animierte Übergänge zwischen ihnen zuzulassen?

Ein genaues Beispiel, aber keine Animation: Verwendung mehrerer benutzerdefinierter iOS-Ansichtscontroller ohne Navigationscontroller

Bearbeiten: Die Verwendung des Nav-Controllers ist in Ordnung, aber es müssen animierte Übergangsstile (nicht nur die Folieneffekte) vorhanden sein. Der angezeigte View-Controller muss vollständig ausgetauscht (nicht gestapelt) werden. Wenn der zweite Ansichtscontroller einen anderen Ansichtscontroller aus dem Stapel entfernen muss, ist er nicht ausreichend gekapselt.

Edit 2: iOS 4 sollte das Basis-Betriebssystem für diese Frage sein, ich hätte das klarstellen sollen, wenn ich Storyboards erwähne (oben).

141
TigerCoding

BEARBEITEN: Neue Antwort, die in jeder Ausrichtung funktioniert. Die ursprüngliche Antwort funktioniert nur, wenn sich die Benutzeroberfläche im Hochformat befindet. Hierbei handelt es sich um b/c-Ansichtsübergangsanimationen, die eine Ansicht ersetzen, wobei eine andere Ansicht mit Ansichten auftreten muss, die mindestens eine Ebene unter der ersten dem Fenster hinzugefügten Ansicht liegen (z. B. window.rootViewController.view.anotherView).

Ich habe eine einfache Containerklasse namens TransitionController implementiert. Sie finden es unter https://Gist.github.com/1394947 .

Abgesehen davon bevorzuge ich die Implementierung in einer separaten Klasse b/c, die sich leichter wiederverwenden lässt. Wenn Sie das nicht möchten, können Sie dieselbe Logik einfach direkt in Ihren App-Delegaten implementieren, ohne die Klasse TransitionController zu benötigen. Die Logik, die Sie benötigen würden, wäre jedoch die gleiche.

Verwenden Sie es wie folgt:

In Ihrem App-Delegaten

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

So wechseln Sie von einem beliebigen View-Controller zu einem neuen View-Controller

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

BEARBEITEN: Originale Antwort unten - funktioniert nur zur Porträtorientierung

Für dieses Beispiel habe ich folgende Annahmen getroffen:

  1. Sie haben einen View Controller als rootViewController Ihres Fensters zugewiesen

  2. Wenn Sie zu einer neuen Ansicht wechseln, möchten Sie den aktuellen viewController durch den viewController mit der neuen Ansicht ersetzen. Zu jedem Zeitpunkt ist nur der aktuelle viewController aktiv (z. B. zugewiesen).

Der Code kann leicht geändert werden, um anders zu funktionieren. Der entscheidende Punkt ist der animierte Übergang und der einzelne Ansichts-Controller. Stellen Sie sicher, dass Sie einen View-Controller nicht außerhalb der Zuweisung zu window.rootViewController Behalten.

Code zum Animieren des Übergangs im App-Delegaten

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

Beispiel für die Verwendung in einem View Controller

- (IBAction)flipToNextView
{
    AnotherViewController *anotherVC = [[AnotherVC alloc] init...];
    MyAppDelegate *appDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate;
    [appDelegate transitionToViewController:anotherVC
                             withTransition:UIViewAnimationOptionTransitionFlipFromRight];
}
108
XJones

Sie können das neue ViewController-Containment-System von Apple verwenden. Weitere Informationen finden Sie im WWDC 2011-Sitzungsvideo "Implementing UIViewController Containment".

Neu in iOS5 ist UIViewController Containment, mit dem Sie einen übergeordneten viewController und mehrere untergeordnete viewController verwenden können, die darin enthalten sind. So funktioniert der UISplitViewController. Auf diese Weise können Sie Ansichtscontroller in einem übergeordneten Element stapeln, aber für Ihre bestimmte Anwendung verwenden Sie nur das übergeordnete Element, um den Übergang von einem sichtbaren Ansichtscontroller zu einem anderen zu verwalten. Dies ist die Apple genehmigte Methode, Dinge zu tun und von einem untergeordneten viewController aus zu animieren. Außerdem können Sie alle verschiedenen UIViewAnimationOption Übergänge verwenden!

Außerdem müssen Sie sich mit UIViewContainment keine Sorgen machen, es sei denn, Sie möchten, dass die Verwaltung der untergeordneten viewController während der Orientierungsereignisse schwierig ist. Sie können einfach Folgendes verwenden, um sicherzustellen, dass Ihr parentViewController Rotationsereignisse an die untergeordneten viewController weiterleitet.

- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{
    return YES;
}

Sie können in der viewDidLoad-Methode Ihrer Eltern Folgendes oder Ähnliches tun, um den ersten childViewController einzurichten:

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

wenn Sie dann den untergeordneten viewController ändern müssen, rufen Sie im übergeordneten viewController Folgendes auf:

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

Ich habe hier ein vollständiges Beispielprojekt gepostet: https://github.com/toolmanGitHub/stackedViewControllers . Dieses anderes Projekt zeigt, wie UIViewController Containment für einige verschiedene Arten von Input ViewControllern verwendet wird, die nicht den gesamten Bildschirm einnehmen. Viel Glück

67
timthetoolman

OK, ich weiß, die Frage sagt ohne Verwendung eines Navigationscontrollers, aber kein Grund, nicht zu. OP reagierte nicht rechtzeitig auf Kommentare, damit ich einschlafen konnte. Stimmen Sie mich nicht ab. :)

So öffnen Sie den aktuellen Ansichts-Controller und wechseln mit einem Navigations-Controller zu einem neuen Ansichts-Controller:

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

Da ich gerade auf genau dieses Problem gestoßen bin und Variationen aller vorhandenen Antworten zu einem begrenzten Erfolg ausprobiert habe, schreibe ich, wie ich es schließlich gelöst habe:

Wie in dieser Beitrag zu benutzerdefinierten Segmenten beschrieben, ist es eigentlich ganz einfach, benutzerdefinierte Segmente zu erstellen. Sie lassen sich auch sehr einfach in Interface Builder einbinden, halten Beziehungen in IB sichtbar und erfordern nicht viel Unterstützung durch die Quell-/Ziel-View-Controller des Segues.

Der oben verlinkte Beitrag enthält iOS 4-Code, mit dem der aktuelle Controller für die Draufsicht auf dem navigationController-Stapel durch einen neuen ersetzt wird.

In meinem Fall wollte ich, dass ein ähnliches Ersetzungssegment auftritt, jedoch mit einem FlipFromLeft -Übergang. Ich brauchte auch nur Unterstützung für iOS 5+. Code:

Von RAFlipReplaceSegue.h:

#import <UIKit/UIKit.h>

@interface RAFlipReplaceSegue : UIStoryboardSegue
@end

Von 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

Ziehen Sie nun mit gedrückter Ctrl-Taste, um eine andere Art von Segue einzurichten, und geben Sie den Namen der benutzerdefinierten Segue-Klasse ein.

3
Ryan Artecona

Ich hatte lange mit diesem Problem zu kämpfen, und eines meiner Probleme ist aufgeführt hier , ich bin mir nicht sicher, ob Sie dieses Problem hatten. Aber hier ist, was ich empfehlen würde, wenn es mit iOS 4 funktionieren muss.

Erstellen Sie zunächst eine neue Klasse NavigationController. Hier erledigen wir die ganze Drecksarbeit - andere Klassen können Instanzmethoden wie pushViewController: Und dergleichen "sauber" aufrufen. In Ihrem .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;

Das Array der untergeordneten Ansichts-Controller dient als Speicher für alle Ansichts-Controller in unserem Stapel. Wir würden den gesamten Rotations- und Größenänderungscode automatisch aus der Sicht von NavigationController an currentController weiterleiten.

Jetzt in unserer Implementierung:

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

Mit diesen Methodenaufrufen können Sie jetzt Ihre eigenen benutzerdefinierten Befehle pushViewController:, popViewController und dergleichen implementieren.

Viel Glück und ich hoffe das hilft!

2
aopsfan