可以在不使用 StoryBoard 的情况下执行 segue 吗?

Possible to perform segue without the use of StoryBoard?

到目前为止,我一直在完全以编程方式创建我的 Swift 项目。我已经创建了详细的导航控制器和 Table 视图控制器,而没有接触 StoryBoard (SB) 一次。我已经到了我想将数据从 TableViewCell 单击传递到另一个 ViewController 的地步,但是,我相信我需要使用 segue 和 create/identify 它里面SB。由于这个应用程序随着时间的推移变得相当复杂,模仿 SB 内部的所有视图控制器变得非常困难,并且在 SB 内部创建任何更改都不会反映模拟器上视图内部的任何更改(我有将 SB 内部的视图与其各自的 class 配对,但没有任何效果。当标识符匹配时,即使是 segue 也无法识别)。因此,我有几个问题。

  1. 是否可以通过编程方式执行转场?换句话说,是否可以在不接触故事板的情况下将数据从一个视图控制器传递到另一个视图控制器?

  2. 是否有可以遵循的简单指南或技巧,以便在 StoryBoard 中模仿他们的代码?对于一个简单的应用程序,应该不会太困难。但是,如果有人完全以编程方式创建应用程序,是否还有其他方法可以在 StoryBoard 中描绘应用程序?

Interface Builder (IB) 只是一个用于编程开发​​的 GUI。您在 IB 中可以做的任何事情您肯定可以通过编程方式完成,但是您可以通过编程方式做的一切您都可以在 IB 中做,因为 IB 只是用于界面构建——只是一些界面构建,而不是全部。

segue 只是 IB 术语。在 iOS 中,您只能通过三种方式显示视图控制器:push(通过 UINavigationController)、present(通过表示对象供应商 UIViewControllerTransitioningDelegate)或show(一种更通用的显示视图控制器的方法)。

由于您是程序化 iOS 开发的新手(最好的一种,IMO),快速 101:

class ProgrammaticViewController: UIViewController {

    override func loadView() {

        // do not call super.loadView()
        // most IB developers don't even know this method exists
        //   because this is where IB does its work
        // add all of your view objects here (scroll views,
        //   table views, buttons, everything)

        setView()
        addTableView()
        ...

    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // do call super.viewDidLoad(), however
        // do your post-view setup here, like adding observers
        //   or fetching data
        // this method is called after the entire view
        //   has been loaded into memory so consider that

    }

    // other lifecycle events that come after viewDidLoad where you
    //   can perform last-second work include viewDidLayoutSubviews(),
    //   viewWillAppear(), viewDidAppear(), etc.

    deinit {
        // do any cleanup here like deactivating timers, removing
        //   observers, etc.
    }

    // MARK: Methods

    func setView() {

        // if you're creating your view controller programmatically,
        //   you must create the view controller's actual view property
        //   and it must be done before adding any subviews to the
        //   view controller's view

        view = UIView()
        view.frame = UIScreen.main.bounds
        view.backgroundColor = UIColor.white

    }

}

如果您正在使用自动布局(您可能应该使用),则不必总是使用 view.frame = UIScreen.main.bounds 显式设置视图的框架。如果它是应用程序的根视图控制器,则不需要 - window 拥有该视图控制器并且它将设置框架。如果您随后在根目录中放置了 UINavigationController 并且您正在使用自动布局,那么您推送的视图控制器中的 none 也需要它们的框架集。因此,唯一需要使用 view.frame = UIScreen.main.bounds 之类的东西显式设置视图框架的实时情况是当您以模态方式呈现视图控制器时。这是因为模态呈现的视图控制器不属于 window 或导航控制器;它暂时存在于临时容器视图中。在这种情况下,您必须设置它的框架。

以编程方式向前传递数据比对 IB 开发人员来说更简单。只需实例化一个视图控制器,为它的一个(非私有)属性注入一个值,然后推送、呈现或显示它。 didSelectRowAtUITableView 委托可能如下所示:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    let p = indexPath.row
    let detailViewController = SomeDetailViewController()

    detailViewController.someProperty = searchResults[p]
    navigationController?.pushViewController(detailViewController, animated: true)

}

这显然是可行的,因为您已经在那个视图控制器中创建了那个 属性 并且没有给它一个私有访问修饰符。

class SomeDetailViewController: UIViewController {

    var dataObject: SomeType? // injectable
    private var notInjectable: SomeType?

}

更新: 就 Apple 而言,随着 SwiftUI 的引入,IB 显然不是 iOS 开发的未来,这是另一种以编程方式开发的方式iOS。就个人而言,我讨厌 GUI 编程,我很高兴看到它慢慢被淘汰。

Is it possible to perform a segue programmatically?

当然可以。 UIStoryboardSegue 具有可用于创建 segue 的初始值设定项,并且它具有一个 perform() 方法,您可以调用该方法使 segue 执行此操作。但 segues 主要是一种工具,可以让故事板中的场景之间的转换变得更容易,所以如果你一开始不使用故事板,就没有理由想要这样做。

In other words, is it possible to pass data from one view controller to another without touching the story board?

再一次,是的。情节提要直到 iOS 5 才出现,你可以打赌人们在编写应用程序时会在视图控制器之间进行转换。你已经得到关于如何做到这一点的答案很好。不过,更一般地说,视图控制器只是对象,一个人向另一个人发送信息所需要做的就是对目标控制器的有效引用。

However, is there any other way in order to portray the application in the StoryBoard if someone has been creating it completely programmatically?

如果你的意思是你想切换到使用故事板,那么是的,有一些很好的方法可以做到这一点。如果你的意思是你想以某种方式在故事板中以编程方式表示你的应用程序正在做什么,但继续以编程方式做所有事情,那么不,没有办法做到这一点(而且这样做真的没有意义)。

过渡到故事板不一定是全部或 none 之类的事情。您可以在故事板中只建模一个视图控制器,并根据需要使用 init(name:bundle:)(初始化故事板本身)和 instantiateViewController(withIdentifier:)(加载特定视图控制器)加载它,然后只使用该视图控制器和你现在做的一样。然后可以删除用于设置该视图控制器及其视图层次结构的所有代码,因为您现在是从情节提要中加载它。一旦成功,对其他视图控制器重复该过程,扩展您的故事板并根据需要删除设置代码。