【问题标题】:How to create NSCollectionView programmatically from scratch?如何从头开始以编程方式创建 NSCollectionView?
【发布时间】:2011-12-28 19:57:25
【问题描述】:

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

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

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

如果我们能找到一个很好的例子来说明如何做到这一点,可供许多人使用,我认为这将帮助所有与NSCollectionViews 合作并像我一样困惑的人。

请求摘要

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

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

【问题讨论】:

    标签: xcode cocoa nscollectionview


    【解决方案1】:

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

    简介

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

    • 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 文件中获取视图的极少数情况下,开发人员必须在视图控制器接收到-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];
    

    收藏视图

    到目前为止,唯一建立的关系是项目原型 (BVPrototype) 使用的视图 (BVView)。集合视图必须被告知它应该使用的原型以及从中获取数据的模型。

    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
    

    【讨论】:

    • NSCollectionView 上的文档出奇的差。任何了解这种神秘野兽如何工作并能够分享他们知识的人都在帮助各地的 Objective-C 开发人员。非常感谢。
    • Bavarious,我可以请您展示如何将集合视图与可变的项目数组同步吗?
    • @brigadir 你需要使用绑定。
    • 那么 NSCollectionView 实例出现在哪里呢?我尝试在 IB 中拖动一个,但我只得到一个滚动视图、剪辑视图等嵌套。没有对象具有“NSCollectionView”类作为出口添加到我的控制器。
    • 它在 NSScrollView 的 Clip 视图中(如表格、大纲和文本视图)
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      第零个 - 将标题设为 NSMutableArray

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

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

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

      例如

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-10-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-20
        • 1970-01-01
        • 2021-12-17
        相关资源
        最近更新 更多