【问题标题】:How to initialize a NSWindowController in Swift?如何在 Swift 中初始化 NSWindowController?
【发布时间】:2014-07-04 15:09:15
【问题描述】:

我想从一个 nib 文件初始化一个窗口控制器对象,很简单吧?但我根本无法让它工作。

根据我之前在ObjC的经验,我写了如下代码:

init()  {
    super.init(windowNibName: "SplitWindowController")
}

在应用程序委托文件中,我只是简单地初始化并显示窗口:

var myWindowController: MyWindowController = MyWindowController()
myWindowController.showWindow(self)
myWindowController.window.makeKeyAndOrderFront(nil)

但是编译器给了我这个错误:Must call a designated initializer of the superclass 'NSWindowController'。而根据NSWindowController的Swift版本定义,只有3个指定初始化器,分别是init()、init(window)、init(coder)。我不知道下一步该做什么。我应该从一个 nib 文件构建一个NSCoder,我不知道该怎么做吗?

【问题讨论】:

标签: cocoa swift appkit


【解决方案1】:

你快到了。您确实可以覆盖 init() 作为便利初始化程序,其方式与您习惯的 Obj-C 代码等效:

import Cocoa

class MyWindowController: NSWindowController {

    override convenience init() {
        self.init(windowNibName: "<xib name>")
    }
}

请注意,您在self 上调用init(windowNibName:),因为init() 是一个方便的初始化程序,您仍然从超类继承所有初始化程序。来自documentation:

规则 1:指定初始化器必须调用指定初始化器 来自它的直接超类。

规则 2:便利构造器必须调用另一个构造器 同一个班级。

规则 3:便利构造器最终必须调用指定的 初始化器。

另外,正如上面提到的@weichsel,确保将File's Owner 的类设置为NSWindowController 的子类(在上面的示例中,这将是MyWindowController)和然后将其window 插座与窗口本身连接。

话虽如此,我不确定为什么编译器要求添加 override 关键字。尽管NSWindowController 是NSResponder 的子类,它定义了一个init(),但即使它实现了等效的继承层次结构,以下代码也可以毫无问题地编译:

class A {
    init() { }
}

class B: A {
    init(Int) {
        super.init()
    }
    convenience init(String) {
        self.init(5)
    }
}

class C: B {
    convenience init() {
        self.init("5")
    }
}

【讨论】:

  • 我在 Xcode 7.3 中收到“Initializer does not override a specified initializer from its superclass”错误。删除 override 关键字可以消除错误。
  • @NicolasMiari,显然,自从写这篇文章以来,Apple 已经解决了这个特定的错误。感谢您记下这一点!同样改变的是我们现在必须显式标记未命名的参数(而不是init(Int),我们现在写init(_: Int)。
【解决方案2】:

NSWindowController 有 2 个指定的初始化器:

init(window: NSWindow!)
init(coder: NSCoder!)

当创建一个子类时,你应该调用它的超类的指定初始化器。最新版本的 Xcode 强制执行此操作。通过内置语言机制 (Swift) 或通过 NS_DESIGNATED_INITIALIZER 宏 (Objective-C)。

Swift 还要求您在覆盖便利构造器时调用超类指定构造器。
来自 Swift 编程指南的"Initialization: Designated Initializers and Convenience Initializers" 部分:

如果您要覆盖的初始化程序是便利初始化程序, 您的覆盖必须从自己的调用另一个指定的初始化程序 子类,按照上面在初始化链中描述的规则。

在您的情况下,您可能应该覆盖 init(window: NSWindow!) 并从那里调用 super 的对应方。

【讨论】:

  • 感谢您的解释,我想知道如何从 nib 文件构造一个 NSWindow 对象?
  • 我的意思是应该从 nib 文件构造窗口对象,对吗?以及如何从 nib 文件构建窗口?
  • 如果您的窗口控制器有专用的 xib,您可以在文件所有者的身份检查器中将您的子类设置为“自定义类”。请注意,当从 xib 创建窗口控制器时,如果您想在初始化程序中执行特定操作,则必须覆盖 initWithCoder 而不是 initWithWindow。
  • @weichsel 你能详细说明一下吗?我想继承 NSWindowController 以便子类知道初始化时要加载什么 Nib。在 Obc-C 中,这是通过从“init”调用“initWithNibName:”来完成的。如何在 Swift 中做到这一点?
  • 我遇到了同样的问题。我发现的最好的是这里的第二个答案(改用类工厂方法):stackoverflow.com/questions/24220638/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-08
  • 1970-01-01
  • 1970-01-01
  • 2015-01-16
  • 2014-12-21
相关资源
最近更新 更多