在不使用导航控制器堆栈、子视图或模式控制器的情况下对视图控制器进行动画更改?

发布于 2024-12-15 23:44:33 字数 684 浏览 1 评论 0原文

导航控制器具有要管理的 ViewController 堆栈和有限的动画转换。

将视图控制器作为子视图添加到现有视图控制器需要将事件传递到子视图控制器,这管理起来很痛苦,充满了一些烦恼,并且通常在实现时感觉像是一个糟糕的黑客(Apple 也建议不要这样做)这样做)。

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

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

有人可以提供一个可靠的代码示例,说明如何更改视图控制器而不受上述限制并允许它们之间的动画转换吗?

一个很接近的例子,但没有动画: 如何使用多个 iOS 自定义视图控制器没有导航控制器

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

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

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).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

眼眸 2024-12-22 23:44:33

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

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

顺便说一句,我更喜欢在单独的类 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 是活动的(例如已分配的)。

可以轻松修改代码以实现不同的工作方式,关键点是动画过渡和单视图控制器。确保除了将视图控制器分配给 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];
}

EDIT: New answer that works in any orientation.
The original answer only works when the interface is in portrait orientation. This is b/c view transition animations that replace a view w/ a different view must occur with views at least a level below the first view added to the window (e.g. window.rootViewController.view.anotherView).

I've implemented a simple container class I called TransitionController. You can find it at https://gist.github.com/1394947.

As an aside, I prefer the implementation in a separate class b/c it's easier to reuse. If you don't want that, you could simply implement the same logic directly in your app delegate eliminating the need for the TransitionController class. The logic you'd need would be the same however.

Use it as follows:

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

I made the following assumptions for this example:

  1. You have a view controller assigned as the rootViewController of your window

  2. When you switch to a new view you want to replace the current viewController with the viewController owning the new view. At any time, only the current viewController is alive (e.g. alloc'ed).

The code can be easily modified to work differently, the key point is the animated transition and the single view controller. Make sure you don't retain a view controller anywhere outside of assigning it to 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];
}
这样的小城市 2024-12-22 23:44:33

您可以使用 Apple 的新 viewController 遏制系统。如需更深入的信息,请观看 WWDC 2011 会议视频“实现 UIViewController Containment”。

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

另外,使用 UIViewContainment,除非您愿意,否则您不必担心在方向事件期间管理子 viewController 的混乱情况。您可以简单地使用以下命令来确保您的parentViewController 将旋转事件转发到子viewController。

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

然后,当您需要更改子视图控制器时,您可以在父视图控制器中调用类似以下内容的内容:

-(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 Containment不占据整个屏幕。
祝你好运

You can use Apple's new viewController containment system. For more in-depth information check out the WWDC 2011 session video "Implementing UIViewController Containment".

New to iOS5, UIViewController Containment allows you to have a parent viewController and a number of child viewControllers that are contained within it. This is how the UISplitViewController works. Doing this you can stack view controllers in a parent, but for your particular application you are just using the parent to manage the transition from one visible viewController to another. This is the Apple approved way of doing things and animating from one child viewController is painless. Plus you get to use all the various different UIViewAnimationOption transitions!

Also, with UIViewContainment, you do not have to worry, unless you want to, about the messiness of managing the child viewControllers during orientation events. You can simply use the following to make sure your parentViewController forwards rotation events to the child viewControllers.

- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{
    return YES;
}

You can do the following or similar in your parent's viewDidLoad method to setup the first childViewController:

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

then when you need to change the child viewController, you call something along the lines of the following within the parent 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];
                             }];
 }

I posted a full example project here: https://github.com/toolmanGitHub/stackedViewControllers. This other project shows how to use UIViewController Containment on some various input viewController types that do not take up the whole screen.
Good luck

别再吹冷风 2024-12-22 23:44:33

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

OK, I know the question says without using a navigation controller, but no reason not to. OP wasn't responding to comments in time for me to go to sleep. Don't vote me down. :)

Here's how to pop the current view controller and flip to a new view controller using a navigation 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];
心如狂蝶 2024-12-22 23:44:33

由于我刚刚遇到了这个确切的问题,并尝试了所有现有答案的变体,但取得的成功有限,我将发布我最终如何解决它的方法:

这篇关于自定义转场的文章,实际上非常简单进行自定义转场。它们在 Interface Builder 中也非常容易连接,它们使 IB 中的关系保持可见,并且它们不需要 segue 的源/目标视图控制器的太多支持。

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

就我而言,我希望发生类似的替换转场,但使用 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

现在,按住 Control 并拖动以设置任何其他类型的 Segue,然后将其设为自定义 Segue,并输入自定义 Segue 类的名称,等等!

Since I just happened across this exact problem, and tried variations on all the pre-existing answers to limited success, I'll post how I eventually solved it:

As described in this post on custom segues, it's actually really easy to make custom segues. They are also super easy to hook up in Interface Builder, they keep relationships in IB visible, and they don't require much support by the segue's source/destination view controllers.

The post linked above provides iOS 4 code to replace the current top view controller on the navigationController stack with a new one using a slide-in-from-top animation.

In my case, I wanted a similar replace segue to happen, but with a FlipFromLeft transition. I also only needed support for iOS 5+. Code:

From RAFlipReplaceSegue.h:

#import <UIKit/UIKit.h>

@interface RAFlipReplaceSegue : UIStoryboardSegue
@end

From 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

Now, control-drag to set up any other kind of segue, then make it a Custom segue, and type in the name of the custom segue class, et voilà!

楠木可依 2024-12-22 23:44:33

我在这个问题上挣扎了很长一段时间,我的问题之一列在这里,不知道你是否也遇到过这个问题。但如果它必须与 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 等。

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

I struggled with this one for a long time, and one of my issues is listed here, I'm not sure if you have had that problem. But here's what I would recommend if it must work with iOS 4.

Firstly, create a new NavigationController class. This is where we'll do all the dirty work--other classes will be able to "cleanly" call instance methods like pushViewController: and such. In your .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;

The child view controllers array will serve as a store for all the view controllers in our stack. We would automatically forward all rotation and resizing code from the NavigationController's view to the currentController.

Now, in our implementation:

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

Now you can implement your own custom pushViewController:, popViewController and such, using these method calls.

Good luck, and I hope this helps!

ぺ禁宫浮华殁 2024-12-22 23:44:33
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UINavigationController *viewController = (UINavigationController *)[storyboard instantiateViewControllerWithIdentifier:@"storyBoardIdentifier"];
viewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentViewController:viewController animated:YES completion:nil];

试试这个代码。


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

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

Try This Code.


This code gives Transition from a view controller to another view controller which having a navigation controller.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文