Share Extension 使用户在使用其他的app 的时候, 更加方便的将其内容分享出去,像是社会化分享还有上传服务器。比如说, 在一个 app 中有个分享按钮, 用户可以选择其中一个 Share Extension 来发表评论或者内容。
写在前面的话
最好的 Share Extension 能够让用户能够很轻松的分享网页中的内容。如果你需要用一个扩展来让用户使用这些内容做一些其他的操作, 或者为用户提供他们所关心的内容的更新, Share Extension 可能就不是最好的解决方案了。
如何理解 Share Extension
Share Extension 有以下几个特点:
- 让用户更容易分享内容。
- 如果可以的话,能够让用户预览,编辑,标注,并且自定义内容。
- 在用户发送内容的时候,能够确保内容是合法的。
用户能够通过系统提供的 UI 来获得他能够使用的 Share Extension。在 iOS 中, 用户点击分享按钮,然后从系统弹出来的分享区域中选择一个 Share Extension。
当用户选择了你的 Share Extension 之后,你需要展示一个包含了内容的视图,然后发表出去。你可以将你的视图机遇系统提供的 vc, 或者自定义一个。系统提供的那个提供了一些很常见的操作,比如说,预览,合法性判断,同步内容,以及视图的动画,还有设置发布。
创建 Share Extension
创建的过程类似于之前写的 面向 Extension 开发 🌞 Today Extension
唯一不同的是 Today Extension 有唯一的一个 宿主 app 而 Share Extension 在使用的时候, 可能有很多的宿主 app 所以在运行的时候,需要选择一个宿主 app。 一般都是选择的 Safari 然后,随便打开一个网页,下面的分享按钮就可用了,点击之后,在分享列表里面就能够看到你的 app 咯。
需要注意的是,这个时候看到的 Share Extension 的名称是你 Share Extension 的名称,这个是可以更app 名称不一样的。只要改 Share Extension 的 info.plist 中的 Bundle display name 为你想要的名称就可以了。
这篇文章要做什么?
写到这里, 基本上已经完成了准备工作了。可能还有 创建 app groups 之类的工作,这块将在下面的内容中介绍。花了几天时间断断续续的研究 Share Extension
,对比了系统中本来就存在的facebook twitter 以及国内的微博什么的。我将在本文中模仿着做一个类似的效果出来。
这是最终效果的 gif 图。这只是第一步。好了,我们开始吧。
基本设置
|
|
如注释所见,这里设置了placeholder 已经右下角的数字。
|
|
这段代码用来验证用户输入的内容是否合法。这里我只是简单的设置了内容的长度不能超过140,并且不能小于2.
系统在SLComposeServiceViewController
中提供了open func didSelectPost()
、open func didSelectCancel()
两个方法分别是上面两个按钮的事件。
需要注意的是,重写 cancel 的时候,需要调用 super
接下来是设置位置,分组这些内容。这写也是在系统的api 中能找到对应的方法。
|
|
这个方法返回了一个数组,就是对应的按钮等内容。每个按钮其实也很简单。只有 title
,value
, tapHandler
, valuePending
四个属性。
- title: 左边的文字
- value: 右边的文字
- tapHandler: 处理这个 item 事件的 closure
- valuePending: 左边转菊花的indicator,是一个 bool 类型的属性。
在上面的代码里,我用 self.pushConfigurationViewController(list)
这行代码push 到了另外的界面,用来让用户选择他们要把消息分享到的具体分组。这个操作是在 Facebook 的 share extension 中看见的。在实际中,我们也可以这样做其他很多的事情。
需要注意的是,推出来的 Controller 需要设置背景为clear,cell 也要设置背景为 clear 这是为了保证界面跟系统统一(模糊效果)。
然后就是要把用户选择的内容分享出去了。
通过 Share Extension 分享内容
要将内容分享出去,需要解决几个问题。
- 用户信息
- 获取分享的内容
因为 App Extension 和主 App 是两个不同的 Target, 这就需要我们在这个获取到主 app 中用户的登录信息。至少需要知道我们要把内容分享到哪个用户的数据流中吧。
这个其实也是很简单的事情。在 Today 中我们已经知道了 App Groups 这个东西。也知道了如何共享部分代码。
所以在 Share Extension 中
|
|
然后在主app 中
|
|
就实现了数据之间的交换。到这儿,可能会想到另外一个问题。如果没有登录的话需要跳转到主 app 中进行登录操作。这里也没有什么问题通过 openurl 就可以。
- 设置主app 的url type
- 跳转
所以我在 viewDidload
方法中添加了以下代码
|
|
判断登录状态,然后弹窗。取消或者去登录。如果选择去登录的话,就通过 openUrl 去打开主 app。
很完美吧!but it doesn’t work!!!, 我在 stackoverflow 上找到了些资料。
苹果爸爸只允许 Today Extension 通过 extensionContext 的 openUrl 打开主app
但是这个需求总是需要实现的。其实还是有解决方法。
方法一: 在 Extension 中实现登录操作
这个确实没什么好说的。也是弹出一个 alert,然后输入用户名,密码,登录。完成所有操作。或者是其他什么方案,都可以。这个就不再详细描述了。Share Extension 来实现登录行为,然后 主 app 也能够共享等了状态。这仿佛也是解决了这种问题。
当然,强迫症笔者,还是想通过打开主 app 的方法来解决这个问题。
方法二: 另类的 openUrl
|
|
当然,上面的两个链接还有一些其他的方法,就不一一列举了。
解决了最开始的用户信息的问题。接下来就是要获取分享的内容这个问题了。在ShareExtension 中,相信已经看见了。需要两个东西,第一个是用户关于这个内容的评论,以及这个内容本身(url、照片等)。关于用户对内容的评论这点其实很简单。
用户评论
|
|
系统提供的这个 api 就能够解决这个问题。
附件内容
暂且叫做附件内容吧!我也不知道应该怎么叫。这个东西,我们还是看看 extensionContext
这个东西吧!
NSExtensionContext
这个类一共暴露了四个api出来。我们看第一个
|
|
看样子就是这个了。
看注释内容,突然感觉,apple 的api 也有设计的不是很好的地方,既然注释都明确说了 NSExtensionItems 数组应该不是 Any 的吧😂
既然这样, 我们再看看 NSExtensionItem
这个类吧!
|
|
注释太复杂了,整理成一个表格就是这样的:
Properties | Description |
---|---|
attributedTitle | 标题 optional |
attributedContentText | 内容 optional |
attachments | 所有的附件NSItemProvider 组成一个数组 optional |
userInfo | 一个key-value结构的数据。NSExtensionItem中的属性都会在这个属性中一一映射。注释中讲到的在 info.plist 中要设置的部分会在后面提到 |
下面的表格就是 userInfo
中的 key :
名称 | 说明 |
---|---|
NSExtensionItemAttributedTitleKey | 标题 的键名 |
NSExtensionItemAttributedContentTextKey | 内容 的键名 |
NSExtensionItemAttachmentsKey | 附件 的键名 |
上面又提到了 NSItemProvider
这个东西。这相必须就是我们需要的附件了吧!
Api | description |
---|---|
initWithItem:typeIdentifier: | 初始化方法,item为附件的数据,typeIdentifier是附件对应的类型标识,对应UTI的描述。 |
initWithContentsOfURL: | 根据制定的文件路径来初始化。 |
registerItemForTypeIdentifier:loadHandler: | 为一种资源类型自定义加载过程。这个方法主要针对自定义资源使用,例如自己定义的类或者文件格式等。当调用loadItemForTypeIdentifier:options:completionHandler:方法时就会触发定义的加载过程。 |
hasItemConformingToTypeIdentifier: | 用于判断是否有typeIdentifier(UTI)所指定的资源存在。存在则返回YES,否则返回NO。该方法结合loadItemForTypeIdentifier:options:completionHandler:使用。 |
loadItemForTypeIdentifier:options:completionHandler: | 加载typeIdentifier指定的资源。加载是一个异步过程,加载完成后会触发completionHandler。 |
loadPreviewImageWithOptions:completionHandler: | 加载资源的预览图片。 |
这时候看看整体的结构:(这个图是在看到的)
到这里,应该已经知道了应该怎么做了吧!
|
|
代码中分别是分享网页和图片两个东西。这一步解决了找到分享的内容的代码。
具体分享的行为可以有两个办法来解决
- 将需要分享的内容功过 apps group 保存,然后在打开主 app 的时候,在主 app 中取出然后发送给sever。
- 直接在 Share Extension 中分享。
这个过程就不再叙述了。
info.plist
既然说到了 info.plist 中的设置,就再看看这部分是说的什么吧!都是一些很固定的内容,我随便挑两个说说吧!
Key | Description |
---|---|
NSExtensionActivationSupportsAttachmentsWithMaxCount | 附件最大个数 |
NSExtensionActivationSupportsAttachmentsWithMinCount | 附件最小个数 |
NSExtensionActivationSupportsFileWithMaxCount | 附件种类限制 |
NSExtensionActivationSupportsMovieWithMaxCount | 视频个数限制 |
NSExtensionActivationSupportsImageWithMaxCount | 图片个数限制 |
NSExtensionActivationSupportsText | 是否支持文本类型 |
NSExtensionActivationSupportsWebURLWithMaxCount | web 链接最多限制 |
NSExtensionActivationSupportsWebPageWithMaxCount | web 页面最多限制 |
如果要设置你的 extension 只支持图片,url 什么的。只需要把个数限制写成 0!
但是设置的时候需要注意是将NSExtensionActivationRule 改成 Dictionary 类型并添加:
- NSExtensionActivationSupportsAttachmentsWithMaxCount
- NSExtensionActivationSupportsAttachmentsWithMinCount
- NSExtensionActivationSupportsImageWithMaxCount
- NSExtensionActivationSupportsMovieWithMaxCount
- NSExtensionActivationSupportsWebPageWithMaxCount
- NSExtensionActivationSupportsWebURLWithMaxCount
这就基本上完成了,我们要在 系统或者 外部 app 中将内容分享到我们自己的 app 中。这好像还是有很大的限制。毕竟如果我们的产品不是像微博qq这样的社交app 的话,这个东西就没什么作用了。
另外注意这个警告
在自己的app 中调起 Share Extension
|
|
当然还有 UIActivityViewControllerCompletionHandler
这个东西,来回调分享的结果。
另外一种方法可以直接调起某个系统的分享。
|
|
这种方式有一个缺陷,就是,这样的分享只能对系统的分享,微信什么的就不能这么做了。
最后的话
Share Extension 写到这里就差不多了。初步的入门步骤也已经完成了。最后,我看了一下,微信的 Share Extension 做的事情,感觉用他还能做很多的事情。这个也需要在开发中根据实际需求去拓展了,另外还有自定义 UI 等,也是很简单的事情。只是用自己 UIViewController 就好了。这个就不再详细的说了。到此,我能想到的功能,就基本上完成了。如果有更多需求也可以跟我讨论。