33

NSCollectionView仍然是我见过的 Cocoa API 中最神秘的部分之一。文档很差,而且有很多活动部分,其中许多通常在 Interface Builder 中实现,这使得文档变得具有挑战性。

请提供示例代码来创建最简单的情况,NSCollectionView即在不使用 Xcode 的情况下显示文本字段或按钮,其中每个文本字段或按钮都有不同的标题。假设一个带有默认windowIBOutlet 的新 Xcode 项目。

对于此示例,无需绑定即可在数据源更改时更新 NSCollectionView。只需显示一个原型对象网格并将每个对象的标题设置为某个值。

如果我们可以为许多人提供一个如何做到这一点的好例子,我认为这将帮助每个与我一起工作NSCollectionViews并且像我一样困惑的人。

请求摘要

  • 提供示例代码以在新的 Xcode 项目中呈现 NSCollectionView
  • 不要使用 Interface Builder,请使用提供的默认窗口 IBOutlet
  • NSCollectionView 应该包含文本字段或按钮,您可以选择
  • 视图中的每个项目都应该有不同的标题
  • 无需绑定

如果有满足这些要求的示例代码,请提供一个链接,那就太好了!

4

3 回答 3

61

我不确定在没有绑定的情况下以编程方式创建集合视图是否有很多见解,但它就在这里。

介绍

使用集合视图时基本上有四个组件:

  • 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

视图控制器(原型)

通常,视图控制器从 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

@Bavarious 你在那里做得很好。这只是一个很棒的教程,我有时会在 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

回答 brigadir 关于如何绑定到可变数组的问题。

zero'th - 使标题成为NSMutableArray

首先 - 将数组绑定到您的项目

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

第二 - 更改标题时,请务必修改代理。

例如

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