访问NSCollectionView的视图控件

时间:2012-07-08 21:13:33

标签: cocoa nscollectionview

我正在使用NSCollectionView来显示各种对象。除了一件烦人的事情外,整件事情都运作良好。我无法弄清楚如何访问用于表示集合中每个对象的视图上的各种控件。

以下是设置:

  • 我已将NSCollectionView拖入我在IB中的视图。
  • 我制作了NSCollectionViewItem的自定义子类。在IB中映射我的班级。
  • 我创建了NSBox的自定义子类,作为集合中每个对象的视图。还在IB中映射了此类,并将其连接到我的view子类的NSCollectionViewItem属性。
  • 我在IB中创建了所有绑定,以显示每个对象的正确信息。

观点:

enter image description here

生成的集合视图:

enter image description here

推测我的NSCollectionViewItem的子类基本上是集合中每个视图的控制器,我在控制器子类的视图中引用了各种控件的出口:

@interface SourceCollectionViewItem : NSCollectionViewItem

@property (weak) IBOutlet NSTextField *nameTextField;
@property (weak) IBOutlet NSTextField *typeTextField;
@property (weak) IBOutlet RSLabelView *labelView;
@property (weak) IBOutlet NSButton *viewButton;

@end

当我在调试器中检查SourceCollectionViewItem的任何实例时,所有属性都显示为nil,尽管事实上我可以在屏幕上看到它们并且所有内容都显示为它应该是。

我的设置灵感来自Apple的示例应用IconCollection

我显然缺少一些东西。什么?

编辑:我发现各种帖子暗示了类似的问题: 关于SO的CocoaBuilder.comthis question

编辑:只是为了完成:this post也处理主题,并根据接受的答案中提到的选项组合提供解决方案。

3 个答案:

答案 0 :(得分:6)

在笔尖加载期间设置出口,并且仅从笔尖加载原型项并分配其出口。所有其他ViewItem和它们的View都是从原型中克隆出来的,在这种情况下,出口只是从未初始化的实例变量。

以下是我可以提出的选项:

  • 覆盖集合视图的newItemForRepresentedObject:并重新加载nib,而不是克隆原型。但这可能会大大损害性能。
  • 覆盖copyWithZone集合视图项并使用viewWithTag:手动分配插座以查找它们。
  • 放弃并尝试仅通过绑定提供数据。

答案 1 :(得分:1)

我发现重写NSCollectionViewItem -setRepresentedObject:也是一个不错的选择,因为当所有IBOutlet似乎准备就绪时,它会在新项目上调用。致电super后,您可以做任何需要的事情:

- (void)setRepresentedObject:(id)representedObject
{
    if (representedObject) {
        [super setRepresentedObject:representedObject];
        [self.anOutlet bind:@"property" toObject:self.representedObject withKeyPath:@"representeProperty" options:nil];
    }
}

我使用此方法绑定接口对象的自定义属性。当representObject尚未准备好时,检查是为了避免无用的调用。该项目使用单独的xib作为ViewItem,如原始编辑中的链接所述。

答案 2 :(得分:0)

好问题。就像@hamstergene建议的那样,你可以使用copyWithZone,与newItemForRepresentedObject相比,它会更有效率。但是viewWithTag并不总是一个选项,首先,因为不是所有东西都可以标记(轻松),其次,为此目的使用标记有点不对。在Swift中,这是一个考虑性能的很酷的方法。

import AppKit

class MyViewController: NSCollectionItemView
{

    // Here you are cloning the original item loaded from the storyboard, which has 
    // outlets available, but as you've seen the default implementation doesn't take
    // care of them. Each view has a unique identifiers, which you can use to find it
    // in sublayers. What's really cool about this, is that you don't need to assign
    // any tags or do anything else while having advantage of better performance using
    // cached nib object.

    override func copyWithZone(zone: NSZone) -> AnyObject {
        let copy: NSCollectionItemView = super.copyWithZone(zone) as! NSCollectionItemView
        let oldView: RecordingView = self.view as! MyView
        let newView: RecordingView = copy.view as! MyView

        newView.foo = newView.viewWithIdentifier(oldView.foo.identifier!) as! NSTextfield
        newView.bar = newView.viewWithIdentifier(oldView.bar.identifier!) as! NSImageView

        return copy
    }
}

@IBDesignable class MyView: View
{

    // Custom collection view item view. Lets assume inside of it you have two subviews which you want
    // to access in your code.

    @IBOutlet weak var foo: NSTextfield!
    @IBOutlet weak var bar: NSImageView!
}

extension NSView
{

    // Similar to viewWithTag, finds views with the given identifier.

    func viewWithIdentifier(identifier: String) -> NSView? {
        for subview in self.subviews {
            if subview.identifier == identifier {
                return subview
            } else if subview.subviews.count > 0, let subview: NSView = subview.viewWithIdentifier(identifier) {
                return subview
            }
        }
        return nil
    }
}