2011-12-28 68 views
31

NSCollectionView仍然是我見過的可可API的最神祕的部分之一。文檔很差,並且有許多移動部件,其中許多通常在Interface Builder中實施,使文檔變得具有挑戰性。如何從頭開始以編程方式創建NSCollectionView?

請提供示例代碼來創建最簡單的NSCollectionView的案例,它顯示文本字段或按鈕而不使用Xcode,其中每個文本字段或按鈕具有不同的標題。假定一個新的Xcode項目使用默認的window IBOutlet。

對於這個例子,沒有結合是必需的,以更新NSCollectionView作爲數據源的改變。只需顯示一個原型對象的網格,並將每個對象的標題設置爲某個值。

如果我們能夠爲很多人提供一個很好的例子,我認爲它會幫助所有與NSCollectionViews一起工作的人,並且和我一樣困惑。

的要求總結

  • 提供的示例代碼來呈現NSCollectionView在一個新的Xcode項目
  • 不要使用Interface Builder,確實使用IBOutlet中提供
  • NSCollectionView應該包含文本的默認窗口區域或按鈕,您的選擇
  • 視圖中的每一項都應該有不同的名稱
  • 沒有結合蛋白g是必需的

如果有滿足這些要求的示例代碼,請提供一個鏈接,那將很棒!

回答

59

我不知道有一個在編程和不綁定創建集合視圖很大的啓示,但在這裏不言而喻。

介紹

基本上有使用集合視圖時四個部分組成:

  • 查看:NSView一個子類,負責顯示信息;
  • 集合視圖本身;
  • 視圖控制器:的NSCollectionViewItem用作集合視圖項目的原型的子類;
  • 型號:對象的數組。

通常一個視圖是在Interface Builder中設計的,而一個模型是由Cocoa綁定調解的。

否則它編程:

常量
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 

視圖控制器(原型)

通常的視圖控制器加載從nib文件的視圖。在極少數情況下,視圖控制器無法從nib文件獲取其視圖,開發人員必須在視圖控制器收到-view之前發送它-setView:,或覆蓋-loadView。下面的代碼用於後者。

視圖控制器通過-setRepresentedObject:接收相應的模型對象。我已經重寫了它,以便在模型對象更改時更新按鈕標題。請注意,這可以通過使用Cocoa綁定來完成,而不需要任何代碼。

請注意,這些代碼都不是特定於集合視圖 - 它是通用視圖控制器行爲。

@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 

型號

字符串代表按鈕標題的簡單數組:

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

集合視圖

到目前爲止,這是成立的唯一關係是使用的視圖(BVView)由項目原型(BVPrototype)。收集視圖必須通知它應該使用的原型以及從中獲取數據的模型。

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

完整的源代碼的應用程序委託

#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

有關NSCollectionView的文檔是驚人的差。瞭解這個神話般的野獸是如何工作,並且可以分享他們的知識的人,正在幫助各地的Objective-C開發人員。非常感謝。 – Tronathan 2012-02-07 00:14:31

+0

Bavarious,我可以問你展示如何同步收藏視圖與可變數組項目? – brigadir 2012-05-25 09:10:36

+0

@Bavarious - 感謝您的一個很好的答案。 – 2012-11-12 19:03:45

4

要回答brigadir對如何綁定到一個可變數組的問題。

第零 - 使冠軍的NSMutableArray

第一 - 數組綁定到你的項目

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

二 - 改變標題的時候,一定要修改代理。

例如

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

@Bavarious 你在那裏做得很好。這只是一個令人驚歎的教程,我有時會在蘋果文檔中錯過。

我改寫Bavarious'代碼斯威夫特(V2)的人誰的興趣:

// 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。迅速:

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) 
    } 
}