Пример простого документо-ориентированного Cocoa приложения для Mac OS

1) Создайте в Xcode проект типа Cocoa Application.


2) Щёлкните на проект DocBasedAppExample, далее на target DocBasedAppExample, перейдите на вкладку Info, и раскройте пункт Document Types.


Измените Name DocumentType на что-то более осмысленное, например, на Plain Text.

3) Добавьте в Document.xib текстовое поле NSTextView в котором будет отображаться содержимое текстового файла.

#import <Cocoa/Cocoa.h>

@interface Document : NSDocument

@property (assign) IBOutlet NSTextView *textView;

@end


4) Далее нужно реализовать метод dataOfType:error: класса Document, который по умолчанию выглядит так:

- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
    // Insert code here to write your document to data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning nil.
    // You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
    NSException *exception = [NSException exceptionWithName:@"UnimplementedMethod" reason:[NSString stringWithFormat:@"%@ is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
    @throw exception;
    return nil;
}

Требуется имплементация этого метода.

- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
    if ([typeName compare:@"Plain Text"] == NSOrderedSame) {
        return [_textView.textStorage.string dataUsingEncoding:NSUTF8StringEncoding];
    }
    
    NSLog(@"ERROR: dataOfType typeName=%@", typeName);
    *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:nil];
    return nil;
}

5) Далее нужно реализовать метод readFromData:ofType:error: класса Document, который по умолчанию выглядит так:


- (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.
    NSException *exception = [NSException exceptionWithName:@"UnimplementedMethod" reason:[NSString stringWithFormat:@"%@ is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
    @throw exception;
    return YES;
}

Требуется имплементация этого метода.

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
    if ([typeName compare:@"Plain Text"] != NSOrderedSame) {
        NSLog(@"** ERROR ** readFromData typeName=%@", typeName);
        *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
        return NO;
    } // end if
    
    NSDictionary *zDict = [NSDictionary dictionaryWithObjectsAndKeys:NSPlainTextDocumentType, NSDocumentTypeDocumentAttribute, nil];
    NSDictionary *zDictDocAttributes;
    NSError *zError = nil;
    NSAttributedString * zNSAttributedStringObj = [[NSAttributedString alloc]initWithData:data options:zDict documentAttributes:&zDictDocAttributes error:&zError];
    
    if (zError != nil)
    {
        NSLog(@"Error readFromData: %@",[zError localizedDescription]);
        return NO;
    } // end if
    
    fileContents = [zNSAttributedStringObj string];

    return YES;
}

Так как компонент NSTextView во время выполнения метода readFromData:ofType:error: оказывается еще неинициализированным, то нам понадобилась глобальная переменная fileContents.

#import "Document.h"

@implementation Document
{
    NSString *fileContents;
}

- (id)init
{
    self = [super init];
    if (self) {
        // Add your subclass-specific initialization here.
        
        fileContents = @"";
    }
    return self;
}

- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
    [super windowControllerDidLoadNib:aController];
    // Add any code here that needs to be executed once the windowController has loaded the document's window.
    
    _textView.string = fileContents;
}

6) Теперь можно запустить приложение и протестировать.



Пост написан на основе этой статьи.