ChatGPT解决这个技术问题 Extra ChatGPT

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

NavigationController 具有要管理的 ViewController 堆栈和有限的动画转换。

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

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

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

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

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

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

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

您可以使用导航控制器进行自定义动画转换。如果这是可以接受的,请从您的问题中删除该约束,我将发布一个代码示例。
@Richard 如果它跳过了管理堆栈的麻烦并在视图控制器之间适应不同的动画过渡样式,那么导航控制器的使用就可以了!
好的好的。我不耐烦并发布了代码。试试看。为我工作。
@RichardBrightwell 您在这里说过可以使用导航控制器在视图控制器之间进行自定义动画转换......如何?你可以发布一个例子吗?谢谢。

P
Paras Joshi

编辑:适用于任何方向的新答案。原始答案仅在界面为纵向时有效。这是 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];
}

编辑:下面的原始答案 - 仅适用于纵向

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

您将视图控制器分配为窗口的 rootViewController 当您切换到新视图时,您希望将当前视图控制器替换为拥有新视图的视图控制器。在任何时候,只有当前的 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];
}

是的,很好!它不仅可以解决问题,而且是一个非常简单和干净的代码示例。非常感谢!
他不会给你带来景观问题吗?另外:这会触发 viewControllers 的 willAppear 和 didAppear 方法吗?
我知道出现呼叫正在完成,因为我记录了它们。我也不明白为什么这会影响方向变化。你能详细说明为什么你认为它会吗?
@XJones 很棒的解决方案,在我的 iPad 应用程序中运行良好,除了我面临的一个问题,在第一次初始化 transVC=[TransitionController ... initWithViewController:aUINavCtrlObj]; window.rootVC=transVC; aUINavCtrlObj 中的视图从顶部填充 20 像素(即底部 20 像素在所有方向都超出屏幕(UIWindow)边界),但是在我执行 [transVC transitionToViewController:anotherVC] 之后,填充消失了。我在TransitionController的loadView中尝试了wantsFullScreenLayout=NO,它的作用是在statusBar下方添加了一个20 px的黑色区域。
@AbduliamRehmanius:我遇到了同样的问题。我通过将 TransitionController.m 的第 25 行更改为 UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds]; 来修复它,但我只在最新版本的 iOS 上使用过它,因此请仔细测试。
R
ReactiveRaven

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

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

此外,使用 UIViewContainment,除非您愿意,否则您不必担心在方向事件期间管理子视图控制器的混乱。您可以简单地使用以下内容来确保您的 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。此 other project 展示了如何在不占用整个屏幕的一些不同输入 viewController 类型上使用 UIViewController Containment。祝你好运


一个很好的例子。感谢您花时间发布代码。另一方面,它仅限于 iOS 5。正如问题中提到的:“[Storyboards] 仅限于 iOS 5,并且几乎是理想的,但不能在所有项目中使用。”考虑到很大比例(大约 40%?)的客户仍在使用 iOS 4,我们的目标是提供适用于 iOS 4 及更高版本的功能。
您不应该在转换前调用 [self.currentViewController willMoveToParentViewController:nil]; 吗?
@FelixLam - 根据 UIViewController 包含的文档,只有当您覆盖 addChildViewController 方法时,您才会调用 willMoveToParentViewController 。在此示例中,我正在调用它,但没有覆盖。
在 WWDC 的演示中,我似乎记得他们在调用开始转换之前调用了它,因为转换并不意味着 currentVC 将变为 nil。在 UITabBarController 的情况下,转换不会更改任何 vc 的父级。从父级移除调用 didMoveToParentViewController:nil,但没有 will... 调用。恕我直言
R
Richard Brightwell

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

这不是堆栈视图控制器吗?
是的,因为我们使用的是导航控制器。但是,它绕过了您可以执行哪种转换的限制,我认为这是您问题的核心。
关闭,但最大的问题之一是堆栈上有多个视图控制器要管理。我正在寻找完全改变视图控制器的方法。 =)
啊。我可能也有类似的东西……给我一分钟。如果没有,我会删除这个答案。
好的,我将两个不同的代码拼凑在一起。我认为这会做你想要的。
R
Ryan Artecona

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

this post on custom segues 中所述,制作自定义转场实际上非常容易。它们在 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 类的名称,等等!


有没有办法在没有情节提要的情况下以编程方式解决这个问题?
据我所知,要使用 segue,您必须定义它并在情节提要中给它一个标识符。您可以使用 UIViewController 的 –performSegueWithIdentifier:sender: 方法在代码中调用 segue。
你永远不应该直接调用 viewDidXXX 和 viewWillXXX。
C
Community

我在这个问题上苦苦挣扎了很长时间,其中一个问题列在 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 等。

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


同样,我们必须求助于添加视图控制器作为现有视图控制器的子视图。当然,这就是现有的导航控制器所做的,但这意味着我们基本上必须重写它以及它的所有方法。实际上,我们应该避免分发 viewWillAppear 和类似的方法。我开始认为没有干净的方法可以做到这一点。但是,我非常感谢您花时间和精力!
我认为,通过这个根据需要添加和删除视图控制器的系统,这个解决方案可以防止你不得不转发这些方法。
你确定吗?我以前用类似的方式交换视图控制器,我总是不得不转发消息。可以另外确认吗?
不,我不确定,但我会假设,只要您在视图控制器的视图消失时删除它们,并在它们出现时添加它们,这应该会自动触发 viewWillAppearviewDidAppear 等.
D
Deepak Kumar Sahu
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UINavigationController *viewController = (UINavigationController *)[storyboard instantiateViewControllerWithIdentifier:@"storyBoardIdentifier"];
viewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentViewController:viewController animated:YES completion:nil];

试试这个代码。

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