Animate change of view controllers without using navigation controller stack, subviews or modal controllers?

NavigationControllers have ViewController stacks to manage, and limited animation transitions.

Adding a view controller as a sub-view to an existing view controller requires passing events to the sub-view controller, which is a pain to manage, loaded with little annoyances and in general feels like a bad hack when implementing (Apple also recommends against doing this).

Presenting a modal view controller again places a view controller on top of another, and while it doesn't have the event passing problems described above, it doesn't really 'swap' the view controller, it stacks it.

Storyboards are limited to iOS 5, and are almost ideal, but cannot be used in all projects.

Can someone present a SOLID CODE EXAMPLE on a way to change view controllers without the above limitations and allows for animated transitions between them?

A close example, but no animation: How to use multiple iOS custom view controllers without a navigation controller

Edit: Nav Controller use is fine, but there needs to be animated transition styles (not simply the slide effects) the view controller being shown needs to be swapped completely (not stacked). If the second view controller must remove another view controller from the stack, then it's not encapsulated enough.

Edit 2: iOS 4 should be the base OS for this question, I should have clarified that when mentioning storyboards (above).

82243 次浏览

好吧,我知道问题是说不用导航控制器,但没理由不用。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];

你可以使用苹果新的 viewController 控制系统。要了解更多深入的信息,请查看 WWDC 2011年会议视频“实现 UIViewController遏制”。

对于 iOS5来说,UIViewController遏制允许您拥有一个父 viewController 和包含在其中的许多子 viewController。这就是 UISplitViewController 的工作方式。这样做,您可以在父视图控制器中堆栈视图控制器,但是对于您的特定应用程序,您只是使用父视图控制器来管理从一个可见 viewController 到另一个可见 viewController 的转换。这是苹果认可的做事方式,从一个孩子的角度来看动画控制器是无痛的。另外,您可以使用所有不同的 UIViewAnimationOption转场!

另外,对于 UIViewCon遏,您不必担心(除非您愿意)在方向事件期间管理子 viewController 的混乱。您可以简单地使用以下方法来确保您的 ParentViewController 将旋转事件转发给子 viewController。

- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{
return YES;
}

您可以在父节点的 viewDidLoad 方法中执行以下操作或类似操作来设置第一个 children ViewController:

[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。这个 其他项目展示了如何在不占用整个屏幕的各种输入 viewController 类型上使用 UIViewController遏制。 祝你好运

我与这一个斗争了很长时间,我的问题之一是列出 给你,我不知道你是否有这个问题。但是如果它必须和 iOS4一起工作的话,这里有我的建议。

首先,创建一个新的 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等。

祝你好运,希望这个能帮上忙!

编辑: 适用于任何方向的新答案。 原来的答案只有当界面在纵向方向时才能工作。这是 b/c 视图转换动画,取代了一个视图 w/一个不同的视图必须发生的视图至少低于第一个视图添加到窗口(例如 window.rootViewController.view.anotherView)的水平。

我已经实现了一个简单的容器类,我称之为 TransitionController

顺便说一句,我更喜欢在单独的类 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];
}

编辑: 下面的原始答案-只作品的肖像方向

对于这个例子,我做了以下假设:

  1. 有一个视图控制器被分配为窗口的 rootViewController

  2. 当您切换到一个新视图时,您希望将当前 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];
}

因为我只是碰巧遇到了这个问题,并且尝试了所有已经存在的对有限成功的答案的不同版本,我将发布我最终是如何解决它的:

正如在 这篇关于习俗的文章中所描述的,实际上制作自定义接口非常容易。它们也非常容易在 Interface Builder 中连接起来,它们使 IB 中的关系可见,并且它们不需要 segue 的源/目标视图控制器提供太多支持。

上面链接的帖子提供了 iOS 4代码,使用从顶部滑入的动画将导航控制器栈上当前的顶视图控制器替换为一个新的顶视图控制器。

在我的例子中,我希望发生一个类似的替换继承,但使用 FlipFromLeft转换。我也只需要 iOS5 + 的支持。密码:

来自 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 类的名称,瞧!

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

试试这个代码。


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