objective-c - objective - xcode manual pdf
cargar el objeto mediante programaciĆ³n desde la subclase NSView desde la punta (5)
Tenga en cuenta que un archivo nib contiene:
- Uno o más objetos de nivel superior. Por ejemplo, una instancia de
MyView
; - El marcador de posición del propietario de un archivo. Este es un objeto que existe antes de que se cargue el archivo nib. Dado que existe antes de que se cargue el archivo nib, no puede ser igual a la vista en el archivo nib.
Escenario 1: NSNib
Consideremos que su archivo de punta tiene solo un objeto de nivel superior cuya clase es MyView
, y la clase de propietario del archivo es MyAppDelegate
. En ese caso, considerando que el archivo nib se carga en un método de instancia de MyAppDelegate
:
NSNib *nib = [[[NSNib alloc] initWithNibNamed:@"MyView" bundle:nil] autorelease];
NSArray *topLevelObjects;
if (! [nib instantiateWithOwner:self topLevelObjects:&topLevelObjects]) // error
MyView *myView = nil;
for (id topLevelObject in topLevelObjects) {
if ([topLevelObject isKindOfClass:[MyView class]) {
myView = topLevelObject;
break;
}
}
// At this point myView is either nil or points to an instance
// of MyView that is owned by this code
Parece que el primer argumento de -[NSNib instantiateNibWithOwner:topLevelObjects:]
puede ser nil
por lo que no tendría que especificar un propietario, ya que parece que no le interesa tener uno:
if (! [nib instantiateWithOwner:nil topLevelObjects:&topLevelObjects]) // error
Aunque esto funciona, no confiaría en él ya que no está documentado.
Tenga en cuenta que tiene propiedad de los objetos de nivel superior, por lo tanto, usted es responsable de liberarlos cuando ya no sean necesarios.
Escenario 2: NSViewController
Cocoa proporciona NSViewController
para administrar vistas; usualmente, vistas cargadas desde un archivo de punta. Debe crear una subclase de NSViewController
contenga los puntos de venta necesarios. Cuando edite el archivo de punta, configure la salida de la view
y cualquier otra salida que haya definido. También debe establecer el propietario del archivo nib para que su clase sea esta subclase de NSViewController
. Entonces:
MyViewController *myViewController = [[MyViewController alloc] initWithNibName:@"MyView" bundle:nil];
NSViewController
carga automáticamente el archivo nib y establece las salidas correspondientes cuando lo envía -view
. De forma alternativa, puede forzarlo a que cargue el archivo nib enviándolo -loadView
.
¿Cómo cargo correctamente un objeto que es una subclase de NSView usando un Xib?
Quiero que se cargue dinámicamente no desde el principio, así que hice un MyView.Xib de MyDocument.m Lo hice:
MyView *myView = [[MyView alloc] initWithFrame:...];
[NSBundle loadNibNamed:@"MyView" owner:myView];
[someview addSubview:myView];
...
y el fondo está bien (llama a drawrect: y se dibuja como se esperaba), pero no aparecerán todos los botones que puse en el xib. Lo he comprobado, y están cargados PERO su supervista no es el mismo objeto que myView
.
¿Por qué es esto? Creo que me falta algo en el Xib, pero no sé exactamente qué. En otras palabras: ¿cómo me aseguro de que la vista raíz en mi xib sea el mismo objeto que el propietario del archivo?
Ojalá hubiera algo similar a esto para el mac:
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil]; //in iOS this will load the xib
MyView* myView = [ nibViews objectAtIndex:1];
[someview addSubview:myView];
...
Gracias por adelantado.
EDITAR
Creo que me he dado cuenta del origen del problema ... (??)
En la clase MyView tengo varios IBOutlet que están conectados correctamente en IB por eso se cargan bien (puedo recomendarlos). Sin embargo, no hay IBOutlet para la vista superior. Entonces, cuando NSBundle carga el plumín, la vista superior se asigna a algún otro objeto. Pensé que esto pasaría si configuraba mi vista superior en IB como perteneciente a la clase: MyView
y ponía a myView
como propietario en [NSBundle loadNibNamed:@"MyView" owner:myView];
pero parece que no es el caso. ¿Qué estoy haciendo mal?
Puedes referirte a esta categoría
https://github.com/peterpaulis/NSView-NibLoading-/tree/master
+ (NSView *)loadWithNibNamed:(NSString *)nibNamed owner:(id)owner class:(Class)loadClass {
NSNib * nib = [[NSNib alloc] initWithNibNamed:nibNamed bundle:nil];
NSArray * objects;
if (![nib instantiateWithOwner:owner topLevelObjects:&objects]) {
NSLog(@"Couldn''t load nib named %@", nibNamed);
return nil;
}
for (id object in objects) {
if ([object isKindOfClass:loadClass]) {
return object;
}
}
return nil;
}
Encontré la respuesta de @Bavarious muy útil, pero aún necesitaba algunas ardides por mi parte para que funcionara para mi caso de uso: cargue una de las varias definiciones de vistas de xib en una vista existente de "contenedor". Aquí está mi flujo de trabajo:
1) En .xib, cree la vista en IB como se esperaba.
2) NO agregue un objeto para viewController de la nueva vista, sino
3) Establecer el propietario del archivo .xib en viewController de la nueva vista
4) Conecte cualquier punto de venta y acciones al propietario del archivo y, específicamente, a .view
5) Llamar a loadInspector (ver a continuación).
enum InspectorType {
case None, ItemEditor, HTMLEditor
var vc: NSViewController? {
switch self {
case .ItemEditor:
return ItemEditorViewController(nibName: "ItemEditor", bundle: nil)
case .HTMLEditor:
return HTMLEditorViewController(nibName: "HTMLEditor", bundle: nil)
case .None:
return nil
}
}
}
class InspectorContainerView: NSView {
func loadInspector(inspector: InspectorType) -> NSViewController? {
self.subviews = [] // Get rid of existing subviews.
if let vc = inspector.vc {
vc.loadView()
self.addSubview(vc.view)
return vc
}
Swift.print("VC NOT loaded from /(inspector)")
return nil
}
}
Aquí hay una forma de escribir la subclase NSView para que la vista en sí provenga completamente de un xib separado y tenga todo configurado correctamente. Se basa en el hecho de que init
puede cambiar el valor de self
.
Crea una nueva ''vista'' xib usando Xcode. Establezca la clase del propietario del archivo en NSViewController y configure su salida de view
en su vista de destino. Establezca la clase de la vista de destino en su subclase NSView. Diseña tu vista, conecta puntos de venta, etc.
Ahora, en su subclase NSView, implemente los inicializadores designados:
- (id)initWithFrame:(NSRect)frameRect
{
NSViewController *viewController = [[NSViewController alloc] init];
[[NSBundle mainBundle] loadNibNamed:@"TheNib" owner:viewController topLevelObjects:NULL];
id viewFromXib = viewController.view;
viewFromXib.frame = frameRect;
self = viewFromXib;
return self;
}
Y lo mismo con initWithCoder:
para que también funcione cuando use su subclase NSView en otro xib.
class CustomView: NSView {
@IBOutlet weak var view: NSView!
@IBOutlet weak var textField: NSTextField!
required init(coder: NSCoder) {
super.init(coder: coder)!
let frameworkBundle = Bundle(for: classForCoder)
assert(frameworkBundle.loadNibNamed("CustomView", owner: self, topLevelObjects: nil))
addSubview(view)
}
}