2

我正在创建一个基于“测试”文档的应用程序,以了解有关它们如何工作的更多信息。我想加载一个我之前保存的数组。

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
    NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSArray *array = [string componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];

    documentTitle.stringValue = [array objectAtIndex:0];

    // For testing
    NSLog(@"%@", array);
}

我加载了文件并获取了数据。我将数据转换为 NSString。该文件有多行,每一行都有自己的值。第一行是文档标题。所以我创建了一个 NSArray,将每一行分别放在数组中。然后我将文档的第一行设置为 documentTitle 标签。出于测试目的,我也创建了一个 NSLog 函数。它必须记录数组。

问题是当我运行代码时,它根本没有做任何事情。documentTitle.stringValue 不会改变。但奇怪的是它确实记录了数组:

2015-09-22 16:57:40.098 DocBased Test[10765:277445] 
(
    "The awesome title!",
    "This is a document!"
)

我在Document.h中声明了 documentTitle,如下所示:

#import <Cocoa/Cocoa.h>

    @interface Document : NSDocument
    {
        IBOutlet NSTextField *documentTitle;
    }

@end

如果你需要在 Xcode 中查看项目,这里有一个链接:链接

有谁知道如何更改Document类中 documentTitle (或任何)标签的 stringValue ?

4

2 回答 2

1

幸运的是,这是一个常见问题:)

识别包含您的 .xib 文件的NSTextView. 比如说它叫做MyViewController.xib.

在 Interface Builder 中,打开 Utilities 面板(右侧面板,按钮位于右上角)。在实用程序面板的底部,单击带有正方形的圆圈图标,然后搜索“对象”

在此处输入图像描述

现在单击并将其拖动Object到“占位符”部分下方的左侧(文档大纲)。

在此处输入图像描述

这将创建一个名为“对象”的新部分。

单击文档大纲中的新对象。现在回到实用程序面板,转到“身份检查器”

将“类”更改为Document

在此处输入图像描述

现在转到“实用程序”面板中的“连接检查器”。您的 (IB) 网点Document应该出现在这里,它们看起来像这样:
在此处输入图像描述

除了“nextKeyView”之外,您将看到documentTitle

单击并拖动插座右侧的圆圈,然后在NSTextField您想成为您的documentTitle.

现在让我们谈谈将要发生的事情。

  • 首先,您将MyViewController使用类似的方法 实例化您的initWithNibName:...
  • 请注意,nib支持 的MyViewController.xib尚未加载(这称为延迟加载)
  • 当您尝试访问 的view属性时NSViewController,它将加载与其关联的 nib
  • nib 被加载,里面的对象都被实例化了
  • 这些init方法都在这些对象中的每一个上调用
  • 现在已建立插座连接,包括您与Document对象的连接NSTextView
  • -awakeFromNib发送到通过 nib 实例化的所有对象

此时,您的属性documentTitle现在正在引用一个有效 NSTextField对象,并且不再是nil. 您现在可以随意stringValue设置

希望这可以帮助!

于 2015-09-24T18:01:01.573 回答
1

鉴于您的项目@ https://www.mediafire.com/?z6drw20suvt0re0
我能够让它工作。写一个新答案,因为我的原件已经很笨重,更适合一般情况;而这个答案对于您的代码将非常具体

只有几个变化:
1) 因为您的 Document.xib 的所有者是Document,所以您不需要将 Document Object 拖入您的 Document Outline 中。所以我删除了那个。
2)我选择了文件的所有者(文档),并设置了与您的 NSTextField 属性的连接
3)我删除了 ivar

@interface Document : NSDocument
{
    IBOutlet NSTextField *documentTitle;
}

所以你的标题现在看起来像这样:

@interface Document : NSDocument

@end

4) 我在您的实现文件 (Document.m) 中创建了属性以及一个NSString属性(我将在稍后解释)

@property NSString *titleString;
@property IBOutlet NSTextField *documentTitle;

5)因此,如果您继续并覆盖该-awakeFromNib方法(只需在NSLog其中放置一个或其他内容),您将看到实际上-awakeFromNib正在调用 AFTER -readFromData。现在请记住我所说的——对象已实例化,但在加载视图并-awakeFromNib发送消息之前不会建立任何连接。

所以会发生这样的事情:

  • 您单击“打开”并选择一个文件
  • Document的实例化了
  • -readData...NSTextField在实例化过程中立即调用,但尚未建立与的连接,所以它是nil
  • -awakeFromNib被调用,从Document任何NSViews (包括你的NSTextField)的连接被建立
  • 但为时已晚。

所以解决方案很简单,
-readData方法中,而不是设置你NSTextField的 's stringValue,你应该把你读到的字符串保存到一个NSString属性中(我之前提到的)

然后当你-awakeFromNib被调用时,你可以访问NSString你想要的值的属性,然后设置stringValuethen.

以下是更改的两种方法:

-(void)awakeFromNib
{
   if( self.titleString )
   {
      [self.documentTitle setStringValue:self.titleString];
   }
}

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
    // Insert code here to read your document from the given data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning NO.
    // You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead.
    // If you override either of these, you should also override -isEntireFileLoaded to return NO if the contents are lazily loaded.

    NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSArray *array = [string componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];

    NSLog(@"\r%@", array);


    self.titleString = [array objectAtIndex:1];

    return YES;
}

如果您想在任何地方获得更多解释,请告诉我,但这对您有用:)

于 2015-09-24T20:02:22.610 回答