arraysmacosclasscocoadocument-based

Can't change stringValue from label in Document class (Document based application, Mac)


I'm creating a 'test' document based application to learn more about how they work. I want to load an array that I saved earlier.

- (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);
}

I loaded the file and got the data. I converted the data into an NSString. The file has multiple lines and every line has its own value. The first line is the document title. So I created an NSArray that put every line separately in the array. Then I set the first line of the document to the documentTitle label. For testing purposes I created an NSLog function too. It has to log the array.

The problem is that when I run the code, it doesn't do anything at all. The documentTitle.stringValue doesn't change. But the strange thing is that it does log the array:

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

I declared the documentTitle in the Document.h like this:

#import <Cocoa/Cocoa.h>

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

@end

If you need to view the project in Xcode here's a link: The link.

Does anyone know how to change the stringValue of the documentTitle (or any) label in the Document class?


Solution

  • Given your project @ https://www.mediafire.com/?z6drw20suvt0re0,
    I was able to get it working. Writing a new answer because my original is already really bulky, and fits more of a general case; whereas this answer will be very specific with your code

    Only a few changes:
    1) Because your Document.xib's owner is Document, you do not need to drag in the Document Object into your Document Outline. So I deleted that.
    2) I selected the File's Owner (Document), and set up the connection to your NSTextField property
    3) I removed the ivar

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

    so your header now looks like this:

    @interface Document : NSDocument
    
    @end
    

    4) I created the property inside of your implementation file (Document.m) as well as an NSString property (I will explain that in a second)

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

    5) So if you go ahead and override the -awakeFromNib method (Just put an NSLog or something in there), you will see that in fact -awakeFromNib is being called AFTER -readFromData. Now remember what I said-- that the object is instantiated, but no connections are made until the view is loaded and the -awakeFromNib message is sent.

    So what happens is this:

    So the solution is simple,
    in the -readData method, instead of setting your NSTextField's stringValue, you should just save the string you read into a NSString property (What I mentioned earlier)

    Then when your -awakeFromNib is called, you can just access that NSString property for the value you want, and set the stringValue then.

    Here are the two methods that were changed:

    -(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;
    }
    

    Let me know if you would like more explanation anywhere, but this will work for you :)