33

NSCollectionView私が今まで見た Cocoa API の中で最も不可解な部分の 1 つです。ドキュメンテーションは貧弱で、多くの可動部分があり、その多くは Interface Builder で実装されることが多いため、ドキュメンテーションが困難になります。

NSCollectionViewテキスト フィールドまたはボタンごとに異なるタイトルを持つ Xcode を使用せずに、テキスト フィールドまたはボタンのいずれかを表示する最も単純なケースを作成するためのサンプル コードを提供してください。windowデフォルトのIBOutletを持つ新しい Xcode プロジェクトを想定します。

この例では、データ ソースが変更されたときに NSCollectionView を更新するためのバインディングは必要ありません。プロトタイプ オブジェクトのグリッドを表示し、各オブジェクトのタイトルを何らかの値に設定するだけです。

これを行う方法の良い例を多くの人が利用できるようにすることができれば、私とNSCollectionViews同じように困惑しているすべての人を助けることができると思います.

リクエストの概要

  • 新しい Xcode プロジェクトで NSCollectionView をレンダリングするサンプル コードを提供する
  • Interface Builder を使用しないでください。提供されているデフォルトのウィンドウ IBOutlet を使用してください。
  • NSCollectionView には、選択したテキスト フィールドまたはボタンが含まれている必要があります
  • ビュー内の各アイテムには異なるタイトルが必要です
  • 縛り不要

これらの要件を満たすサンプル コードがある場合は、リンクを提供してください。

4

3 に答える 3

61

コレクション ビューをプログラムでバインドせずに作成することについて、あまり洞察があるかどうかはわかりませんが、ここでは説明します。

序章

コレクション ビューを使用する場合、基本的に 4 つのコンポーネントがあります。

  • View: のサブクラスでNSView、情報の表示を担当します。
  • コレクション ビュー自体。
  • NSCollectionViewItemビュー コントローラー:コレクション ビュー アイテムのプロトタイプとして機能する のサブクラス。
  • モデル: オブジェクトの配列。

通常、ビューは Interface Builder で設計され、モデルは Cocoa バインディングによって仲介されます。

プログラムでそれを行う:

定数

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

意見

これは、ボタンを含む標準ビュー (Interface Builder 用語でのカスタム ビュー) です。ビューのサイズは固定されていることに注意してください。

@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 はそのビューを nib ファイルからロードします。ビュー コントローラが nib ファイルからビューを取得しないまれなケースでは、開発者はビュー コントローラが を受信する-setView:前にそれを送信するか-view、オーバーライドする必要があります-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
于 2012-02-05T23:22:01.743 に答える
7

@Bavarous あなたはそこで素晴らしい仕事をしました。これは、私が時々 Apple Docs で見逃してしまう素晴らしいチュートリアルでした。

興味のある人のために、Swift (v2) で Bavarious のコードを書き直しました。

// 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)
    }
}
于 2015-12-21T22:02:38.670 に答える
4

変更可能な配列にバインドする方法に関するブリガディールの質問に答えるには。

zero'th - タイトルをNSMutableArray

最初 - 配列を項目にバインドします

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

2 つ目 - タイトルを変更するときは、必ずプロキシを変更してください。

例えば

NSMutableArray *kvcTitles = [self mutableArrayValueForKey:@"titles"];
[kvcTitles removeLastObject];
于 2012-11-12T19:07:54.883 に答える