杏z学習帳

Swift、Unityなどを中心に節操なく手を出してますまる

iOS13のUIActivityViewControllerのプレビュー画面の挙動が実装方法によって異なる(2019/11/24)

[Xcode11]iOS13のUIActivityViewControllerのプレビュー画面の挙動が実装方法によって異なる

ざっくりまとめ。

iOS 13から UIActivityViewController が少し変わって、プレビュー画面ができていい感じになったと。
確認してみると意外なところで挙動に差異が生じるのを発見しました(笑)
どうやら UIActivityViewControllerString などを直接渡していると場合と、
UIActivityItemSource プロトコルに準拠した物を渡している場合で挙動がかわります。
iOS 13 からは LPLinkMetadata をつかってプレビュー画面のカスタマイズができるようになるので、後者の方法で実装しておくといいかもしれません。

環境

問題

iOS 13 から UIActivityViewController をつかって何かしらシェアする時のプレビュー画面がいい感じになった(リッチになった?)という話をきいて
いくつかアプリを確認してみるとなにか挙動が違うアプリがあったのです。

テキストとURLで同じようなセットで利用しているにも関わらず、
とあるプリはテキストとテキストアイコンのようなもの表示している
また別のものでは、URLのOGPからの情報を表示している
という感じで同じ情報を渡しているにもかかわらず挙動に違いがあると。。。

テキストアイコンとテキストが表示される場合

Stringとか直接渡した場合のシェア

OGPが表示されてる場合

UIActivityItemSourceを渡した場合のシェア

どっちも渡しているとのはStringとURLなのだけれど。

対応

どちらもStringとURLを設定しているにもかかわらずなぜ挙動が違うのだろう?🤔
見比べたら違いは一目瞭然でした(笑)

どうやら、UIActiivtyItemSource に準拠したものをアイテムとして渡している場合と String などを直接渡している場合で挙動が変わってくるようでした。

class ViewController {
    func share() {
        let shareText = "シェアてきすとだよー"
        let shareUrl = URL(string: "https://blog.anzfactory.xyz")!
        let activityVC = UIActivityViewController(activityItems: [shareText, shareUrl], applicationActivities: nil)
        present(activityVC, animated: true, completion: nil)
    }
}

こういう感じで String なんかを直接渡しているとテキストアイコンとテキストが表示されるパターンになります。
そして下記のように UIActiivtyItemSource に準拠したものを渡すとOGPからの情報が表示されるパターンになります。

class ViewController {
    func share() {
        let shareText = ShareItem("シェアてきすとだよー")
        let shareUrl = ShareItem(URL(string: "https://blog.anzfactory.xyz")!)
        let activityVC = UIActivityViewController(activityItems: [shareText, shareUrl], applicationActivities: nil)
        present(activityVC, animated: true, completion: nil)
    }
}

class ShareItem<T>: NSObject, UIActivityItemSource {
    
    private let item: T
    
    init(_ item: T) {
        self.item = item
    }
    
    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return item
    }
    
    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
        return activityViewControllerPlaceholderItem(activityViewController)
    }
}

補足

この挙動に気づいたことは実は別にやりたいことがあったからで、
iOS 13からプレビュー画面ができてそれが編集可能だというのをしってそれで遊んでみようとごにょごにょしていたところ気づいたというわけです。

上記の例だとOGPの情報を良い感じに表示していますが、これらを変更できるというわけです。

class ShareItem<T>: NSObject, UIActivityItemSource {
    
    private let item: T
    
    init(_ item: T) {
        self.item = item
    }
    
    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return item
    }
    
    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
        return activityViewControllerPlaceholderItem(activityViewController)
    }
    
    @available(iOS 13.0, *)
    func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {

        let metadata = LPLinkMetadata()
        metadata.title = "シェアする!"
        return metadata
    }
}

こう書いておくと...こうなります。

LPLinkMetadataをつかった場合のシェア

ちなみに表示されている画像はアプリアイコンです(こちらも指定可能です)

LPLinkMetadata は結構癖が強い気がします。
OGP が展開された時のようにサブタイトルのとこに何か指定したいのだけれどできない(originalURL に設定したら表示はされるけど URL に限定されてしまう)とか
Item に紐づくので今回のように複数アイテム渡す場合、もしアイテムごとにことなる情報を持つ metadata を返した時の挙動は確証がもてないとか

参考