3

I'm using an NSCollectionView to display various objects. The whole things works rather well, except for one annoying thing. I cannot figure out how to access the various controls on the view used to represent each object in the collection.

Here's the setup:

  • I have dragged an NSCollectionView into my view in IB.
  • I made a custom subclass of NSCollectionViewItem. Mapped my class in IB.
  • I made a custom subclass of NSBox to act as the view for each object in the collection. Also mapped this class in IB and connected it to the view property of my NSCollectionViewItem subclass.
  • I made all the bindings in IB to display the correct information for each object.

The view:

enter image description here

The resulting collection view:

enter image description here

Reasoning that that my subclass of NSCollectionViewItem is basically a controller for each view in the collection, I made referencing outlets of the various controls in the view in my controller subclass:

@interface SourceCollectionViewItem : NSCollectionViewItem

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

@end

When I inspect any instance of SourceCollectionViewItem in the debugger, all the properties show up as nil despite the fact that I can actually see them on my screen and that everything is displayed as it should be.

My setup was inspired by Apple's sample app IconCollection.

I am obviously missing something. What?

EDIT: I found various posts hinting at a similar issue: CocoaBuilder.com and this question on SO.

EDIT: Just to be complete: this post deals with the subject as well and delivers a solution based on a combination of the options mentioned in the accepted answer.

4

3 に答える 3

6

コンセントは nib の読み込み中に設定され、プロトタイプ アイテムのみが nib から読み込まれ、そのコンセントが割り当てられます。他のすべてViewItemの とそのViewはプロトタイプから複製されます。その場合、アウトレットは初期化されない単なるインスタンス変数です。

私が思いつくことができるオプションは次のとおりです。

  • newItemForRepresentedObject:プロトタイプのクローンを作成する代わりに、コレクション ビューをオーバーライドして nib をリロードします。しかし、これはおそらくパフォーマンスを大きく損なうでしょう。
  • コレクション ビュー アイテムをオーバーライドcopyWithZoneし、アウトレットを手動で割り当て viewWithTag:て検索します。
  • あきらめて、バインディングのみを介してデータを提供してみてください。
于 2012-07-09T07:45:35.557 に答える
1

すべての IBOutlet の準備ができているように見えるときに新しいアイテムで呼び出されるため、 NSCollectionViewItem をオーバーライド-setRepresentedObject:することも良い選択であることがわかりました。あなたへの呼び出しの後、super必要なことは何でもできます:

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

このメソッドを使用して、インターフェイス オブジェクトのカスタム プロパティをバインドしました。このチェックは、presentedObject がまだ準備できていないときに、無駄な呼び出しを避けるために行われます。元の編集のリンクで説明されているように、プロジェクトは ViewItem に別の xib を使用します。

于 2014-08-31T22:14:39.163 に答える
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
    }
}
于 2016-02-04T19:35:46.573 に答える