xcode 如何从头开始以编程方式创建 NSCollectionView?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8660626/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create NSCollectionView programmatically from scratch?
提问by Tronathan
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.
NSCollectionView
仍然是我见过的 Cocoa API 中最神秘的部分之一。文档很差,并且有许多活动部分,其中许多经常在 Interface Builder 中实现,这使得文档具有挑战性。
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.
请提供示例代码以创建最简单的情况,NSCollectionView
即在不使用 Xcode 的情况下显示文本字段或按钮,其中每个文本字段或按钮具有不同的标题。假设有一个带有默认window
IBOutlet的新 Xcode 项目。
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.
对于这个例子,当数据源发生变化时,不需要绑定来更新 NSCollectionView。只需显示原型对象的网格并将每个对象的标题设置为某个值。
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.
如果我们能得到一个很好的例子来说明如何为许多人提供帮助,我认为这将有助于每个NSCollectionViews
和我一起工作并且和我一样困惑的人。
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
- 提供示例代码以在新的 Xcode 项目中呈现 NSCollectionView
- 不要使用Interface Builder,使用提供的默认窗口IBOutlet
- NSCollectionView 应包含文本字段或按钮,您的选择
- 视图中的每个项目都应该有不同的标题
- 不需要绑定
If there's sample code out there that meets these requirements, please provide a link, that'd be great!
如果有满足这些要求的示例代码,请提供链接,那就太好了!
回答by
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.
- View: 的子类
NSView
,负责显示信息; - 集合视图本身;
- 视图控制器:
NSCollectionViewItem
作为集合视图项原型的子类; - 模型:对象数组。
Usually a view is designed in Interface Builder, and a model is mediated by Cocoa bindings.
通常在 Interface Builder 中设计一个视图,一个模型由 Cocoa 绑定介导。
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 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 (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.
通常,视图控制器从 nib 文件加载其视图。在视图控制器未从 nib 文件中获取其视图的极少数情况下,开发人员必须在视图控制器接收到它-setView:
之前发送它-view
,或者覆盖-loadView
. 以下代码执行后者。
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.
视图控制器通过-setRepresentedObject:
. 我已经覆盖它以便在模型对象发生变化时更新按钮标题。请注意,这可以通过使用 Cocoa 绑定来完成,根本不需要任何代码。
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.
到目前为止,唯一建立的关系是BVView
项目原型 ( BVPrototype
)使用的视图( )。集合视图必须被告知它应该使用的原型以及从中获取数据的模型。
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
回答by Andreas Utzinger
@Bavarious You did an excellent job there. This was just an amazing tutorial which I sometimes miss at the Apple Docs.
@Bvarious 你在那里做得很好。这只是一个很棒的教程,我有时会在 Apple Docs 上错过它。
I rewrote Bavarious' code in Swift (v2) for anyone who's interested:
我为任何感兴趣的人在 Swift (v2) 中重写了 Bavarious 的代码:
// AppDelegate.swift:
// 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:
// 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:
// 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)
}
}
回答by Confused Vorlon
To answer brigadir's question on how to bind to a mutable array.
回答brigadir关于如何绑定到可变数组的问题。
zero'th - make titles an NSMutableArray
第零 - 使标题成为 NSMutableArray
first - bind the array to your items
首先 - 将数组绑定到您的项目
[cv bind:NSContentBinding
toObject:self
withKeyPath:@"titles"
options:NULL];
Second - when altering titles, make sure to modify the proxy.
其次 - 更改标题时,请确保修改代理。
e.g.
例如
NSMutableArray *kvcTitles = [self mutableArrayValueForKey:@"titles"];
[kvcTitles removeLastObject];