在Swift 3中以编程方式创建一个没有XIB的NSViewController

时间:2016-09-04 16:38:46

标签: swift xcode macos cocoa

我正在尝试在不使用Interface Builder的情况下制作macOS应用。我的项目构建和运行,但我的主视图控制器似乎没有加载其视图。也就是说,不会调用viewDidLoad()方法。我正在使用Xcode-beta 8.0 beta 6(8S201h)。

NSViewController的Swift 3文档说明了view属性:

  

如果在访问它时尚未设置此属性的值,则视图控制器将调用loadView()方法。反过来,该方法从视图控制器的nibNamenibBundle属性标识的nib文件中设置视图。

     

如果要直接设置视图控制器的视图,请在创建视图控制器后立即设置此属性的值。

对于viewDidLoad

  

对于以编程方式创建的视图控制器,在loadView()方法完成后立即调用此方法。

最后,对于isViewLoaded

  

视图控制器使用其视图的延迟加载:在视图控制器加载到内存后,其isViewLoaded属性的值立即为falsetrue方法返回后,系统调用loadView()方法之前,值将更改为viewDidLoad()

所以文档让我相信它是可能的。

我的项目如下:

 .
 ├── 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")
    }
}

使用上述方法构建和运行项目会产生此控制台输出:

false
true

也就是说,视图会加载控制器,但永远不会调用loadViewviewDidLoad方法。

我错过了什么?

2 个答案:

答案 0 :(得分:7)

仔细阅读文档是有启发性的。具体做法是:

  

如果访问时该属性的值尚未设置,则视图控制器会调用loadView()方法。

含义,只有在属性具有值之前尝试读取loadView()属性时,才会调用view。直接为属性赋值将绕过方法调用。

所以如果AppDelegate.applicationDidFinishLaunching(_:)写成这样:

    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)

...然后系统会调用loadView来尝试加载视图:

false
PrimaryController.loadView
false

请注意,viewDidLoad()仍未被调用,因为没有视图可以自动与控制器关联(即,笔尖为nilPrimaryController.xib不存在)。完成此过程的正确方法是在PrimaryController.loadView()

中手动加载视图
print("PrimaryController.loadView")
view = NSView() // instantiate and bind a view to the controller

现在该程序提供了所需的结果:

false
PrimaryController.loadView
PrimaryController.viewDidLoad
true

答案 1 :(得分:4)

从代码(Swift 4.1)

制作ViewController的最小设置
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()
   }
}