Создание пользовательского объекта документа

Основанное на документе приложение должно иметь экземпляр подкласса UIDocument это представляет и управляет данными документа. В этой главе рассматриваются переопределения метода, большинство приложений должно сделать и выдвигает предположения для переопределения других методов. Для базовых точек переопределения — loadFromContents:ofType:error: и contentsForType:error: методы — примеры даны для обоих NSData и NSFileWrapper поскольку типы данных документа читали из и записанный в файл. Хранить Данные Документа в Пакете Файла далее объясняет, как использовать интерфейсные объекты файла для данных документа.

Вы можете переопределенные методы UIDocument кроме тех обсужденных в этой главе, чтобы читать и записать данные документа для конкретных целей — например, записать и считать данные документа инкрементно. Однако эти более усовершенствованные переопределения имеют более сложные требования и должны избежаться, если это возможно. См. Ссылку класса UIDocument для обсуждений этих переопределений.

Объявление интерфейса класса документа

В XCode добавьте новый Objective C исходные и заголовочные файлы к Вашему проекту, назвав их соответственно (предложение: работайте «Документ» на имя). В заголовочном файле измените суперкласс UIDocument и добавьте свойства для содержания данных документа. В Перечислении 3-1 данные документа являются простым текстом, таким образом, NSString свойство - все, что необходимо для содержания его. (Текст будет преобразован в NSData объект, записанный в файл документа.)

  Документ перечисления 3-1 разделяет объявления на подклассы (NSData)

@interface MyDocument : UIDocument {
}
@property(nonatomic, strong) NSString *documentText;
@end

Перечисление 3-2 иллюстрирует набор объявлений для другого приложения, использующего NSFileWrapper возразите как тип представления данных. (Примеры кода в главе чередуются между этими двумя приложениями.) Не только там свойство для содержания интерфейсного объекта файла, существуют свойства для содержания текста и компонентов изображения представленного пакета файла.

  Документ перечисления 3-2 разделяет объявления на подклассы (NSFileWrapper)

@interface ImageNotesDocument : UIDocument
 
@property (nonatomic, strong) NSString* text;
@property (nonatomic, strong) UIImage* image;
@property (nonatomic, strong) NSFileWrapper *fileWrapper;
 
@property (nonatomic, weak) id <ImageNotesDocumentDelegate> delegate;
@end
 
@protocol ImageNotesDocumentDelegate <NSObject>
-(void)noteDocumentContentsUpdated:(ImageNotesDocument*)noteDocument;
@end

Этот код показывает дополнительные объявления для делегата и протокола, что это принимает. Контроллер представления объекта документа делает себя делегатом объекта документа (и принимает протокол) так, чтобы это могло быть уведомлено (через noteDocumentContentsUpdated: сообщения) модификаций к файлу документа. Перечисление 3-4 показывает когда и как noteDocumentContentsUpdated: сообщение отправляется.

Загрузка данных документа

Когда приложение открывает документ (в запросе пользователя), UIDocument читает содержание файла документа и вызывает loadFromContents:ofType:error: метод, передающий в объекте, инкапсулирующем данные документа. Тот объект может быть NSData возразите или NSFileWrapper объект. В Вашем переопределении метода инициализируйте внутренние структуры данных документа (т.е. его объекты модели) от содержания переданного - в объекте.

Пример в Перечислении 3-3 создает строку из переданного - в NSData возразите и присваивает его documentText свойство. Это также сообщает своему делегату (в этом случае, контроллер представления документа) обновленного содержания документа путем вызова метода протокола. Мотивация позади этого сообщения делегации то, что loadFromContents:ofType:error: метод вызывают не только как результат открытия документа, но также и из-за обновлений iCloud и операций реверсии (revertToContentsOfURL:completionHandler:).

Перечисление 3-3  , Загружающее данные документа (NSData)

- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError {
    if ([contents length] > 0) {
        self.documentText = [[NSString alloc] initWithData:(NSData *)contents encoding:NSUTF8StringEncoding];
    } else {
        self.documentText = @"";
    }
    if ([_delegate respondsToSelector:@selector(noteDocumentContentsUpdated:)]) {
        [_delegate noteDocumentContentsUpdated:self];
    }
    return YES;
}

Если у Вас есть больше чем один тип документа, проверьте typeName параметр; различный тип документа мог бы влиять, как Ваш код обрабатывает объект данных документа. Если Ваш код сталкивается с ошибкой, препятствующей тому, чтобы он загрузил данные документа, возвратиться NO; дополнительно, можно возвратиться ссылкой NSError объект, описывающий ошибку.

Пример в Перечислении 3-4 обрабатывает данные документа в форме NSFileWrapper объект. Это просто присваивает этот объект своему свойству.

Перечисление 3-4  , Загружающее данные документа (NSFileWrapper)

-(BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError {
    self.fileWrapper = (NSFileWrapper *)contents;
    if ([_delegate respondsToSelector:@selector(noteDocumentContentsUpdated:)]) {
        [_delegate noteDocumentContentsUpdated:self];
    }
    return YES;
}

В этом коде реализация метода не извлекает текст и компоненты изображения обертки файла и присваивает их их свойствам. Это сделано лениво в методах получателя для text и image свойства.

Предоставление снимка данных документа

Когда документ закрывается или когда он автоматически сохраняется, UIDocument отправляет объект документа a contentsForType:error: сообщение. Необходимо переопределить этот метод для возврата снимка данных документа к UIDocument, который тогда пишет его в файл документа. Перечисление 3-5 дает пример возврата снимка данных документа в форме NSData объект.

Перечисление 3-5  Возвращая снимок данных документа (NSData)

- (id)contentsForType:(NSString *)typeName error:(NSError **)outError {
    if (!self.documentText) {
        self.documentText = @"";
    }
    NSData *docData = [self.documentText dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO];
    return docData;
}

Если documentText свойство еще еще не было присвоено никакое строковое значение, это присваивается пустая строка, прежде чем это будет использоваться для создания NSData объект.

Перечисление 3-6 показывает реализацию того же метода, возвращающегося NSFileWrapper объект. В основном, если верхний уровень (каталог) интерфейсный объект файла не существует, код создает его; и если два содержали (регулярный файл), интерфейсные объекты файла не существуют, код создает их из значений text и image свойства. Затем это возвращает обертку файла верхнего уровня UIDocument, который создает пакет файла в файловой системе. Посмотрите Хранящие Данные Документа в Пакете Файла для более подробного объяснения пакетов файла и документов.

Перечисление 3-6  Возвращая снимок данных документа (NSFileWrapper)

-(id)contentsForType:(NSString *)typeName error:(NSError **)outError {
 
    if (self.fileWrapper == nil) {
        self.fileWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil];
    }
    NSDictionary *fileWrappers = [self.fileWrapper fileWrappers];
    if (([fileWrappers objectForKey:TextFileName] == nil) && (self.text != nil)) {
        NSData *textData = [self.text dataUsingEncoding:TextFileEncoding];
        NSFileWrapper *textFileWrapper = [[NSFileWrapper alloc] initRegularFileWithContents:textData];
        [textFileWrapper setPreferredFilename:TextFileName];
        [self.fileWrapper addFileWrapper:textFileWrapper];
    }
    if (([fileWrappers objectForKey:ImageFileName] == nil) && (self.image != nil)) {
        @autoreleasepool {
            NSData *imageData = UIImagePNGRepresentation(self.image);
            NSFileWrapper *imageFileWrapper = [[NSFileWrapper alloc] initRegularFileWithContents:imageData];
            [imageFileWrapper setPreferredFilename:ImageFileName];
            [self.fileWrapper addFileWrapper:imageFileWrapper];
        }
    }
    return  self.fileWrapper;
}

Храня данные документа в пакете файла

Пакет файла имеет внутреннюю структуру, отражающуюся в методах NSFileWrapper класс. Обертка файла является представлением во время выполнения узла файловой системы, который является или каталогом, регулярным файлом или символьной ссылкой. Как показано на рисунке 3-1, пакет файла является узлом файловой системы, обычно каталог и его содержание, что операционная система обрабатывает как единственный, непрозрачный объект. Это подобно в понятии пакету.

  Структура рисунка 3-1 пакета файла

Вы программно составляете пакет файла путем создания обертки файла каталога верхнего уровня и затем добавления к тому контейнерному постоянному клиенту файлов и подкаталогов, каждый представленный другим NSFileWrapper объекты. Обертки файла в каталоге верхнего уровня должны были предпочесть имена, связанные с ними.

С этим кратким обзором в памяти, смотрите снова на следующие строки кода от contentsForType:error: метод в Перечислении 3-6. Пакет файла, создаваемый в этом методе, имеет два компонента, текстовый файл и файл образа. (Создание обертки файла образа не показано в отрывке.)

    if (self.fileWrapper == nil) {
        self.fileWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil];
    }
    NSDictionary *fileWrappers = [self.fileWrapper fileWrappers];
    if (([fileWrappers objectForKey:TextFileName] == nil) && (self.text != nil)) {
        NSData *textData = [self.text dataUsingEncoding:TextFileEncoding];
        NSFileWrapper *textFileWrapper = [[NSFileWrapper alloc] initRegularFileWithContents:textData];
        [textFileWrapper setPreferredFilename:TextFileName];
        [self.fileWrapper addFileWrapper:textFileWrapper];
    }

Код создает каталог верхнего уровня, если он не существует. Если обертка файла не существует для текстового файла, она создает один из строкового содержания текстового свойства. Это дает этой обертке файла предпочтительное имя файла и затем добавляет его к обертке файла каталога верхнего уровня.

Для больше на NSFileWrapper, посмотрите Ссылку класса NSFileWrapper; также посмотрите Экспорт Документа UTI для требуемого Info.plist свойство для пакетов файла документа.

Другие переопределения метода Вы могли бы сделать

Существуют некоторые другой UIDocument переопределения, которые много основанных на документе приложений могли бы хотеть сделать: