通过Swift编程实现导航到另一个视图控制器/场景

100

我正在使用以下代码来编程方式导航到另一个视图控制器。它可以正常工作,但是它会在某种程度上隐藏导航栏我该如何修复这个问题?(如果有影响的话,导航栏是通过将ViewController嵌入导航控制器创建的。)

let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)

let nextViewController = storyBoard.instantiateViewControllerWithIdentifier("nextView") as NextViewController
self.presentViewController(nextViewController, animated:true, completion:nil)
8个回答

239

Swift 5

默认的模态展示样式是卡片式。这会在顶部显示上一个视图控制器,并允许用户滑动关闭所呈现的视图控制器。

如果要保留旧样式,您需要像这样修改将要呈现的视图控制器:

newViewController.modalPresentationStyle = .fullScreen

这适用于无论是通过编程创建还是通过Storyboard创建的控制器。

Swift 3

对于以编程方式创建的控制器

如果您想要导航到以编程方式创建的控制器,则可以执行以下操作:

let newViewController = NewViewController()
self.navigationController?.pushViewController(newViewController, animated: true)

使用已创建的StoryBoard控制器

如果您想要导航到标识符为 "newViewController" 的 StoryBoard 控制器,则可以执行以下操作:

let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "newViewController") as! NewViewController
        self.present(newViewController, animated: true, completion: nil)

2
在故事板选项中不需要 "as! NewViewController"。 - Lavi Avigdor
2
是的我知道那是可选的,但如果我们显示它,另一个开发者就可以清楚地知道哪个视图控制器是目标。 - jaiswal Rajan
5
在主线程中调用 storyBoard.instantiateViewControllerself.present,否则会导致视图控制器组件出现延迟。 - Alex
2
大家好:作为一般规则,我可以建议使用更友好的语气吗?(这在SO上是一个相当大的问题) 至于主题:虽然OP没有直接提问,但我认为这个好答案仍然是一个适当的地方,以便提醒(任何人阅读)必须在主线程上执行操作。 - superjos
1
只是给我和有同样问题的人一个提示:如果将代码放在viewDidLoad函数中,它不起作用。 - bfhaha
你好,如何在Swift 5中使用self.present?当我使用self.present(newViewController, animated: true, completion: nil)时,它现在显示错误。感谢您的帮助。 - user3064009

42

SWIFT 4.x

双引号中的字符串总是让我感到困惑,因此我认为需要一些图形展示来澄清这个问题。

对于一个银行应用程序,我有一个LoginViewController和一个BalanceViewController。每个都有它们各自的屏幕。

应用程序启动并显示登录屏幕。当登录成功时,应用程序打开余额屏幕。

这是它的外观:

enter image description here

enter image description here

登录成功的处理方式如下:

let storyBoard: UIStoryboard = UIStoryboard(name: "Balance", bundle: nil)
let balanceViewController = storyBoard.instantiateViewController(withIdentifier: "balance") as! BalanceViewController
self.present(balanceViewController, animated: true, completion: nil)

如您所见,小写的storyboard ID“balance”是代码第二行需要输入的内容,这个ID在故事板设置中有定义,详见附图。

大写字母'B'的术语“Balance”是故事板文件的名称,用于代码的第一行。

我们知道,在代码中使用硬编码字符串是一种非常不好的做法,但在iOS开发中,它却成为了一种常见的做法,而且Xcode甚至不会发出警告。


令人惊叹的演示 - Elias Fazel
1
老兄,你太棒了! - OhhhThatVarun
谢谢,伙计!根据被接受的答案不知道该怎么做... - Ovi Trif

21

你应该使用当前导航控制器来推送新的视图控制器,而不是使用present方法。

self.navigationController.pushViewController(nextViewController, animated: true)

为什么我们应该进行推送?有没有好处? - DragonFire
1
@DragonFire,因为操作者希望它能够在不覆盖导航栏的情况下工作。如果您想要像 WhatsApp 联系人屏幕和聊天屏幕之间那样具有主细节页面设计,则必须使用导航控制器“推送”视图控制器(这将从左到右进行动画)。如果您只想在当前屏幕上显示、呈现或弹出(这将从底部到顶部进行动画),只需使用“present”。 - Okan Kocyigit
谢谢!使用它后,我的导航控制器选项卡等正常工作。 - David_2877

14

根据@jaiswal Rajan在 他的答案,您可以按照以下方式执行pushViewController:


let storyBoard: UIStoryboard = UIStoryboard(name: "NewBotStoryboard", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "NewViewController") as! NewViewController
self.navigationController?.pushViewController(newViewController, animated: true)

1
嗨,我在想:为什么要重复已经发布的答案呢?也许留下评论或点个赞也可以。或者还有其他原因吗? - superjos
他正在演示如何使用NavigationController进行导航。谢谢。 - Hiran D.A Walawage

6

如果您呈现一个视图控制器,它将不会显示在导航控制器中。它只会占据整个屏幕。对于这种情况,您必须创建另一个导航控制器,并将您的nextViewController作为其根视图控制器,并呈现这个新的导航控制器。

另一种方法是直接推送视图控制器。

self.presentViewController(nextViewController, animated:true, completion:nil)

更多信息请查看苹果文档:- https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/doc/uid/TP40006926-CH3-SW96


presentViewController has been renamed to present(loginController, animated:true, completion:nil) - kite_n_code

5
OperationQueue.main.addOperation {
   let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
   let newViewController = storyBoard.instantiateViewController(withIdentifier: "Storyboard ID") as! NewViewController
   self.present(newViewController, animated: true, completion: nil)
}

当我将代码放入OperationQueue.main.addOperation中时,它对我起作用了,这将在主线程中执行。

1
上述代码运行良好,但如果您想从一个NSObject类导航,您将无法使用self.present:
let storyBoard = UIStoryboard(name:"Main", bundle: nil)
if let conVC = storyBoard.instantiateViewController(withIdentifier: "SoundViewController") as? SoundViewController,
    let navController = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController {
    
    navController.pushViewController(conVC, animated: true)
}

1

其他答案都很好,我想讲述我的情况,在那里我需要制作一个动画启动屏幕,然后在3到4秒的动画之后,下一个任务是转移到主屏幕。我尝试了segue,但这为目标视图创建了问题。因此最后我访问了AppDelegate的Window属性,并将一个新的NavigationController屏幕分配给它。

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let homeVC = storyboard.instantiateViewController(withIdentifier: "HomePageViewController") as! HomePageViewController

//Below's navigationController is useful if u want NavigationController in the destination View
let navigationController = UINavigationController(rootViewController: homeVC)
appDelegate.window!.rootViewController = navigationController

如果您不想在目标视图中使用navigationController,则只需分配如下:

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let homeVC = storyboard.instantiateViewController(withIdentifier: "HomePageViewController") as! HomePageViewController
appDelegate.window!.rootViewController = homeVC

类型为“AppDelegate”的值没有成员“window”。 - undefined

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