Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
747 views
in Technique[技术] by (71.8m points)

xcode - How to create NSCollectionView programmatically from scratch?

NSCollectionView remains one of the most mysterious parts of the Cocoa API that I've ever seen. Documentation is poor and there are many moving parts, many of which are often implemented in Interface Builder, making documentation challenging.

Please provide sample code to create the simplest case of NSCollectionView which displays either Text Fields or Buttons without using Xcode where each Text Field or Button has a different Title. Assume a new Xcode project with the default window IBOutlet.

For this example, no binding is required to update the NSCollectionView as the data source changes. Simply display a grid of prototype objects and set each object's Title to some value.

If we can get a good example of how to do this available to many people, I think it will help everyone who works with NSCollectionViews and is as baffled as I am.

Summary of request

  • Provide sample code to render an NSCollectionView in a new Xcode project
  • Do not use Interface Builder, do use the default window IBOutlet provided
  • NSCollectionView should contain Text Fields or Buttons, your choice
  • Each item in the view should have a different Title
  • No binding is required

If there's sample code out there that meets these requirements, please provide a link, that'd be great!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I’m not sure there’s much insight in creating a collection view programmatically and without bindings, but here it goes.

Introduction

There are essentially four components when using a collection view:

  • View: a subclass of NSView, responsible for displaying information;
  • The collection view itself;
  • View controller: a subclass of NSCollectionViewItem that serves as the collection view item prototype;
  • Model: an array of objects.

Usually a view is designed in Interface Builder, and a model is mediated by Cocoa bindings.

Doing it programmatically:

Constants

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

View

This is a standard view (a custom view in Interface Builder parlance) containing a button. Note that the view has fixed size.

@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 (Prototype)

Normally a view controller loads its view from a nib file. In the rare cases where the view controller doesn’t obtain its view from a nib file, the developer must either send it -setView: before -view is received by the view controller, or override -loadView. The following code does the latter.

View controllers receive the corresponding model object via -setRepresentedObject:. I’ve overridden it so as to update the button title whenever the model object changes. Note that this can be accomplished by using Cocoa bindings without any code at all.

Note that none of this code is specific to collection views — it’s general view controller behaviour.

@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

A simple array of strings representing button titles:

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

Collection View

So far, the only relation that’s been established is the view (BVView) used by the item prototype (BVPrototype). The collection view must be informed of the prototype it should be using as well as the model from which to obtain data.

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

Full Source Code for the Application Delegate

#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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...