xcode - ¿Cómo crear NSCollectionView programáticamente desde cero?
cocoa (3)
@Bavarious Hiciste un excelente trabajo allí. Este fue solo un tutorial increíble que a veces echo de menos en los Apple Docs.
Reescribí el código de 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)
}
}
NSCollectionView
sigue siendo una de las partes más misteriosas de Cocoa API 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.
Proporcione un código de muestra para crear el caso más simple de NSCollectionView
que muestre Campos de texto o Botones sin usar Xcode, donde cada Campo de texto o Botón tiene un Título diferente. Asuma un nuevo proyecto Xcode con la window
predeterminada 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 obtener 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
- Proporcione código de muestra para representar NSCollectionView en un nuevo proyecto de Xcode
- No use Interface Builder, use la ventana predeterminada IBOutlet proporcionada
- NSCollectionView debe contener campos de texto o botones, su elección
- Cada elemento en la vista debe tener un título diferente
- No se requiere enlace
Si hay un código de muestra que cumple estos requisitos, proporcione un enlace, ¡sería genial!
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 usa una vista de colección:
- Vista: una subclase de
NSView
, responsable de mostrar la información; - La vista de la colección en sí misma;
- Controlador de vista: una subclase de
NSCollectionViewItem
que sirve como prototipo de 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.
Haciéndolo programáticamente:
Constantes
static const NSSize buttonSize = {80, 20};
static const NSSize itemSize = {100, 40};
static const NSPoint buttonOrigin = {10, 10};
Ver
Esta es una vista estándar (una vista personalizada en el lenguaje de Interface Builder) 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
Ver controlador (Prototipo)
Normalmente, un controlador de vista carga su vista desde un archivo de punta. En los casos excepcionales en que el controlador de vista no obtiene su vista desde un archivo de punta, el desarrollador debe enviarlo -setView:
before -view
es recibido por el controlador de vista o sobrescribir -loadView
. El siguiente código hace esto último.
Los controladores de vista reciben el objeto de 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 un comportamiento de 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 serie de cadenas que representan títulos de botones:
@property (strong) NSArray *titles;
self.titles = [NSArray arrayWithObjects:@"Case", @"Molly", @"Armitage",
@"Hideo", @"The Finn", @"Maelcum", @"Wintermute", @"Neuromancer", nil];
Vista de colección
Hasta ahora, la única relación que se ha establecido es la vista ( BVView
) utilizada por el prototipo del 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 la 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
Para responder la pregunta de brigadir sobre cómo enlazar a una matriz mutable.
zero''th - make titles un NSMutableArray
primero, vincula la matriz a tus elementos
[cv bind:NSContentBinding
toObject:self
withKeyPath:@"titles"
options:NULL];
En segundo lugar, al modificar los títulos, asegúrese de modificar el proxy.
p.ej
NSMutableArray *kvcTitles = [self mutableArrayValueForKey:@"titles"];
[kvcTitles removeLastObject];