2011-12-28 13 views
31

NSCollectionView sigue siendo una de las partes más misteriosas de la API Cocoa que he visto. La documentación es deficiente y hay muchas partes móviles, muchas de las cuales a menudo se implementan en Interface Builder, lo que hace que la documentación sea un desafío.¿Cómo crear NSCollectionView programáticamente desde cero?

Proporcione el código de muestra para crear el caso más simple de NSCollectionView que muestra campos de texto o botones sin usar Xcode donde cada campo o botón de texto tiene un título diferente. Suponga un nuevo proyecto de Xcode con el valor predeterminado window IBOutlet.

Para este ejemplo, no se requiere ningún enlace para actualizar NSCollectionView a medida que cambia la fuente de datos. Simplemente visualice una cuadrícula de objetos prototipo y establezca el Título de cada objeto con algún valor.

Si podemos tener un buen ejemplo de cómo hacer esto disponible para muchas personas, creo que ayudará a todos los que trabajan con NSCollectionViews y está tan desconcertado como yo.

resumen de la solicitud

  • Proporcionar código de ejemplo para rendir NSCollectionView en un nuevo proyecto Xcode
  • No utilice Interface Builder, hacer uso de la ventana por defecto IBOutlet proporciona
  • NSCollectionView debe contener texto Campos o botones, su elección
  • Cada elemento de la vista debe tener un título diferente
  • Sin vínculo g es obligatorio

Si hay un código de muestra que cumpla con estos requisitos, proporcione un enlace, ¡sería genial!

Respuesta

59

No estoy seguro de que haya mucha información para crear una vista de colección mediante programación y sin enlaces, pero aquí va.

Introducción

Hay esencialmente cuatro componentes cuando se utiliza una vista de colección:

  • Vista: una subclase de NSView, responsable de mostrar la información;
  • La vista de la colección en sí;
  • Controlador de vista: una subclase de NSCollectionViewItem que sirve como prototipo del elemento de vista de colección;
  • Modelo: una matriz de objetos.

Por lo general, una vista está diseñada en Interface Builder, y un modelo está mediado por enlaces Cocoa.

Hacerlo mediante programación:

Constantes

static const NSSize buttonSize = {80, 20}; 
static const NSSize itemSize = {100, 40}; 
static const NSPoint buttonOrigin = {10, 10}; 

Ver

Ésta es una vista estándar (una vista personalizada en el Interface Builder jerga) que contiene un botón. Tenga en cuenta que la vista tiene un tamaño fijo.

@interface BVView : NSView 
@property (weak) NSButton *button; 
@end 

@implementation BVView 
@synthesize button; 
- (id)initWithFrame:(NSRect)frameRect { 
    self = [super initWithFrame:(NSRect){frameRect.origin, itemSize}]; 
    if (self) { 
     NSButton *newButton = [[NSButton alloc] 
      initWithFrame:(NSRect){buttonOrigin, buttonSize}]; 
     [self addSubview:newButton]; 
     self.button = newButton; 
    } 
    return self; 
} 
@end 

Vista Controlador (Prototipo)

Normalmente, un controlador de vista carga su vista desde un archivo semilla. En los casos excepcionales en que el controlador de vista no obtiene su vista desde un archivo de punta, el desarrollador debe enviarlo -setView: antes de que -view sea recibido por el controlador de vista, o anular -loadView. El siguiente código hace esto último.

Los controladores de vista reciben el objeto modelo correspondiente a través de -setRepresentedObject:. Lo he reemplazado para actualizar el título del botón cada vez que cambia el objeto del modelo. Tenga en cuenta que esto se puede lograr mediante el uso de enlaces Cocoa sin ningún código en absoluto.

Tenga en cuenta que ninguno de este código es específico para las vistas de colección: es el comportamiento del controlador de vista general.

@interface BVPrototype : NSCollectionViewItem 
@end 

@implementation BVPrototype 
- (void)loadView { 
    [self setView:[[BVView alloc] initWithFrame:NSZeroRect]]; 
} 
- (void)setRepresentedObject:(id)representedObject { 
    [super setRepresentedObject:representedObject]; 
    [[(BVView *)[self view] button] setTitle:representedObject]; 
} 
@end 

Modelo

Una simple matriz de cadenas que representan los títulos de botón:

@property (strong) NSArray *titles; 
self.titles = [NSArray arrayWithObjects:@"Case", @"Molly", @"Armitage", 
    @"Hideo", @"The Finn", @"Maelcum", @"Wintermute", @"Neuromancer", nil]; 

linterna Vista

Hasta ahora, la única relación que se ha establecido es la vista (BVView) utilizado por el prototipo de artículo (BVPrototype). La vista de la colección debe ser informada del prototipo que debería estar utilizando así como del modelo del que se obtienen los datos.

NSCollectionView *cv = [[NSCollectionView alloc] 
    initWithFrame:[[[self window] contentView] frame]]; 
[cv setItemPrototype:[BVPrototype new]]; 
[cv setContent:[self titles]]; 

código fuente completo para el Delegado de aplicación

#import "BVAppDelegate.h" 


static const NSSize buttonSize = { 80, 20 }; 
static const NSSize itemSize = { 100, 40 }; 
static const NSPoint buttonOrigin = { 10, 10 }; 


@interface BVView : NSView 
@property (weak) NSButton *button; 
@end 

@implementation BVView 
@synthesize button; 
- (id)initWithFrame:(NSRect)frameRect { 
    self = [super initWithFrame:(NSRect){frameRect.origin, itemSize}]; 
    if (self) { 
     NSButton *newButton = [[NSButton alloc] 
      initWithFrame:(NSRect){buttonOrigin, buttonSize}]; 
     [self addSubview:newButton]; 
     self.button = newButton; 
    } 
    return self; 
} 
@end 


@interface BVPrototype : NSCollectionViewItem 
@end 

@implementation BVPrototype 
- (void)loadView { 
    [self setView:[[BVView alloc] initWithFrame:NSZeroRect]]; 
} 
- (void)setRepresentedObject:(id)representedObject { 
    [super setRepresentedObject:representedObject]; 
    [[(BVView *)[self view] button] setTitle:representedObject]; 
} 
@end 


@interface BVAppDelegate() 
@property (strong) NSArray *titles; 
@end 

@implementation BVAppDelegate 

@synthesize window = _window; 
@synthesize titles; 

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 
    self.titles = [NSArray arrayWithObjects:@"Case", @"Molly", @"Armitage", 
     @"Hideo", @"The Finn", @"Maelcum", @"Wintermute", @"Neuromancer", nil]; 

    NSCollectionView *cv = [[NSCollectionView alloc] 
     initWithFrame:[[[self window] contentView] frame]]; 
    [cv setItemPrototype:[BVPrototype new]]; 
    [cv setContent:[self titles]]; 

    [cv setAutoresizingMask:(NSViewMinXMargin 
          | NSViewWidthSizable 
          | NSViewMaxXMargin 
          | NSViewMinYMargin 
          | NSViewHeightSizable 
          | NSViewMaxYMargin)]; 
    [[[self window] contentView] addSubview:cv]; 
} 

@end 
+15

La documentación sobre NSCollectionView es sorprendentemente mala. Cualquiera que entienda cómo funciona esta bestia mítica y pueda compartir sus conocimientos está ayudando a los desarrolladores de Objective-C en todas partes. Muchas gracias. – Tronathan

+0

Bavarious, ¿puedo pedirle que muestre cómo sincronizar la vista de colección con una matriz mutable de elementos? – brigadir

+0

@Bavarious - gracias por una gran respuesta. –

4

Para responder a la pregunta de Brigadir sobre cómo unirse a una matriz mutable.

zero'th - realizar una primera títulos NSMutableArray

- enlazar la matriz a sus artículos

[cv bind:NSContentBinding 
    toObject:self 
    withKeyPath:@"titles" 
    options:NULL]; 

En segundo lugar - cuando se altera títulos, asegúrese de modificar el proxy.

p. Ej.

NSMutableArray *kvcTitles = [self mutableArrayValueForKey:@"titles"]; 
[kvcTitles removeLastObject]; 
5

@Bavarious Se hizo un excelente trabajo allí. Este fue solo un tutorial increíble que a veces echo de menos en los Apple Docs.

Reescribí código Bavarious' en Swift (v2) para cualquiera que esté interesado:

// AppDelegate.swift:

import Cocoa 

let buttonSize:NSSize = NSSize(width: 80, height: 20) 
let itemSize:NSSize = NSSize(width: 100, height: 40) 
let buttonOrigin:NSPoint = NSPoint(x: 10, y: 10) 

let titles:[String] = ["Case", "Molly", "Armitage", "Hideo", "The Finn", "Maelcum", "Wintermute", "Neuromancer"] 

@NSApplicationMain 
class AppDelegate: NSObject, NSApplicationDelegate { 

    @IBOutlet weak var window: NSWindow! 

    func applicationDidFinishLaunching(aNotification: NSNotification) { 
     let cv = NSCollectionView(frame: self.window.contentView!.frame) 
     cv.itemPrototype = BVTemplate() 
     cv.content = titles 

     cv.autoresizingMask = NSAutoresizingMaskOptions.ViewMinXMargin 
      .union(NSAutoresizingMaskOptions.ViewWidthSizable) 
      .union(NSAutoresizingMaskOptions.ViewMaxXMargin) 
      .union(NSAutoresizingMaskOptions.ViewMinYMargin) 
      .union(NSAutoresizingMaskOptions.ViewMaxYMargin) 
      .union(NSAutoresizingMaskOptions.ViewHeightSizable) 

     window.contentView!.addSubview(cv) 
    } 

    func applicationWillTerminate(aNotification: NSNotification) { 
     // Insert code here to tear down your application 
    } 
} 

// BVTemplate.swift:

import Cocoa 

class BVTemplate: NSCollectionViewItem { 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     // Do view setup here. 
    } 

    override func loadView() { 
     print("loadingView") 
     self.view = BVView(frame: NSZeroRect) 
    } 

    override var representedObject:AnyObject? { 
     didSet { 
      if let representedString = representedObject as? String { 
       (self.view as! BVView).button?.title = representedString 
      } 
     } 
    } 
} 

// BVView.swift:

import Cocoa 

class BVView: NSView { 

    var button:NSButton? 

    override init(frame frameRect: NSRect) { 
     super.init(frame: NSRect(origin: frameRect.origin, size: itemSize)) 
     let newButton:NSButton = NSButton(frame: NSRect(origin: buttonOrigin, size: buttonSize)) 
     self.addSubview(newButton) 
     self.button = newButton 
    } 

    required init?(coder: NSCoder) { 
     super.init(coder: coder) 
    } 
} 
Cuestiones relacionadas