在不使用导航控制器堆栈,子视图或模态控制器的情况下更改视图控制器的动画?

时间:2011-11-16 03:18:51

标签: iphone ios ipad uiviewcontroller uianimation

NavigationControllers具有ViewController堆栈来管理和限制动画过渡。

将视图控制器作为子视图添加到现有视图控制器需要将事件传递给子视图控制器,这很难管理,加载时几乎没有烦恼,并且通常在实现时感觉像是一个糟糕的黑客(Apple)也建议不要这样做。)

再次呈现一个模态视图控制器会将一个视图控制器放在另一个上面,虽然它没有上面描述的事件传递问题,但它并没有真正“交换”视图控制器,而是堆叠它。

故事板仅限于iOS 5,几乎是理想的,但不能在所有项目中使用。

有人可以在没有上述限制的情况下更改视图控制器的方式提供SOLID代码示例,并允许它们之间的动画转换吗?

一个近似的例子,但没有动画: How to use multiple iOS custom view controllers without a navigation controller

编辑:导航控制器使用很好,但需要动画过渡样式(不仅仅是幻灯片效果),显示的视图控制器需要完全交换(不是堆叠)。如果第二个视图控制器必须从堆栈中删除另一个视图控制器,那么它的封装不够。

编辑2:iOS 4应该是这个问题的基本操作系统,我应该在提到故事板(上图)时澄清。

6 个答案:

答案 0 :(得分:108)

编辑:适用于任何方向的新答案。 原始答案仅在界面处于纵向时才有效。这是b / c视图过渡动画,用于替换具有不同视图的视图,视图必须至少比添加到窗口的第一个视图低一级(例如window.rootViewController.view.anotherView)。

我实现了一个名为TransitionController的简单容器类。您可以在https://gist.github.com/1394947找到它。

顺便说一句,我更喜欢在单独的类b / c中实现它更容易重用。如果您不想这样,您可以直接在您的app委托中实现相同的逻辑,从而无需TransitionController类。然而,你需要的逻辑是相同的。

按如下方式使用:

在您的app delegate

// 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存活(例如,分配)。

  3. 可以轻松修改代码以使其工作方式不同,关键点是动画过渡和单视图控制器。确保在将视图控制器分配给window.rootViewController之外的任何地方都不会保留视图控制器。

    代码修改app delegate中的转换

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

答案 1 :(得分:67)

您可以使用Apple的新viewController包含系统。有关更深入的信息,请查看WWDC 2011会话视频“实施UIViewController遏制”。

iOS5的新功能,UIViewController包含允许您拥有父视图控件和包含在其中的多个子视图控制器。这就是UISplitViewController的工作原理。这样做可以在父级中堆叠视图控制器,但对于您的特定应用程序,您只需使用父级来管理从一个可见的viewController到另一个的可见视图控制器的转换。这是Apple批准的做事方式和动画从一个孩子viewController是无痛的。另外,您可以使用各种不同的UIViewAnimationOption转换!

此外,使用UIViewContainment,除非您想要了解在定向事件期间管理子视图控制器的混乱,否则您不必担心。您只需使用以下命令确保您的parentViewController将轮换事件转发给子viewControllers。

- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{
    return YES;
}

您可以在父视图中使用以下或类似方法设置第一个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。这个other project显示了如何在一些不占用整个屏幕的各种输入viewController类型上使用UIViewController包含。 祝你好运

答案 2 :(得分:7)

好的,我知道这个问题没有使用导航控制器,但没有理由不这样做。 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];

答案 3 :(得分:3)

由于我刚刚遇到了这个确切的问题,并尝试了所有已有的成功变化的答案,我将发布我最终如何解决它:

this post on custom segues所述,实际上很容易制作自定义segues。它们也非常容易在Interface Builder中连接,它们使IB中的关系可见,并且它们不需要segue的源/目标视图控制器的大量支持。

上面链接的帖子提供了iOS 4代码,用new-a动画替换了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

现在,按住Ctrl键拖动设置任何其他种类的segue,然后将其设为自定义segue,并输入自定义segue类的名称,etvoilà!

答案 4 :(得分:2)

我很长时间都在努力解决这个问题,其中一个问题列在here,我不确定你是否遇到过这个问题。但是如果它必须适用于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等。

祝你好运,我希望这会有所帮助!

答案 5 :(得分:-1)

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

试用此代码。

此代码提供从视图控制器到另一个具有导航控制器的视图控制器的转换。