cocoa swift initializer nswindowcontroller

cocoa - Subclase de NSWindowController en Swift e init(windowNibName)



initializer (5)

Estoy tratando de iniciar un nuevo proyecto de Cocoa basado en documentos en Swift y quiero crear una subclase de NSWindowController (como se recomienda en las guías de Apple sobre aplicaciones basadas en documentos). En ObjC haría una instancia de una subclase NSWindowController enviando el initWithWindowNibName: mensaje, que se implementó en consecuencia, llamando al método superclases.

En Swift init(windowNibName) solo está disponible como un inicializador conveniente, el inicializador designado de NSWindowController es init(window) que obviamente quiere que pase en una ventana.

No puedo llamar a super.init(windowNibName) de mi subclase, porque no es el inicializador designado, así que obviamente tengo que implementar la convenience init(windowNibName) , que a su vez necesita llamar a self.init(window) . Pero si todo lo que tengo es mi archivo de punta, ¿cómo puedo acceder a la ventana del archivo de punta para enviarlo a ese inicializador?


El golpe de genio en la respuesta de @ hamstergene es anular también init() , que se hereda de NSResponder . Ahora uno puede introducir un nuevo inicializador y delegar en self.init(windowNibName: NoteWindowName) , que a su vez se hereda una vez que se self.init(windowNibName: NoteWindowName) los tres inicializadores designados:

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") } }

Ahora ya no es necesario decirle al controlador de ventana personalizado de qué archivo de cabecera cargar. En cambio, puede ser especializado para lo que motivó la subclase en primer lugar (como participar en alguna jerarquía de documentos, por ejemplo) ...


En lugar de anular cualquiera de los métodos de inicio, simplemente puede anular la propiedad windowNibName y devolver una cadena codificada. Esto le permite llamar al método de arranque vain básico para crear el controlador de ventana.

class WindowController: NSWindowController { override var windowNibName: String! { return "NameOfNib" } } let windowController = WindowController()

Prefiero esto más que llamar let windowController = WindowController(windowNibName: "NameOfNib") ya que el nombre del plumín es un detalle de implementación que debe estar completamente encapsulado dentro de la clase de controlador de ventana y nunca expuesto afuera (y simplemente es más fácil llamar a WindowController() ).

Si desea agregar parámetros adicionales al método init, haga lo siguiente:

  • En su método de inicio personalizado, llame a super.init(window: nil) . Esto hará que NSWindowController con la propiedad windowNibName .
  • Reemplace el método init(coder: NSCoder) para configurar su objeto o simplemente llame a fatalError() para prohibir su uso (albiet en tiempo de ejecución).

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)


Pude solucionar esto simplemente teniendo un método de clase que llama al inicializador de conveniencia, modifica las variables personalizadas y luego devuelve el nuevo objeto.

Entonces un método de init Objective C sería así:

//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

Así es como hice la versión Swift:

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


Una actualización de la respuesta de hamstergene.

Esto funciona bien en Xcode versión 6.1 (6A1052d)

// // 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__) } }

Salida de la consola:

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


NSWindowController anular cualquiera de los tres inicializadores designados de NSWindowController ( init() , init(window) e init(coder) ), o ninguno de ellos, en cuyo caso su subclase heredará automáticamente init(windowNibName) y todos los demás inicializadores de conveniencia y usted podrá construirlo usando el inicializador de conveniencia de la superclase:

// 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")

Esto está cubierto por "Inicialización / Inicialización Automática del Inventario" en la guía de idioma:

Sin embargo, los inicializadores de superclase se heredan automáticamente si se cumplen ciertas condiciones. En la práctica, esto significa que no necesita escribir anulaciones de inicializador en muchos escenarios comunes, y puede heredar sus inicializadores de superclase con un mínimo esfuerzo siempre que sea seguro hacerlo.

Suponiendo que proporcione valores predeterminados para cualquier propiedad nueva que introduzca en una subclase, se aplican las dos reglas siguientes:

Regla 1 Si su subclase no define ningún inicializador designado, automáticamente hereda todos los inicializadores designados de su superclase.

Regla 2 Si su subclase proporciona una implementación de todos sus inicializadores designados de superclase, ya sea heredando según la regla 1 o proporcionando una implementación personalizada como parte de su definición, entonces hereda automáticamente todos los inicializadores de conveniencia de la superclase.