在Swift和init(windowNibName)中对NSWindowController进行子类化

时间:2014-06-14 13:55:31

标签: cocoa swift initializer nswindowcontroller

我正在尝试在Swift中启动一个基于Cocoa项目的新文档,并希望创建一个NSWindowController的子类(正如Apple在基于文档的应用程序的指南中所推荐的那样)。在ObjC中,您将创建一个NSWindowController子类的实例,发送initWithWindowNibName:消息,并相应地实现,调用超类方法。

在Swift中init(windowNibName)仅作为便利初始化程序使用,NSWindowController的指定初始化程序为init(window),显然希望我在窗口中传递。

我不能从我的子类中调用super.init(windowNibName),因为它不是指定的初始值设定项,所以我显然必须实现convenience init(windowNibName),而self.init(window)又需要调用{{1}}。但是,如果我只有我的nib文件,如何访问nib文件的窗口以发送到该初始化程序?

5 个答案:

答案 0 :(得分:15)

您需要覆盖NSWindowControllerinit()init(window)init(coder))的所有三个指定初始值设定项,或者都不覆盖它们,在这种情况下,您的子类将自动覆盖继承init(windowNibName)和所有其他便利初始化程序,您将能够使用超类的便利初始化程序构建它:

// this overrides none of designated initializers
class MyWindowController: NSWindowController {
    override func windowDidLoad() {
        super.windowDidLoad()
    }
}

// this one overrides all of them
//
// Awkwardly enough, I see only two initializers 
// when viewing `NSWindowController` source from Xcode, 
// but I have to also override `init()` to make these rules apply.
// Seems like a bug.
class MyWindowController: NSWindowController
{
    init()
    {
        super.init()
    }

    init(window: NSWindow!)
    {
        super.init(window: window)
    }

    init(coder: NSCoder!)
    {
        super.init(coder: coder)
    }

    override func windowDidLoad() {
        super.windowDidLoad()
    }
}

// this will work with either of the above
let mwc: MyWindowController! = MyWindowController(windowNibName: "MyWindow")

这包括"初始化/自动初始化器继承"在语言指南中:

  

但是,如果满足某些条件,则会自动继承超类初始值设定项。实际上,这意味着您不需要在许多常见场景中编写初始化程序覆盖,并且可以在安全的情况下以最小的努力继承您的超类初始化程序。

     

假设您为在子类中引入的任何新属性提供默认值,则适用以下两个规则:

     

规则1   如果您的子类没有定义任何指定的初始值设定项,它会自动继承其所有超类指定的初始值设定项。

     

规则2   如果您的子类提供了所有超类指定初始化器的实现 - 通过按照规则1继承它们,或者通过提供自定义实现作为其定义的一部分 - 那么它会自动继承所有超类便捷初始化器。

答案 1 :(得分:14)

您可以简单地覆盖windowNibName属性并返回硬编码字符串,而不是覆盖任何init方法。这允许您调用基本的vanilla init方法来创建窗口控制器。

class WindowController: NSWindowController {

    override var windowNibName: String! {
        return "NameOfNib"
    }
}

let windowController = WindowController()

我比调用let windowController = WindowController(windowNibName: "NameOfNib")更喜欢这个,因为nib的名称是一个实现细节,应该完全封装在窗口控制器类中,并且永远不会暴露在外面(而且调用WindowController()更容易)。

如果要向init方法添加其他参数,请执行以下操作:

  • 在自定义init方法中调用super.init(window: nil)。这将通过NSWindowController属性windowNibName获得初始化。
  • 覆盖所需的init(coder: NSCoder)方法,以配置对象或只是调用fatalError()禁止其使用(运行时为albiet)。
class WindowController: NSWindowController {

    var test: Bool

    override var windowNibName: String! {
        return "NameOfNib"
    }

    init(test: Bool) {
        self.test = test
        super.init(window: nil) // Call this to get NSWindowController to init with the windowNibName property
    }

    // Override this as required per the class spec
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented. Use init()")

        // OR

        self.test = false
        super.init(coder: coder)
    }
}

let windowController = WindowController(test: true)

答案 2 :(得分:5)

我能够解决这个问题,只需要一个调用便捷初始化器的类方法,修改自定义变量,然后返回新对象。

所以Objective C init方法看起来像这样:

//Class.h
@class PPPlugInInfo;

@interface PPPlugInInfoController : NSWindowController
//...
- (id)initWithPlugInInfo:(PPPlugInInfo *)plugInfo;
//...
@end

//Class.m
#include "Class.h"

@interface PPPlugInInfoController ()
@property (strong) PPPlugInInfo *info;
@end

@implementation PPPlugInInfoController

- (id)initWithPlugInInfo:(PPPlugInInfo *)plugInfo;
{
    if (self = [self initWithWindowNibName:@"PPPlugInInfoController"]) {
        self.info = plugInfo;
    }
    return self;
}

@end

这就是我使用Swift版本的方式:

class PPPluginInfoController: NSWindowController {
    private var info: PPPlugInInfo!
    class func windowControllerFromInfo(plugInfo: PPPlugInInfo) -> Self {
        var toRet = self(windowNibName:"PPPlugInInfoController")
        toRet.info = plugInfo

        return toRet
    }
}

答案 3 :(得分:1)

@ hamstergene的答案中的天才中风也是覆盖init(),它继承自NSResponder。现在可以引入一个新的初始化程序并委托给self.init(windowNibName: NoteWindowName),一旦所有三个指定的初始化程序被覆盖,它就会被继承:

class WindowController: NSWindowController {

    var note: Document! // must be optional because self is not available before delegating to designated init

    convenience init(note: Document) {
        self.init(windowNibName: NoteWindowName)
        self.document = document
    }

    override init(window: NSWindow?) {
        super.init(window: window)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override init() {
        fatalError("init() has not been implemented")
    }
}

现在不再需要告诉自定义窗口控制器要加载哪个nib文件。相反,它可以专门用于最初激励子类的任何东西(例如,参与某些文档层次结构)......

答案 4 :(得分:-1)

对hamstergene的更新回答。

这适用于Xcode Version 6.1(6A1052d)

Add your custom class to window controller

//
//  MainWindowController.swift
//  VHDA Editor
//
//  Created by Holyfield on 20/11/14.
//  Copyright (c) 2014 Holyfield. All rights reserved.
//

import Cocoa

class MainWindowController: NSWindowController {

    //override func windowDidLoad() {
    //    super.windowDidLoad()

        // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
   // }

    override init()
    {
        super.init()
        println(__FILE__, __FUNCTION__)
    }

    override init(window: NSWindow!)
    {
        super.init(window: window)
        println(__FILE__, __FUNCTION__)
    }

    required init?(coder: (NSCoder!))
    {
        super.init(coder: coder)
        println(__FILE__, __FUNCTION__)
    }

    override func windowDidLoad() {
        super.windowDidLoad()
        println(__FILE__, __FUNCTION__)
    }

}

控制台输出:

(…/MainWindowController.swift, init(coder:))
(…/MainWindowController.swift, windowDidLoad())