NSView上的唯一ID

时间:2011-12-13 16:49:18

标签: objective-c cocoa

是否有任何类型的ID可以通过Xcode在.nib / .xib中使用和设置,可以在运行时查询以从代码中识别特定的视图实例?

特别是在我们的界面中拥有相同NSView子类的多个副本时,我们如何知道我们当前正在查看哪个?

4 个答案:

答案 0 :(得分:13)

在Interface Builder中,有一种方法可以设置NSView的“标识符”。在这种情况下,我将使用标识符“54321”作为标识符字符串。

NSView符合NSUserInterfaceItemIdentification Protocol,它是NSString的唯一标识符。您可以遍历视图层次结构并找到具有该标识符的NSView。

因此,要在这篇关于获取NSView列表Get ALL views and subview of NSWindow的帖子的基础上构建,您可以找到具有所需标识符的NSView:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    NSView *viewToFind = [self viewWithIdentifier:@"54321"];
}  

- (NSView *)viewWithIdentifier:(NSString *)identifier
{
    NSArray *subviews = [self allSubviewsInView:self.window.contentView];

    for (NSView *view in subviews) {
        if ([view.identifier isEqualToString:identifier]) {
            return view; 
        }
    }

    return nil;
}

- (NSMutableArray *)allSubviewsInView:(NSView *)parentView {

    NSMutableArray *allSubviews     = [[NSMutableArray alloc] initWithObjects: nil];
    NSMutableArray *currentSubviews = [[NSMutableArray alloc] initWithObjects: parentView, nil];
    NSMutableArray *newSubviews     = [[NSMutableArray alloc] initWithObjects: parentView, nil];

    while (newSubviews.count) {
        [newSubviews removeAllObjects];

        for (NSView *view in currentSubviews) {
            for (NSView *subview in view.subviews) [newSubviews addObject:subview];
        }

        [currentSubviews removeAllObjects];
        [currentSubviews addObjectsFromArray:newSubviews];
        [allSubviews addObjectsFromArray:newSubviews];

    }

    for (NSView *view in allSubviews) {
        NSLog(@"View: %@, tag: %ld, identifier: %@", view, view.tag, view.identifier);
    }

    return allSubviews;
}

或者,由于您使用的是NSView子类,因此可以在运行时设置每个视图的“标记”。 (或者,您可以在运行时设置标识符。)标记的好处是,有一个预先构建的函数,用于查找具有特定标记的视图。

// set the tag
NSInteger tagValue = 12345;
[self.myButton setTag:tagValue];

// find it 
NSButton *myButton = [self.window.contentView viewWithTag:12345];

答案 1 :(得分:10)

通用NSView对象不能在Interface Builder中设置其tag属性。 tag上的NSView方法是只读方法,只能在NSView的子类中实现。 NSView未实施setTag:方法。

我怀疑其他答案是指NSControl定义-setTag:方法的实例,并且有一个Interface Builder字段可以设置标记。

可以对通用视图执行的操作是使用用户定义的运行时属性。这允许您在视图对象中预设属性的值。因此,如果您的视图定义了这样的属性:

@property (strong) NSNumber* viewID;

然后,在Interface Builder中Identity检查器的用户定义属性部分中,您可以添加一个包含键路径viewID,类型Number和值123的属性。 / p>

在视图的-awakeFromNib方法中,您可以访问该属性的值。您会发现,在上面的示例中,您视图的viewID属性已预先设置为123

答案 2 :(得分:1)

这里介绍了如何在OSX中模拟“标签”而不进行子类化。

在iOS中:

{
    // iOS:
    // 1. You add a tag to a view and add it as a subView, as in:
    UIView *masterView = ... // the superview
    UIView *aView = ... // a subview
    aView.tag = 13;
    [masterView addSubview:aView];

    // 2.  Later, to retrieve the tagged view:
    UIView *aView = [masterView viewWithTag:13];
    // returns nil if there's no subview with that tag
}

等效于OSX:

#import <objc/runtime.h> // for associated objects
{
    // OSX:
    // 1.  Somewhere early, create an invariant memory address
    static void const *tag13 = &tag13; // put at the top of the file

    // 2.  Attach an object to the view to which you'll be adding the subviews:
    NSView *masterView = ... // the superview
    NSView *aView = ...  // a subview
    [masterView addSubview:aView];
    objc_setAssociatedObject(masterView, tag13, aView, OBJC_ASSOCIATION_ASSIGN);

    // 3.  Later, to retrieve the "tagged" view:
    NSView *aView = objc_getAssociatedObject(masterView, tag13);
    // returns nil if there's no subview with that associated object "tag"
}

编辑:关联的对象“键”(声明为const void *key)必须是不变的。我正在使用Will Pragnell(https://stackoverflow.com/a/18548365/236415)的想法。 在堆栈溢出中搜索其他用于生成密钥的方案。

答案 3 :(得分:-1)

这是一种无需子类即可在OSX中获取NSView标签的简单方法。

尽管NSView的tag属性是只读的,但某些对象 从NSView继承的具有读/写标签属性。 例如,NSControlNSImageView具有读/写标记属性。

因此,只需使用NSControl而不是NSView,然后禁用(或忽略) NSControl件事情。

- (void)tagDemo
{
    NSView *myView1 = [NSView new];
    myView1.tag = 1; // Error: "Assignment to readonly property"

    // ---------

    NSControl *myView2 = [NSControl new]; // inherits from NSView
    myView2.tag = 2; // no error
    myView2.enabled = NO; // consider
    myView2.action = nil; // consider

    // ---------

    NSImageView *myView3 = [NSImageView new]; // inherits from NSControl
    myView3.tag = 3; // no error
    myView3.enabled = NO; // consider
    myView3.action = nil; // consider
}

稍后,如果您使用viewWithTag:来获取视图,请确保将NSControl(或NSImageView)指定为返回类型。