【UIActivity】Twitter共有時にのみハッシュタグをつけたい!Instagramにも投稿したい!を実装してみた【Swift 3】

まだSwift3を使っています。Reoです。

UIActivityViewControllerを用いてシェアをするときに、

・Twitter共有では、ハッシュタグをつけたい!

・Instagramにも投稿したい!

と思い、これらを実装してみたので紹介しますー。

どちらも公式アプリが端末内にインストールされているのが前提となります。

 

画像とテキストをシェアする!

通常UIActivityViewControllerを用いてシェアするときは以下のようにします。

    //ボタンが押されたとき
    func btnTapped(sender:UIButton){
        let text = "シェアするテキスト"
        let image = UIImage(named:"test.jpg")
        let activityItem:[Any] = [text,image]
        let activityVC = UIActivityViewController(activityItems: activityItem, applicationActivities: nil)
        self.present(activityVC, animated: true, completion: nil)
    }

UIActivityViewControllerのactivityItemsにシェアしたいテキストや画像やURL等が含まれた配列を渡します。

こちらも何かの参考になれば(【Swift】UIActivityで画像を複数枚同時に共有するとき【メモ】)

 

Instagramにシェアしたい!

Instagramにシェアするときは「単一の画像」もしくは「動画URL」等でないと、選択できるリストに出てきません。

先ほどのコードでの実装だと、このActivitiesのリストに出てきてくれません。

手っ取り早く投稿できるようにするには「単一画像」または「動画URL」のみをActivityItemsに渡してやります

    //ボタンが押されたとき
    func btnTapped(sender:UIButton){
        let image = UIImage(named:"test.jpg")
        let activityItem:[Any] = [image]
        let activityVC = UIActivityViewController(activityItems: activityItem, applicationActivities: nil)
        self.present(activityVC, animated: true, completion: nil)
    }

これだとInstagramにも投稿できるようになります。
ちなみに動画は3秒以上でないとシェアできません。

 

でもこの場合、Twitterとかメールとか別のやつを選んだときにも「画像のみ」のシェアしかできません。
テキストも画像もシェアできるところでは、両方をシェアしたい。しかしこのUIActivityVC、開かれる前にアイテム渡しちゃうし、それに合わせてシェアできるものだけしか表示してくれない。(テキスト・画像を渡すとInstagramが表示されない。)

どうすればいいものか。色々試行錯誤して、ようやく実装できますた。ようやく本題です。

結局やりたいことは、
UIActivityVCが開かれたときには、「画像のみ」でシェアできるものを表示し、選択されたActivityがテキストも渡せるものなら、テキストと画像の両方を渡す
といったことです。

 

UIActivityItemSourceを用いる!

まず渡したいアイテムそれぞれにUIActivityItemSourceプロトコルに適合したNSObjectのサブクラスでを作ります。

//渡したいテキスト
class TextActivityItem:NSObject,UIActivityItemSource{

}

//渡したい画像
class ImageActivityItem:NSObject,UIActivityItemSource{

}

 

まずは画像の方から。

初期化時にUIImageを渡すようにしておきます。

class ImageActivityItem:NSObject,UIActivityItemSource{
    
    var image:UIImage?
    override init(){
        super.init()
    }
    convenience init(_ image:UIImage?){
        self.init()
        self.image = image
    }
    
}

次にプレースホルダーの設定をします。

    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return image ?? UIImage()
    }

これは仮のアイテムで、UIImageをシェアするつもりだよ!っていうのが伝わればおkなやつです。実際に渡すアイテムと型が違っても一応おk。

このactivityViewControllerPlaceholderItem(_:)が結構肝です。これによってどのActivityが表示されるかが決まります。

最後に実際に渡したい値をセット。

    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
        return image
    }

 

ImageActivityItemはこんな感じになりました。

class ImageActivityItem:NSObject,UIActivityItemSource{
    
    var image:UIImage?
    override init(){
        super.init()
    }
    convenience init(_ image:UIImage?){
        self.init()
        self.image = image
    }
    
    //実際に渡す
    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
        return image
    }
    
    //仮に渡す
    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return image ?? UIImage()
    }
    
}

 

テキストを渡す

やりたいことは画像の時とほぼ同じです。UIImageかStringかの差です。

class TextActivityItem:NSObject,UIActivityItemSource{
    
    var shareText = ""
    override init(){
        super.init()
    }
    convenience init(_ text:String){
        self.init()
        self.shareText = text
    }
    
    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
        return shareText
    }
    
    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        //ここではNSObjectを返しておく
        return NSObject()
    }
}

 

この時の1番のキモがここ!

    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        //ここではNSObjectを返しておく
        return NSObject()
    }

ここではString型を返さずNSObjectを返しておく!というのが大事になります。

 

実際にこれらを使うときは、

    //ボタンが押されたとき
    func btnTapped(sender:UIButton){
        let text = "シェアするテキスト"
        let image = UIImage(named:"test.jpg")
        let activityItem:[Any] = [TextActivityItem(text),ImageActivityItem(image)]
        let activityVC = UIActivityViewController(activityItems: activityItem, applicationActivities: nil)
        self.present(activityVC, animated: true, completion: nil)
    }

のようにして配列に入れて渡してやります。

activityViewControllerPlaceholderItem(_:)を設定がここで活用されます。

最初の方法(text,imageをそのまま渡す方法)だと、「String型とUIImage型」をシェアするためのActivityアイコンが並びます。

しかしこの方法では、TextActivityItemのところでPlaceholderItemにNSObject()を渡しているので、「NSObject型とUIImage型」をシェアするためのアイコンが並びます。

このおかげでString型を含んでいると出てこないはずのInstagramのアイコンも出てくるようになります。∩(〃・ω・〃)∩ ばんじゃーい

 

そして実際にActivityアイコンを選択した後に、activityViewController(_:itemForActivityType:)が呼び出され、実際に必要なモノだけを自動的に選んでシェアします。(Instagramには画像のみが渡されます。テキストは渡されません。)

 

Twitterへのシェア時にのみハッシュタグをつけよう!

Twitterへのシェアの時にだけハッシュタグをつけたい!という場合には、先ほど作ったTextActivityItemのactivityViewController(_:itemForActivityType:)で分岐してやります。

    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
        //Twitter投稿時のみハッシュタグをつける
        if activityType == .postToTwitter {
            return shareText + "#ハッシュタグ"
        }
        return shareText
    }

 

TextActivityItemの全体はこんな感じになりました。

class TextActivityItem:NSObject,UIActivityItemSource{
    
    var shareText = ""
    override init(){
        super.init()
    }
    convenience init(_ text:String){
        self.init()
        self.shareText = text
    }
    
    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
        if activityType == .postToTwitter {
            return shareText + "#ハッシュタグ"
        }
        return shareText
    }
    
    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        //ここではNSObjectを返しておく
        return NSObject()
    }
}

 

 

おまけ。

Instagramのときだけ画像を加工したいとき

Twitterへの投稿にはactivityTypeに .postToTwitterというのが用意されていますが、Instagramでは用意されてません。ので以下のように分岐できます。

    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
        if activityType.rawValue == "com.burbn.instagram.shareextension"{
            //例えばここで真四角に切り取り処理をするとか。
            return cropImage(image: image!, contentSize: CGSize(width:600,height:600))
        }
        return image
    }

こんな感じでやってみても良いかも。

 

UIActivity関連これはうまくいったんですが、Facebookへの投稿だけ開いて少しすると勝手に閉じるし、謎エラーもあって結構困る・・・・。

それではでは。

 

2018/04/14追記 Gistにあげときました。
一応gistにあげました。

でも自分はもう使ってない手法。
TwitterKit等々を頑張って使ってます。
コメントは認証制です。詳しくは下記の注意をお読みください。お気軽にコメントお願いします!

Write a Comment

コメント時の注意

「Twitter」「Facebook」「Google+」「WordPress」のいずれかのアカウントをお持ちの方は各アカウントと連携することでコメントできます。 コメントしたことはSNSに流れませんので、アカウントをお持ちの方はこちらの方法でコメントを投稿して下さると嬉しいです。 アカウントをお持ちでない方はメールアドレスで投稿することができます。 初回コメント時は承認後に表示されます。

Related Memo...

記事を書くほどでもないけれどメモっておきたいこと

テスト投稿。

例えばiphone7 の画面サイズ

750 × 1334
半分375 × 667

iOS

UITableView.RowAnimation の .none はアニメーションするよ

UITableView.RowAnimation の .none はアニメーションがnoneなわけじゃなく、デフォルトの設定を使うよという意味らしい。

The inserted or deleted rows use the default animations.

なのでアニメーションしちゃう。今更の気づき。

 

iOS

UINavigationController + UIScrollView の組み合わせで使っている時に謎の余白ができる時

UINavigationController + UIScrollView の組み合わせで使っていて、UIScrollView 上に AutoLayout で上下左右0で View を設置しているのに、30px程度上にずれてしまうとき。

`navigationController.navigationBar.isTranslucent = false` にすると直るかもしれない。

ScrollView上のコンテンツとNavigationBarの重なっているところが透過していたら多分これで直せるはず。

通常のターゲットではちゃんと動いているのに、iOSSnapshotTestCase を用いたテストでだけこの対応が必要なのよくわからないけれど。。。

iOS
more