Создание пользовательского объекта документа
Основанное на документе приложение должно иметь экземпляр подкласса 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, пакет файла является узлом файловой системы, обычно каталог и его содержание, что операционная система обрабатывает как единственный, непрозрачный объект. Это подобно в понятии пакету.
Вы программно составляете пакет файла путем создания обертки файла каталога верхнего уровня и затем добавления к тому контейнерному постоянному клиенту файлов и подкаталогов, каждый представленный другим 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
переопределения, которые много основанных на документе приложений могли бы хотеть сделать:
disableEditing
...enableEditing
—UIDocument
вызывает первый метод, когда небезопасно для пользователя внести изменения для документирования содержания, такой как тогда, когда существуют обновления от iCloud, или вернуться работа в стадии реализации. Можно реализовать этот метод для предотвращения редактирования в течение этого периода. Когда редактирование становится безопасным снова,UIDocument
вызывает второй метод.savingFileType
— Этот метод значением по умолчанию возвращает значениеfileType
свойство. Если текущий документ должен быть сохранен под различным типом файла по какой-либо причине, можно переопределить этот метод для возврата заменяющего типа файла UTI. Пример (от Mac OS X) - то, что, когда изображение добавляется к файлу RTF, это должно быть сохранено как пакет файла RTFD.