iOS 中的分享

分享内容

国内主流 app 的分享功能都是自定义的,这样做的考虑主要是为了让流量留在自己的生态中,另一方面 UIActivityViewController 不太符合国人的使用习惯。

相比之下,国外的 app 如 Twitter,使用的都是苹果从 iOS 6 开始引入的 UIActivityViewController。

UIActivityViewController

如果你的 app 面向国外用户,UIActivityViewController 了解一下。:)

UIActivityViewController 使用起来非常方便,首先初始化一个实例:

1
init(activityItems: [Any],  applicationActivities: [UIActivity]?)

其中,activityItems 是你要分享的内容,可以是字符串、URL或者图片等内容。但是接受分享的应用能接受的文件类型不一样,我们还可以通过自定义 activityItems 来实现以不同形式分享到不同应用的功能。

要自定 activityItems,只需实现 UIActivityItemSource ,它有两个必选方法:

1
2
3
4
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any

func activityViewController(_ activityViewController: UIActivityViewController,
itemForActivityType activityType: UIActivityType?) -> Any?

其中第一个方法返回一个 placeholder,官方文档是这样解释的

For example, the placeholder could be a UIImage object but the actual value could be an NSData object with PDF information.

个人理解是比如 PDF 渲染速度较慢,UIActivityViewController 会先查询 placeholder 显示出来。

第二个方法返回的就是实际要分享的内容,可以根据不同的 UIActivityType 返回不同类型的数据。比如拷贝到剪贴板返回字符串,分享到微信返回图片。如果分享的是一个自定义文件,返回文件的 URL 即可。

至于 UIActivityItemProvider,只是官方给出的一个实现了 UIActivityItemSource 协议的类。

实例化完毕之后,UIActivityViewController 还有一些属性需要设置:

  • excludedActivityTypes:设置你不想显示的分享类型。
  • popoverPresentationController?.sourceView:在 iPad 上显示成 popover 时需要设置
  • popoverPresentationController?.sourceRect:在 iPad 上显示成 popover 时需要设置
  • popoverPresentationController?.barButtonItem:在 iPad 上显示成 popover 时需要设置
  • completionWithItemsHandler:分享结束后的回调

最后 present 设置完毕的 UIActivityViewController。

至此,分享功能就介绍完了。

打开分享的文件

但是如果想用你的 app 打开某个文件,也就是出现在 UIActivityViewController 分享界面中,还需要做额外的一些工作。

在开始之前,你需要了解 UTI(Uniform Type Identifiers) 的概念。

简单来讲,UTI 是苹果定义的用来区分文件类型的标志符。同样是文本文件,不同文件的后缀名可能是 txt、text、html,但实际上一个纯文本编辑器都能打开它们。如果系统遇到一个陌生的文件后缀,也可以通过 UTI 来正确识别,并且知道哪些应用可以用来打开这个文件。

Conformance Hierarchy

可以看到 UTI 本身是个树形结构,类似于类的继承关系,所有的 UTI 都符合(Conforms To)public.data,可以理解成所有 UTI 最终都是 public.data 的子 UTI。

现在要做的就是在 Xcode 的 Info.plist 中的 Document Type 里添加要支持的 UTI,具体步骤参见官方文档。可以根据 Apple 已经定义好的 UTI 列表添加对应的 UTI。

这样,当用户分享特定文件时就可以看到你的 app 了,如何获取内容呢?

AppDelegate 有个方法:

1
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool

当系统通过你的 app 打开文件时会调用这个方法,其中的 url 就是文件的 URL,可以通过这个 URL 来获取文件内容。

自定义文件类型

如果你的 app 会生成自定义格式,那么你需要声明一个自定义的 UTI,Exported UTI 了解一下。

要声明一个自定义的 UTI 需要以下几个信息:

  • UTTypeIdentifier:自定义 UTI 的唯一标识符。
  • UTTypeTagSpecification:包含后缀名、对应的 MIME type 等信息。
  • UTTypeConformsTo:符合的已有 UTI,可以理解成父 UTI。
  • UTTypeIconFile(可选):文件显示的图标
  • UTTypeDescription:用来描述文件类型的字符串

要注意的是:

  • 要保证自定义 UTTypeIdentifier 的唯一性,可以使用

官方的一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>public.jpeg</string>
<key>UTTypeReferenceURL</key>
<string>http://www.w3.org/Graphics/JPEG/</string>
<key>UTTypeDescription</key>
<string>JPEG image</string>
<key>UTTypeIconFile</key>
<string>public.jpeg.icns</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.image</string>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>com.apple.ostype</key>
<string>JPEG</string>
<key>public.filename-extension</key>
<array>
<string>jpeg</string>
<string>jpg</string>
</array>
<key>public.mime-type</key>
<string>image/jpeg</string>
</dict>
</dict>
</array>

具体的添加方法,参见官方文档

参考

给鸡排饭加个蛋