今回は写真 App で選択した写真を、自分のアプリに共有できるまでをなぞります。
その際の画面は、自分で作成した画面を出すようにします ✧٩(ˊωˋ*)و✧
アプリにもよると思いますが、私の周りでは意外と需要があるようで、仕事でもそのような実装をお願いされたり、自分のアプリでも問い合わせがあります。
実装する機会が少なそうなので、方法を載せながらやっていきたいと思います!
開発環境(前提条件)
・Xcode12.5((21年05月16日再確認済み)
・Swift5
・Storyboard を使います(SwiftUI は使いません)
実装方法
ShareExtension を使うための設定
File → New → Target を選択すると、以下の画面になるので、Share Extension を選択。
プロジェクト名をテキトーに入力してください。(ここでは MyProject-ShareExtension とします)
ターゲットの作成が完了したら、写真 App と自分のアプリ間でファイル共有をするために App Gourps という仕組みを利用するので、以下の手順で設定してください。
TARGETS → MyProject → Signing & Capabilities → + Capability → App Groups
App Group に作成された group.bundleid にチェックを入れましょう!
そして同じ手順で MyProject の部分を MyProject-ShareExtension に変えて同じ操作をします。
TARGETS → MyProject-ShareExtension → Signing & Capabilities → + Capability → App Groups
これで共有できるようになったので、コードに戻ります。
画面を作成する
再執筆時では、ターゲット作成時に ShareViewController.swift と MainInterface.storyboard 、Info.plist というファイルが自動で生成されています。
ShareViewController.swift を開くと、以下のようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import UIKit import Social class ShareViewController: SLComposeServiceViewController { override func isContentValid() -> Bool { // Do validation of contentText and/or NSExtensionContext attachments here return true } override func didSelectPost() { // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments. // Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context. self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil) } override func configurationItems() -> [Any]! { // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here. return [] } } |
SLComposeServiceViewController を継承していますが、このクラスを画像検索してみてください。
ザ・投稿!みたいなアラートが表示された画像が出てくると思います。
冒頭でも述べたように、今回は自分で作成した画面を出したいので、override を全て削除し、継承するクラスを UIViewController に変更します。
1 2 3 4 5 6 7 |
import UIKit class ShareViewController: UIViewController { override func viewDidLoad() {} } |
UITableView を使ってもいいし UICollectionView を使ってもいいので、お好きな UI を作成してオリジナルの画面を作成しましょう!(画面作成は省略します)
選択された画像を取得する
とりあえず取得できるコードを載せます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import UIKit import MobileCoreServices class ShareViewController: UIViewController { override func viewDidLoad() { guard let inputItem = self.extensionContext?.inputItems.first as? NSExtensionItem, let attachments = inputItem.attachments else { return } let identifier = String(kUTTypeImage) let imgAttachments = attachments.filter { $0.hasItemConformingToTypeIdentifier(identifier) } for itemProvider in imgAttachments { itemProvider.loadItem(forTypeIdentifier: identifier, options: nil) { item, error in do { if let error = error { throw error } else if let url = item as? URL { let data = try Data(contentsOf: url) let image = UIImage(data: data) } } catch { } } } } } |
このままじゃ使わねーよ感のあるコードですね!
まぁ僕もこのままは使ってないですけど。笑
表示するタイミングとかはよしなにしてください。
元のアプリに戻る設定をする
自作画面なので、元のアプリに戻るコードも自分で実装する必要があります。
昔は UIApplication の shared にアクセスして open(_:options:completionHandler:) が使えたんですけど、今は使えないので別の方法で実装するしかないです。
1 2 3 4 5 6 7 8 9 10 |
func backToApp() { self.extensionContext?.completeRequest(returningItems: nil, completionHandler: { _ in let selector = sel_registerName("openURL:") var responder = self as UIResponder? while let r = responder, !r.responds(to: selector) { responder = r.next } _ = responder?.perform(selector, with: URL.schemeURL) }) } |
無理矢理感ありますけど、これ以外に戻る方法が浮かびませんでしたー!
何かいい方法があれば教えてください。
さいごに
なんかもっと実装が簡単になってほしい。笑
今あるメソッドが廃止になるのは仕方ないけど、ちゃんと代わりのやつ提供してほしい _(꒪ཀ꒪」∠)_
とりあえず共有はできたのでよしとします!!!
コメント