2011-12-28 15 views
31

NSCollectionView pozostaje jedną z najbardziej tajemniczych części API kakao, jaką kiedykolwiek widziałem. Dokumentacja jest słaba i istnieje wiele ruchomych części, z których wiele jest często implementowanych w programie Interface Builder, co utrudnia dokumentację.Jak programowo utworzyć NSCollectionView od podstaw?

Proszę podać przykładowy kod, aby utworzyć najprostszy przypadek NSCollectionView, który wyświetla pola tekstowe lub przyciski bez użycia Xcode, gdzie każde pole tekstowe lub przycisk ma inny tytuł. Załóżmy nowy projekt Xcode z domyślnym IBOutlet window.

W tym przykładzie nie jest wymagane wiązanie, aby zaktualizować NSCollectionView w miarę zmiany źródła danych. Po prostu wyświetl siatkę prototypowych obiektów i ustaw wartość każdego z nich.

Jeśli uda nam się uzyskać dobry przykład tego, jak to zrobić dla wielu osób, myślę, że pomoże to wszystkim, którzy pracują z NSCollectionViews i jest tak zaskoczony, jak ja.

Podsumowanie życzenie

  • podać przykładowy kod do renderowania NSCollectionView w nowym projekcie Xcode
  • Nie używać interfejsu Builder, czy użyć okna domyślny IBOutlet przewidzianego
  • NSCollectionView powinien zawierać tekst Pola lub przyciski, do wyboru
  • Każda pozycja w widoku powinna mieć inny tytuł
  • Bez bindin g jest wymagane

Jeśli istnieje przykładowy kod, który spełnia te wymagania, proszę podać link, który będzie świetny!

Odpowiedz

59

Nie jestem pewien, czy jest dużo wglądu w tworzenie widoku kolekcji programowo i bez powiązań, ale tutaj jest.

Wprowadzenie

Istnieją zasadniczo cztery składniki przy użyciu widoku kolekcji:

  • Widok: podklasę NSView, odpowiedzialnego za wyświetlanie informacji;
  • Sam widok kolekcji;
  • Kontroler widoku: podklasa o numerze NSCollectionViewItem, która służy jako prototyp pozycji widoku kolekcji;
  • Model: tablica obiektów.

Zwykle widok jest projektowany w Konstruktorze interfejsów, a model jest za pośrednictwem wiązań kakao.

Robi to programowo:

Stałe

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

Zobacz

Jest to standardowy widok (widok niestandardowy interfejs Builder w żargonie) zawierający przycisk. Zwróć uwagę, że widok ma ustalony rozmiar.

@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 

View Controller (prototyp)

Normalnie kontroler widoku ładuje swój pogląd z pliku nib. W rzadkich przypadkach, gdy kontroler widoku nie uzyskuje widoku z pliku nib, programista musi albo wysłać go pod numer -setView:, zanim -view zostanie odebrany przez kontroler widoku, albo zastąpić -loadView. Poniższy kod wykonuje to drugie.

Wyświetl kontrolery otrzymujące odpowiedni obiekt modelu poprzez -setRepresentedObject:. Zastąpiłem go, aby zaktualizować tytuł przycisku za każdym razem, gdy zmienia się obiekt modelu. Zauważ, że można to osiągnąć za pomocą wiązania kakao bez żadnego kodu.

Należy zauważyć, że żaden z tych kodów nie jest specyficzny dla widoków kolekcji - jest to ogólne zachowanie kontrolera widoku.

@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 

model

prostą tablicę ciągów reprezentujących tytuły przycisk:

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

Collection Zobacz

Dotychczas tylko relacja, która została założona jest pogląd (BVView) używane przez prototyp przedmiotu (BVPrototype). Widok kolekcji musi być poinformowany o prototypie, który powinien zostać użyty, jak również modelu, z którego należy uzyskać dane.

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

Pełny kod źródłowy aplikacji Delegata

#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

Dokumentacja na NSCollectionView jest zaskakująco słaba. Każdy, kto rozumie, w jaki sposób ta mityczna bestia działa i może dzielić się swoją wiedzą, pomaga deweloperom Objective-C wszędzie. Dzięki wielkie. – Tronathan

+0

Bawarii, czy mogę prosić o pokazanie, jak zsynchronizować widok kolekcji z zmienną tablicą elementów? – brigadir

+0

@Barious - dzięki za wspaniałą odpowiedź. –

4

Aby odpowiedzieć na pytanie Brigadir na temat tego jak wiążą się z zmienny tablicy.

zero'th - make tytuły NSMutableArray

- najpierw związać tablicę do pozycji

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

drugie - gdy zmiany tytułów, upewnij się, aby zmodyfikować proxy.

np.

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

@Bavarious Twoją robotę tam. To był niesamowity samouczek, którego czasami brakuje mi w Apple Docs.

przepisałem kod Bavarious' w Swift (V2) dla każdego, kto jest zainteresowany:

// 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.szybki:

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) 
    } 
} 
Powiązane problemy