在iOS中从已呈现的视图控制器中推送视图

28

简而言之:

我想知道如何从被呈现的视图控制器中推出另一个视图控制器。

简要说明:

我有一个名为MainViewController的视图控制器,其中有一个按钮。当我点击这个按钮时,我会呈现一个名为LoginViewController的视图。

在这个页面(LoginViewController)上,我再次有一个按钮,当我点击它时,我尝试推出我的视图控制器(称为HomeViewController), 但它没有推出。

这是我的代码片段:

MainViewController.m

- (IBAction)LoginClicked:(id)sender {
    LoginViewController *vc = [[LoginViewController alloc] init];
    [self presentViewController:vc animated:YES completion:nil];
}

LoginViewController.m

- (IBAction)buttonActionMethodOnLoginView:(id)sender{
     NSLog(@"viewControllers %@",APPDELEGATE.nav.viewControllers);
     //LoginViewController is not in this array
     HomeViewController *obj = [[HomeViewController alloc] init];
     [self.navigationController pushViewController:obj animated:YES];
}

但是,对我来说这并没有用。同时,我在push之前打印了a stack of view controllers,但它并没有包含LoginViewController。所以,在没有把LoginViewController添加到a stack of view controllers的情况下,如何从LoginViewController推到HomeViewController?当我从HomeViewController返回时,LoginViewController应该被打开。这是否可能使用单个NavigationController?请注意:这里只是举了一个例子,介绍了使用登录、主页和主控制器。但我想要其他屏幕也能实现这样的功能。

2
你的LoginViewController必须有它的NavigationController来从LoginViewController进行推送。 - technerd
@technerd:是的,但我该如何完成? - Meet Doshi
@MeetDoshi,你有没有看过我的答案?有帮助到你吗? - Hemang
@MeetDoshi,你最近更新的问题完全改变了它,现有的回答都不符合这个“新”的问题。 - Nat
我知道那个.. 但是我想要那个.. 谢谢你回答解决我的问题.. 请根据这个帮助我.. 我最近对这个问题的更新已经成为了被接受的答案.. - Meet Doshi
显示剩余2条评论
11个回答

29

嗨,当你呈现登录视图控制器时,只需呈现一个导航控制器,如下所示:

LoginVC *loginVCObj =[[LoginVC alloc]initWithNibName:@"LoginVC" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:loginVCObj];
[self presentViewController:nav animated:YES completion:nil];

现在你的PresentedViewController是一个navigation controller,现在你可以简单地将其推入你的Home VC。

  HomeViewController *obj = [[HomeViewController alloc] init];
 [self.navigationController pushViewController:obj animated:YES];

希望这对您有所帮助


使用这段代码,我可以成功地“推出”视图控制器。但是LoginVC没有被“解除显示”。所以当我解除LoginVC的显示时,它会返回到HomeViewController。而当我“弹出”它时,它会返回到MainViewController。所以这并没有解决我的问题。 - Meet Doshi
让我根据我的代码来解释一下。当您在mainVC上并呈现'UINavigationController' nav时,现在您位于不同的NavigationController上,而不是之前的那个,如果您想从LoginVC推到HomeVC,则您的mainVC不会在此导航中。 - Shubham bairagi
好的。但是如何使用一个 NavigationController 来完成这个操作? - Meet Doshi
3
你放在 completion block 中了什么?天啊! - Luca D'Alberti

12

LoginViewController 不应该被推入导航控制器堆栈中。下面我将描述一下“为什么”。

我们的 MainViewController 应该在堆栈中 - 你总是想要回到那里。

// AppDelegate.m (only if you don't use storyboards, if you do - you don't need to copy this part of code)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // create the window
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [self.window setBackgroundColor:[UIColor whiteColor]];
    [self.window makeKeyAndVisible];

    // set view controllers
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:[[MainViewController alloc] init]];
    [self.window setRootViewController:navigationController];
}

在特定的动作下展示 LoginViewController。 您不希望用户能够点击“返回”并回到MainViewController。稍后,您也不希望用户返回LoginViewController。因此,您需要将其呈现为模态:

// inside `MainViewController.m`
- (IBAction)myCoolActionToShowLogin:(id)sender {
    [self presentViewController:[[LoginViewController alloc] init] animated:YES completion:nil];
}

现在我们可以看到LoginViewController。当用户完成登录后,关闭该窗口并呈现HomeViewController:
// inside `LoginViewController.m`
- (IBAction)myAwesomeActionToShowHome:(id)sender {
    UINavigationController *navigationController = (UINavigationController *)[UIApplication.sharedApplication.keyWindow rootViewController];
    [navigationController pushViewController:[[HomeViewController alloc] init] animated:YES];
    [self dismissViewControllerAnimated:YES completion:nil];
}

注意事项:

您可能已经注意到,myAwesomeActionToShowHome: 需要您将导航控制器设置为您的 rootViewController。这样做是可行的,但应该更好地检查是否实际上是导航控制器而不是强制转换它。或者您可以创建一个委托或块来推送新的导航控制器。这是最快、最简单、最有效的解决方案,但以后应该加以改进。

真的应该阅读:苹果开发者 ->“视图控制器编程”文档,因为这些是您应该了解正确开发和设计用户体验所需的核心基础知识

这里有一个可用的演示样例


好的,谢谢。当我们点击推出按钮时,您已经关闭了那个视图。但是现在,当我从HomeViewController返回时,我想要打开我的已呈现视图(LoginViewController),那我应该怎么做呢? - Meet Doshi
1
@MeetDoshi 我为您创建了一个可工作的演示 :) 请查看链接: https://github.com/natalia-osa/SOFPushPopDemo - Nat
谢谢您的回复和演示。是的,我知道。这里,我只是举了一个LoginViewController的例子。我想把它应用到其他屏幕上。您的演示效果相同。但是我希望,当我从CViewController返回时,BViewController应该打开。 - Meet Doshi
1
如果你想回到那个控制器,它不应该通过 presentViewController:animated: 来呈现,而应该通过 self.navigationController pushNavigationController: 来呈现。导航控制器堆栈将处理前进和后退。present.. 仅在您想要显示它而不是“保存在历史记录”时使用。你似乎对如何使用 UINavigationControllerUITabBarController 和管理视图控制器有一些盲点。请阅读:https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/index.html。 - Nat
1
@MeetDoshi 很高兴我能帮到你,谢谢你的接受。在答案中提供了演示链接 ;) - Nat
显示剩余5条评论

10

您无法从已呈现的视图控制器进行推送。我建议您应该维护您的导航层次结构。

为此,您应该从您的MainViewController中呈现LoginViewController,并且您应该为MainViewController传递导航控制器。

- (IBAction)openLogin:(id)sender {
    LoginViewController *loginVC = (LoginViewController *) [self.storyboard instantiateViewControllerWithIdentifier:@"login"];
    [loginVC setReferencedNavigation:self.navigationController];
    [self presentViewController:loginVC animated:YES completion:nil];
}

然后在LoginViewController内部,您应该像这样推到HomeViewController,

LoginViewController.h

@interface LoginViewController : UIViewController {
    UINavigationController *refNavigationController;
}
- (void) setReferencedNavigation:(UINavigationController *)refNavCon;

LoginViewController.m

- (void) setReferencedNavigation:(UINavigationController *)refNavCon {
    refNavigationController = refNavCon;
}

- (IBAction)openHome:(id)sender {
    [self dismissViewControllerAnimated:YES completion:^{
        UIViewController *homeVC = [self.storyboard instantiateViewControllerWithIdentifier:@"home"];
        [refNavigationController pushViewController:homeVC animated:YES];
    }];
}

这样做看起来好像是从LoginViewController推出去,但实际上是从MainViewController推出去。

您可以自定义此方法以保持此流程的动画和UI。

图片描述


它在Storyboard中可以正常工作...但是XIB呢?我想将其实现到XIB项目中。 - Meet Doshi
为什么?XIB有什么问题吗?你只需要改变创建视图控制器对象的方式。 - Hemang
是的,我尝试过了。但是手动添加我的视图控制器到导航控制器堆栈中是不行的。 - Meet Doshi
1
你可以查看这个这个这个。这些链接展示了如何通过编程方式创建导航控制器并附加视图控制器。如果你还需要更多帮助,请告诉我。 - Hemang
1
我不确定你为什么这样做。这似乎是不合适的。相反,你的MainViewController应该在UINavigationController内部。因此,在呈现LoginViewController时,您不需要重置任何内容。例如:LoginViewController *controller = [[LoginViewController alloc] initWithNibName:@"LoginView" bundle:[NSBundle mainBundle]]; - Hemang
显示剩余2条评论

8

1) 展示一个导航控制器,其中根视图控制器设置为视图控制器。

- (IBAction)LoginClicked:(id)sender 
{
    LoginViewController *loginViewController = [LoginViewController alloc] init];
    UINavigationController *navController = [UINavigationController alloc] initWithRootViewController:loginViewController];
    [self presentViewController:navController animated:YES completion:nil];
}

- (IBAction)buttonActionMethodOnLoginView:(id)sender
{
    HomeViewController *obj = [[HomeViewController alloc] init];
    [self.navigationController pushViewController:obj animated:YES];
}

希望这对您有用。


6
问题在于LoginViewController没有导航控制器。然后你需要给它添加一个。

MainViewController.m

创建一个UINavigationController,将LoginViewController放入堆栈中并呈现UINavigationController

- (IBAction)LoginClicked:(id)sender {
    LoginViewController *vc = [[LoginViewController alloc] init];
    UINavigationController = nav = [[UINavigationController alloc] init];
    nav.viewControllers = @[vc];
    [self presentViewController:nav animated:YES completion:nil];
}

LoginViewController.m

- (IBAction)buttonActionMethodOnLoginView:(id)sender{
    HomeViewController *obj = [[HomeViewController alloc] init];
    [self.navigationController pushViewController:obj animated:YES];
}

取消

在你的MainViewController中调用dismissViewControllerAnimated


6

对于Swift 3.0:

将您的视图控制器呈现为新的rootViewController。

let navController = UINavigationController.init(rootViewController: self.storyboard!.instantiateViewController(withIdentifier: "SignInViewController"))
self.present(navController, animated: true, completion: {})

现在从已呈现的视图控制器中推出您的视图控制器。
self.show(self.storyboard!.instantiateViewController(withIdentifier: "SignUpViewController"), sender: self)

4
创建一个UINavigationController实例 [[UINavigationController alloc] initWithRootViewController:[[LoginViewController alloc] init]] 呈现该navigationController,然后推送您想要的任何VC。

4

这是一个非常简单的代码,用于呈现视图控制器和推送视图控制器。

- (IBAction)LoginClicked:(id)sender {
        LoginViewController *objLogicVC = [LoginViewController alloc] init];
        UINavigationController *navPresent = [UINavigationController alloc] initWithRootViewController:objLogicVC];
        [self presentViewController:navPresent animated:YES completion:nil];
}

- (IBAction)buttonActionMethodOnLoginView:(id)sender{
        HomeViewController *objHomeVC = [[HomeViewController alloc] init];
        [self.navigationController pushViewController:objHomeVC animated:YES];
}

你没有理解我的意思。请再次阅读我的问题。我的主要问题是,“当我从HomeViewController返回时,应该打开LoginViewController。” - Meet Doshi

4
你需要从你的第一个视图(MainViewController)进行Push,但是你可以像PresentView和DismissView一样使用动画。使用以下代码进行操作:
对于Push(在MainViewController上),使用以下代码:
LoginViewController *VC = [[LoginViewController alloc]init];
CATransition* transition = [CATransition animation];
transition.duration = 0.3f;
transition.type = kCATransitionMoveIn;
transition.subtype = kCATransitionFromTop;
[self.navigationController.view.layer addAnimation:transition
                                            forKey:kCATransition];
[[[UINavigationController alloc] initWithRootViewController:VC] pushViewController:VC animated:NO];
//[self.navigationController pushViewController:VC animated:NO];

对于 Pop 动画(在 LoginViewController 中)

CATransition* transition = [CATransition animation];
transition.duration = 0.3f;
transition.type = kCATransitionReveal;
transition.subtype = kCATransitionFromBottom;
[self.navigationController.view.layer addAnimation:transition
                                            forKey:kCATransition];
[self.navigationController popViewControllerAnimated:NO];

使用这段代码,您可以获得与“呈现-解除视图控制器”相同的动画效果。有关更多详细信息,请参见此答案
之后,您可以使用您的代码将LoginViewController推送到HomeViewController
希望这是您要寻找的内容。如有任何疑问,请随时与我联系。:)

4

MainViewController.m

 - (IBAction)LoginClicked:(id)sender {

     LoginViewController *vc = [[LoginViewController alloc] init];

     UINavigationController *loginNav = [[UINavigationController alloc] initWithRootViewController:vc]; 

     [self presentViewController:loginNav animated:YES completion:nil];  

 }

LoginViewController.m

- (IBAction)buttonActionMethodOnLoginView:(id)sender{
     NSLog(@"viewControllers %@",APPDELEGATE.nav.viewControllers);
     //LoginViewController is not in this array
     HomeViewController *obj = [[HomeViewController alloc] init];
     [self.navigationController pushViewController:obj animated:YES];
}

这对我不起作用。为什么你要呈现两次viewController? - Meet Doshi
是的,那是一个打字错误。现在你可以尝试一下,它会起作用的。 - darshan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接