《iOS 6高级开发手册(第4版)》——2.8节秘诀:声明文档支持

简介:

本节书摘来自异步社区《iOS 6高级开发手册(第4版)》一书中的第2章,第2.8节秘诀:声明文档支持,作者 【美】Erica Sadun,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.8 秘诀:声明文档支持
iOS 6高级开发手册(第4版)
应用程序文档并不仅限于它们创建或者从Internet下载的文件。如你在前一个秘诀中所发现的,应用程序可能处理某些文件类型。它们可能打开从其他应用程序传递过来的项目。你已经从发送方的角度见过了文档共享,它使用“open in”(打开在)控制器把文件导出到其他应用程序。现在应该从接收方的角度探讨它。

应用程序在它们的Info.plist属性列表中声明它们对某些文件类型的支持。Launch Services(启动服务)系统将读取该数据,并创建被文档交互控制器使用的文件-应用程序之间的关联。

尽管可以直接编辑属性列表,但是Xcode 4提供了一种简单的形式,作为Project > Target > Info screen的一部分。定位Document Types区域,你将发现它位于Custom iOS Target Properties下面。打开这个区域,并单击“+”,添加受支持的新文档类型。图2-8显示了它对于接受JPEG图像文档的应用程序来说看起来像是什么样子的。


ae95e005981001ba92b6c3d72e3c980e6f6a28cc

这个声明包含3个最低限度的细节:名称、一个或多个UTI以及一个处理程序等级,在这里是替代者。

名称是必需的和任意的。它应该能够描述正在使用的文档的种类,但它在iOS上也有点像是一种事后追加的东西。当在Macintosh上使用时这个字段将更有意义(它是Finder使用的“种类”字符串),但它不是可选的。
在输入时指定一个或多个UTI。这个示例只指定了public.jpeg。在列出多个项目时,可以在项目之间添加逗号。例如,你可能具有一种打开public.jpeg、public.tiff和public.png的“图像”文档类型。当需要限制文件支持时,可以枚举特定的类型。尽管声明public.image将包含全部3种类型,它也可能允许打开不受支持的图像样式。
启动服务处理程序等级描述了应用程序如何在处理这种文件类型的竞争中看待它自身。“所有者”说这是创建这类文件的原始应用程序,“替代者”(如图2-8所示)则提供了辅助的查看方式。可以在额外的文档类型属性中手动添加LSHandlerRank键。
可以选择指定图标文件。它们在OS X中被用作文档图标,与iOS世界具有最少的重叠。在唯一一种情况下,我可以认为你可能把这些图标看作iTunes中的Apps选项卡,此时将使用File Sharing区域添加和移除项目。图标通常具有两种尺寸:320像素×320像素(UTType Size320IconFile)和64像素×64像素(UTTypeSize64IconFile),通常限制于应用程序创建并为其定义一种自定义类型的文件。

Xcode在底下使用这种交互形式在应用程序的Info.plist中构建一个CFBundleDocumentTypes数组。下面的代码段以其Info.plist形式显示了图2-8中的信息。

<key>CFBundleDocumentTypes</key>
<array>
    <dict>
        <key>CFBundleTypeIconFiles</key>
        <array/>
        <key>CFBundleTypeName</key>
        <string>jpg</string>
        <key>LSHandlerRank</key>
        <string>Alternate</string>
        <key>LSItemContentTypes</key>
        <array>
            <string>public.jpeg</string>
        </array>
    </dict>
</array>

2.8.1 创建自定义的文档类型
当应用程序构建新的文档类型时,应该在Target > Info编辑器的Exported UTIs区域中声明它们,如图2-9所示。这将注册系统支持这种文件类型,并把你标识为该类型的所有者。


1e112157ff871333eeed7cfe97177d794efcc780

编辑器的Exported UTIs区域中声明自定义的文件类型

要定义新的类型,可以提供一个自定义的UTI(在这里是com.sadun.cookbookfile)、文档艺术作品(大小为64像素和320像素),并且指定标识文件类型的文件扩展名。与声明文档支持一样,Xcode将把一个导出的声明数组构建到项目的Info.plist文件中。下面显示了图2-9中所示的声明看起来可能像是什么样子的。

<key>UTExportedTypeDeclarations</key>
<array>
    <dict>
        <key>UTTypeConformsTo</key>
        <array>
            <string>public.text</string>
        </array>
        <key>UTTypeDescription</key>
        <string>Cookbook</string>
        <key>UTTypeIdentifier</key>
        <string>com.sadun.cookbookfile</string>
        <key>UTTypeSize320IconFile</key>
        <string>Cover-320</string>
        <key>UTTypeSize64IconFile</key>
        <string>Cover-64</string>
        <key>UTTypeTagSpecification</key>
        <dict>
            <key>public.filename-extension</key>
            <string>cookbook</string>
        </dict>
    </dict>
</array>

如果像这样添加到项目中,应用程序应该会使用com.sadun.cookbookfile UTI,打开具有cookbook扩展名的任何文件。

2.8.2 实现文档支持
当应用程序提供了文档支持时,每当“Inbox”(收件箱)文件夹变成活动状态时都应该检查它。确切地讲,查看Inbox文件夹是否出现在Documents文件夹中。如果是,就应该把那个收件箱中的元素移到属于它们的位置,通常是在主Documents目录中。在清空了收件箱后,就删除它。这提供了最佳的用户体验,尤其对于通过iTunes进行任何文件共享则更是如此,其中的Inbox及其作用可能使用户混淆:

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // perform inbox test here
}

当把项目移到Documents中时,要检查名称冲突,并且使用替代路径名称(通常是通过追加一个连字符,其后接着一个数字)来避免重写任何现有的文件。下面的方法有助于查找目标路径的替代名称。在经过1000次尝试后,它会放弃,但是严重的是,任何用户都不应该宿主许多重复的文档名称。如果他们这样做,总体应用程序设计就会存在严重的错误。

秘诀2-8展示了扫描Inbox并把文件移到合适位置的细节。它将在清空Inbox之后移除它。可以看到,任何像这样的方法都是文件管理器密集型的。它主要涉及处理可能在整个任务过程中弹出的各种可能的错误组合,这对于小文件支持应该运行得很快。如果必须处理大文件,比如视频或音频,就要确保在它自己的操作队列上执行这种处理。

如果计划支持public.data文件(也就是说,将打开任何内容),则可能希望使用UIWebView实例显示那些文件。参阅Technical Q&A QA1630(http://developer.apple.com/library/ios/#qa/qa1630),可了解关于iOS在那些视图中能够以及不能显示哪些文档类型的详细信息。Web视图可以展示大多数音频和视频资源,以及Excel、Keynote、Numbers、Pages、PDF、PowerPoint和Word资源,还包括简单的HTML。

秘诀2-8 处理传入的文档

#define DOCUMENTS_PATH [NSHomeDirectory() \
    stringByAppendingPathComponent:@"Documents"]
#define INBOX_PATH      [DOCUMENTS_PATH \
    stringByAppendingPathComponent:@"Inbox"]

@implementation InboxHelper
+ (NSString *) findAlternativeNameForPath: (NSString *) path
{
    NSString *ext = path.pathExtension;
    NSString *base = [path stringByDeletingPathExtension];

for (int i = 1; i < 999; i++)
{
    NSString *dest =
        [NSString stringWithFormat:@"%@-%d.%@", base, i, ext];

    // if the file does not yet exist, use this destination path
    if (![[NSFileManager defaultManager]
        fileExistsAtPath:dest])
            return dest;
    }
    NSLog(@"Exhausted possible names for file %@. Bailing.",
        path.lastPathComponent);
    return nil;
}
- (void) checkAndProcessInbox
{
    // Does the Inbox exist? If not, we're done
    BOOL isDir;
    if (![[NSFileManager defaultManager]
        fileExistsAtPath:INBOX_PATH isDirectory:&isDir])
        return;

    NSError *error;
    BOOL success;

    // If the Inbox is not a folder, remove it.
    if (!isDir)
    {
        success = [[NSFileManager defaultManager]
            removeItemAtPath:INBOX_PATH error:&error];
        if (!success)
        {
            NSLog(@"Error deleting Inbox file (not directory): %@",
                error.localizedFailureReason);
            return;
        }
    }

// Retrieve a list of files in the Inbox
NSArray *fileArray = [[NSFileManager defaultManager]
    contentsOfDirectoryAtPath:INBOX_PATH error:&error];
if (!fileArray)
{
    NSLog(@"Error reading contents of Inbox: %@",
        error.localizedFailureReason);
    return;
}

// Remember the number of items
NSUInteger initialCount = fileArray.count;

// Iterate through each file, moving it to Documents
for (NSString *filename in fileArray)
{
    NSString *source =
        [INBOX_PATH stringByAppendingPathComponent:filename];
    NSString *dest = [DOCUMENTS_PATH
        stringByAppendingPathComponent:filename];

    // Is the file already there?
    BOOL exists =
        [[NSFileManager defaultManager] fileExistsAtPath:dest];
    if (exists) dest = [self findAlternativeNameForPath:dest];
    if (!dest)
    {
        NSLog(@"Error. File name conflict not resolved");
        continue;
    }

    // Move file into place
    success = [[NSFileManager defaultManager]
        moveItemAtPath:source toPath:dest error:&error];
    if (!success)
    {
        NSLog(@"Error moving file from Inbox: %@",
            error.localizedFailureReason);
        continue;
    }
}

    // Inbox should now be empty
    fileArray = [[NSFileManager defaultManager]
        contentsOfDirectoryAtPath:INBOX_PATH error:&error];
    if (!fileArray)
    {
        NSLog(@"Error reading contents of Inbox: %@",
            error.localizedFailureReason);
        return;
    }

    if (fileArray.count)
    {
        NSLog(@"Error clearing Inbox. %d items remain",
            fileArray.count);
        return;
    }

    // Remove the inbox
    success = [[NSFileManager defaultManager]
        removeItemAtPath:INBOX_PATH error:&error];
    if (!success)
    {
        NSLog(@"Error removing inbox: %@",
            error.localizedFailureReason);
        return;
    }
    NSLog(@"Moved %d items from the Inbox", initialCount);
}
@end
相关文章
|
数据库 iOS开发 数据库管理
|
Android开发 iOS开发 Web App开发
关于安卓图像显示比iOS差的原因(英文文档)
There are so many comparations between Android phone and iPhone. We cannot make the conclusion about which one is better, bu...
896 0
|
iOS开发
【iOS开发】Swift Print 高级用法
这个东西不是我写的,出自 StackOverFlow,但我觉得实在是写得太好了,于是截取了该部分分享出来,感谢原作者。 StackOverFlow
838 0