UIKit有一个分享app内容的大类,其设计上方便其他设备灵活连接。这个类就是UIActivityViewController,它可以用来共享文本,URLs,图像等等
这篇文章中我会举一些UIActivityViewController的完整案例,展示它能做些什么,供各位开发者参考
分享基本内容
我们从基础开始,UIActivityViewController是用户分享文章,URLs等内容的最简单方法,例如这样:
let items = ["This app is my favorite"]
let ac = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(ac, animated: true)
或这个分享URL
let items = [URL(string: "https://www.apple.com")!]
let ac = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(ac, animated: true)
最终结果会根据用户选择哪个分享服务器而不同。比如选了Twitter的话,文本和URL都能很好分享;但如果选了Facebook,就只能分享URL,因为Facebook不允许你为用户提供文本。
通过扩展数组,你可以把各项合并到一起
let items: [Any] = ["This app is my favorite", URL(string: "https://www.apple.com")!]
分享图像
分享图像的代码和其他的一样:
let items = [yourImage]
let ac = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(ac, animated: true)
然而默认的图像分享页面会让用户把图像保存到图像库。这看起来没问题,但是写入到图像库是一种受限操作——有可能导致app崩溃。
要修复这个问题,需要添加一段字符串来描述我们的意图。打开你项目的Info.plist文档,选择任意一项,点击+,之后选择关键词” Privacy - Photo Library Additions Usage Description”,并给它赋值” We need to import photos”,之后点击返回就可以了。
添加主题
如果你分享了一些文本,而用户选择用Mail app分享它,那么文本会被放到邮箱的内容部分,而主题部分是空的。
添加主题需要创建一个项目,并符合UIActivityItemSource协议。这个分享可能是一些ViewController触发的,但最好它是一个独立的项目。
假设如果想用ViewController,首先让它符合UIActivityItemSource:
class ViewController: UIViewController, UIActivityItemSource {
之后对你的项目数组使用self
let items = [self]
let ac = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(ac, animated: true)
最后让你的项目执行该协议下的2个所需方法:一个占位符项,用于你计划分享的内容;和该内容本身。
第一个方法用于让UIKit知道所要分享的数据类型。如果要分享的内容简单,就可以让它和第二个方法返回一样的数据,但如果数据大或复杂,你可以只返回一个dummy。关键是第一个方法必须返回和第二个同样的数据类型,比如一串字符
下面是2个返回字符方法的实现
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return "The pig is in the poke"
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType?) -> Any? {
return "The pig is in the poke"
}
现在我们的分享代码和直接分享文本达到的效果是一样的——这样是最好的。
不过我们选择可以通过UIActivityItemSource使用第三个方法,它能实现我们想要的:
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivityType?) -> String {
return "Secret message"
}
现在任何使用Mail分享我们内容的用户都会有标题和内容了。
根据目的地定制内容
除了添加主题外,我们可以根据目的地定制分享的内容。比如当分享到Twitter时候,可能会使用标签,和“via @yourUsername”字符串,而用其他平台时则使用笔记
使用之前提到的itemForActivityType方法就可以做到,但是这次我们来看看activityType参数。UIKit内置有很多东西,比如.postToFacebook, .mail, .saveToCameraRoll, 和.airDrop, 本例中我们来试试.postToTwitter
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType?) -> Any? {
if activityType == .postToTwitter {
return "Download #MyAwesomeApp via @twostraws."
} else {
return "Download MyAwesomeApp from TwoStraws."
}
}
添加定制动作
最后也是最重要的,我将介绍如何添加一个定制的app展示动作。之所以最后说它,是因为它最重要,如果你想实现这个功能,这是最简单的办法。
首先,你需要一张图作为图标,对于iPhone来说大小是60x60,对于iPad来说是76x76
第二,需要创建UIActivity的子类,并输入一些属性与方法。比较麻烦的是大部分属性是只读的,必须用override var和隐藏属性(即在真实属性可读之前用于保持数据的临时属性) 才能生效
那么就来点击Cmd+N创建一个新类,命名为“ExampleActivity”并让它成为UIActivity的子类。
之后赋予这个类以下4个属性:
var _activityTitle: String
var _activityImage: UIImage?
var activityItems = [Any]()
var action: ([Any]) -> Void
第一项用于分享的主题,第二项用于图像,第三项用于分享的内容,第四项是一个闭包,当用户选择我们的项目时候就会运行。这个闭包要接收Any数组,里面是activityItems的内容。
注意:之所以_activityTitle和_activityImage开头都是下划线,是因为它们隐藏了UIActivity的真实属性。我们无法直接设置它们,所以只能自定属性代替它们。
之后我们需要初始化,这会让我们更容易地从其他地方创建这个动作,它主要用于存储待分享的标题与图片,还有动作闭包。
往类中添加这些:
init(title: String, image: UIImage?, performAction: @escaping ([Any]) -> Void) {
_activityTitle = title
_activityImage = image
action = performAction
super.init()
}
我们需要覆盖4个属性,从activityTitle和activityImage开始,它们只需要返回隐藏属性:
override var activityTitle: String? {
return _activityTitle
}
override var activityImage: UIImage? {
return _activityImage
}
另外2个属性用于为iOS描述动作,我们给它一种唯一的动作类型,这样iOS可以追踪到被选择的动作,以及相应动作分类,来说明是动作行为(底端行)还是分享行为(顶端行)。就像你的app bundle,你的动作类型需要采用位置反转命名规范,例如com.hackingwithswift.project.shareURL。
之后添加这两个属性
override var activityType: UIActivityType? {
return UIActivityType(rawValue: "com.yoursite.yourapp.activity")
}
override class var activityCategory: UIActivityCategory {
return .action
}
最后我们需要覆盖这3个方法。第一个叫做canPerform(),用于声明你的动作是否能够处理它传递的项目。有时它仅仅允许展示图标,但是这里我们声明它支持所有类型:
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
return true
}
第二个方法叫做prepare(),用于传递用户选择的分享项目。我们只需在activityItems属性中保存它:
override func prepare(withActivityItems activityItems: [Any]) {
self.activityItems = activityItems
}
当用户选择我们的图标时,会调用最后一个方法。这里我们不需要做太多,因为我们已经有了可以调用的动作闭包。不过还是要通过activityDidFinish(true)让iOS知道我们的动作已经完成。
override func perform() {
action(activityItems)
activityDidFinish(true)
}
现在我们就可以把它添加到一个Activity View Controller中了
let customItem = ExampleActivity(title: "Tap me!", image: UIImage(named: "YourImageName")) { sharedItems in
guard let sharedStrings = sharedItems as? [String] else { return }
for string in sharedStrings {
print("Here's the string: /(string)")
}
}
let items = ["Hello, custom activity!"]
let ac = UIActivityViewController(activityItems: items, applicationActivities: [customItem])
ac.excludedActivityTypes = [.postToFacebook]
present(ac, animated: true)