转载

UIActivityViewController的使用讲解

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 ViewControllerUIViewControllerUIActivityItemSource {

之后对你的项目数组使用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 activityCategoryUIActivityCategory {
    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)
正文到此结束
Loading...