Subclase NSWindowController en Swift e init (windowNibName)
Estoy tratando de iniciar un nuevo proyecto 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 se haría una instancia de una subclase NSWindowController
enviando el mensaje initWithWindowNibName:
, que se implementó en consecuencia, llamando al método superclasses.
En Swift init(windowNibName)
solo está disponible como inicializador de conveniencia, el inicializador designado de NSWindowController
es init(window)
que obviamente quiere que pase un ventana.
No puedo llamar a super.init(windowNibName)
desde mi subclase, porque no es el inicializador designado, por lo que obviamente tengo que implementar convenience init(windowNibName)
, que a su vez necesita llamar self.init(window)
. Pero si todo lo que tengo es mi archivo nib, ¿cómo puedo acceder a la ventana del archivo nib para enviarlo a ese inicializador?
5 answers
Debe anular los tres inicializadores designados de NSWindowController
(init()
, init(window)
y init(coder)
), o ninguno de ellos, en cuyo caso tu subclase heredará automáticamente init(windowNibName)
y todos los demás inicializadores de conveniencia y podrás 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 / Herencia del Inicializador automático" en la guía del 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 sobreescrituras de inicializadores en muchos escenarios comunes, y puede heredar sus inicializadores de superclase con un esfuerzo mínimo siempre que sea seguro hacerlo.
Suponiendo que proporcione valores predeterminados para cualquier propiedad nueva que introduzca en una subclase, se aplican las siguientes dos reglas:
Artículo 1 Si su subclase no define ningún inicializador designado, hereda automáticamente todos sus inicializadores designados superclase.
Artículo 2 Si su subclase proporciona una implementación de todos sus inicializadores designados como superclase, ya sea heredándolos 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 superclase.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-14 14:36:51
En lugar de sobrescribir cualquiera de los métodos init, simplemente puede sobrescribir la propiedad windowNibName
y devolver una cadena codificada. Esto le permite llamar al método básico vanilla init para crear el controlador de ventana.
class WindowController: NSWindowController {
override var windowNibName: String! {
return "NameOfNib"
}
}
let windowController = WindowController()
Prefiero esto a llamar a let windowController = WindowController(windowNibName: "NameOfNib")
ya que el nombre del nib es un detalle de implementación que debe estar completamente encapsulado dentro de la clase window controller y nunca expuesto fuera (y es simplemente más fácil llamar a WindowController()
).
Si desea agregar los parámetros adicionales al método init hacen lo siguiente:
- En su método init personalizado llame a
super.init(window: nil)
. Esto hará queNSWindowController
se inicie con la propiedadwindowNibName
. - Invalida el método requerido
init(coder: NSCoder)
para configurar tu objeto o simplemente llamar afatalError()
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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-18 21:48:02
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.
Así que un método de Objective C init
se verí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
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-11 22:11:50
El golpe de genio en la respuesta de @hamstergene es anular init()
también, que se hereda de NSResponder
. Ahora se puede introducir un nuevo inicializador y delegar en self.init(windowNibName: NoteWindowName)
, que a su vez se hereda una vez que los tres inicializadores designados se anulan:
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 ventanas personalizado desde qué archivo nib cargar. En su lugar, se puede especializar para lo que motivó a la subclase en primer lugar (como participar en algunos jerarquía de documentos, por ejemplo)...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-04 16:18:33
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 consola:
(…/MainWindowController.swift, init(coder:))
(…/MainWindowController.swift, windowDidLoad())
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-20 14:57:17