首页 文章

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

提问于
浏览
141

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

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

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

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

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

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

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

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

6 回答

  • 2

    EDIT: New answer that works in any orientation. 原始答案仅在界面处于纵向时才有效 . 这是用于替换具有不同视图的视图的b / c视图过渡动画,其必须至少比添加到窗口的第一视图低一级(例如 window.rootViewController.view.anotherView ) .

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

    顺便说一句,我更喜欢在一个单独的类b / c中实现它想要的,你可以直接在你的app委托中实现相同的逻辑,而不需要 TransitionController 类 . 然而,你需要的逻辑是相同的 .

    使用方法如下:

    In your 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;
    }
    

    To transition to a new view controller from any view controller

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

    EDIT: Original Answer below - only works for portait orientation

    我为这个例子做了以下假设:

    • 您将视图控制器指定为窗口的 rootViewController

    • 当您切换到新视图时,您希望将当前viewController替换为拥有新视图的viewController . 在任何时候,只有当前的viewController是活的(例如,alloc'ed) .

    代码可以很容易地修改为不同的工作,关键点是动画过渡和单视图控制器 . 确保在将视图控制器分配给 window.rootViewController 之外的任何地方都不保留视图控制器 .

    Code to animate transition in 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;
                        }];
    }
    

    Example use in a view controller

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

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

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

    此外,使用UIViewContainment,除非您想要了解在定向事件期间管理子视图控制器的混乱,否则您不必担心 . 您只需使用以下命令确保您的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 . 这个other project显示了如何在一些不占用整个屏幕的各种输入viewController类型上使用 UIViewController Containment . 祝好运

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

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

    this post on custom segues中所述,它需要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类的名称,etvoilà!

  • 7

    我很长一段时间都在努力解决这个问题,其中一个问题列出了here,如果它必须适用于iOS 4,我会推荐的 .

    首先,创建一个新的 NavigationController 类 . 这是我们将要做的所有脏工作 - 其他类将能够"cleanly"调用实例方法,如 pushViewController: 等 . 在你的_1432906中:

    @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 等 .

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

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

    试试此代码 .


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

相关问题