Programmatically create an NSViewController without an XIB in Swift 3

假装没事ソ 提交于 2020-07-18 04:59:06

问题


I'm trying to make a macOS app without using Interface Builder. My project builds and runs, but my primary view controller doesn't seem to be loading its view. That is, the viewDidLoad() method is not invoked. I'm using Xcode-beta 8.0 beta 6 (8S201h).

The Swift 3 documentation for NSViewController says this about the view property:

If this property’s value is not already set when you access it, the view controller invokes the loadView() method. That method, in turn, sets the view from the nib file identified by the view controller’s nibName and nibBundle properties.

If you want to set a view controller’s view directly, set this property’s value immediately after creating the view controller.

And for viewDidLoad:

For a view controller created programmatically, this method is called immediately after the loadView() method completes.

Finally, for isViewLoaded:

A view controller employs lazy loading of its view: Immediately after a view controller is loaded into memory, the value of its isViewLoaded property is false. The value changes to true after the loadView() method returns and just before the system calls the viewDidLoad() method.

So the documentation leads me to believe it is possible.


My project looks like this:

 .
 ├── main.swift
 └── Classes
     ├── AppDelegate.swift
     └── Controllers   
         └── PrimaryController.swift

main.swift

import Cocoa

let delegate = AppDelegate()
NSApplication.shared().delegate = delegate
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

AppDelegate.swift

import Cocoa

class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        let pc = PrimaryController(nibName: nil, bundle: nil)!
        print(pc.isViewLoaded)
        pc.view = NSView()
        print(pc.isViewLoaded)
    }
}

PrimaryController.swift

import Cocoa

class PrimaryController: NSViewController {
    override func loadView() {
        print("PrimaryController.loadView")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        print("PrimaryController.viewDidLoad")
    }
}

Building and running a project with the above yields this console output:

false
true

That is, the view loads for the controller, but the loadView and viewDidLoad methods are never invoked.

What am I missing?


回答1:


A careful reading of the documentation is enlightening. Specifically:

If this property’s value is not already set when you access it, the view controller invokes the loadView() method.

Meaning, loadView() is only invoked when an attempt is made to read the view property before the property has a value. Assigning a value to the property directly will bypass the method call.

So if AppDelegate.applicationDidFinishLaunching(_:) is written like this:

    let pc = PrimaryController(nibName: nil, bundle: nil)!
    print(pc.isViewLoaded)
    let x = pc.view // accessing the view property before it has a value
    print(pc.isViewLoaded)

...then loadView will be invoked by the system to attempt to load the view:

false
PrimaryController.loadView
false

Note that viewDidLoad() is still not invoked, since no view can be automatically associated with the controller (ie, the nib is nil and PrimaryController.xib doesn't exist). The correct way to complete the process is to manually load the view in PrimaryController.loadView():

print("PrimaryController.loadView")
view = NSView() // instantiate and bind a view to the controller

Now the program gives the desired result:

false
PrimaryController.loadView
PrimaryController.viewDidLoad
true



回答2:


Minimal setup to make ViewController from code (Swift 4.1)

class WelcomeViewController: NSViewController {

   private lazy var contentView = MyCustomClassOrJustNSView()

   override func loadView() {
      view = contentView
   }

   init() {
      super.init(nibName: nil, bundle: nil)
   }

   required init?(coder: NSCoder) {
      fatalError()
   }
}


来源:https://stackoverflow.com/questions/39319177/programmatically-create-an-nsviewcontroller-without-an-xib-in-swift-3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!