目次
- ShareLinkについて
- ShareLinkに渡す画像の型
- 「画像を保存」アクションを表示させる方法
- おまけ(補足)
- 再現可能なコード全部
ShareLinkについて
ShareLinkはiOS16以上で使用可能なSwiftUIのAPIです。いわゆる「共有シート」を簡単に表示することができます。UIKitのUIActivityViewControllerでは、表示や非表示をiPhone・iPadに応じて制御する必要があり少々手間がかかりますが、ShareLinkは1行で共有シートを簡単に表示できます。
今回はShareLinkにおける画像の共有に絞って解説します。
ShareLinkに渡す画像の型
1. URLによる共有
メモリ上の画像ではなく、ドキュメントディレクトリやアプリのプロジェクトに追加された画像など、ディスクに保存されている画像に対してはURLを渡すことで共有が可能です。
2. メモリ上の画像の共有
共有する画像がImage
型やUIImage
型などメモリ上にある場合の手順です。このケースは公式ドキュメントにはあまり詳しく記載されていません。
まず、前提としてShareLinkに渡すitem
(共有する対象物)はTransferable
型に準拠している必要があります。
ここで注意すべき点は、UIImage
はTransferable
型に準拠していないため、直接渡すことができません。Xcodeでitem
にUIImage
を渡すと以下のエラーが発生します。
Generic struct 'ShareLink' requires that 'UIImage' conform to 'Transferable'
一方で、SwiftUIのImage
型はTransferable
型に準拠しているため、そのまま画像を共有することができます。Image(uiImage:)
としてUIImage
を渡すことで、画像を簡単に共有することができます。
ただし、UIImage
を変換せずとも共有する方法もあります。UIImage
自体をTransferable
に準拠させることが可能です。
extension UIImage: Transferable {
public static var transferRepresentation: some TransferRepresentation {
DataRepresentation(exportedContentType: .png) { image in
if let pngData = image.pngData() {
return pngData
} else {
throw ConversionError.failedToConvertToPNG
}
}
}
enum ConversionError: Error {
case failedToConvertToPNG
}
}
この拡張により、UIImage
がドラッグ&ドロップや共有など、転送時にはPNG形式に変換されるようになります。JPEGやHEIFへの変換も可能で、カスタマイズ性が高いです(Image
型の場合、デフォルトでPNGに変換されます)。この方法により、UIImage
型をそのままShareLink
に渡すことが可能です。
AppleのドキュメントではカスタムなTransferable
型(Image + String
)の定義が紹介されていますが、画像の共有だけであれば前述の方法が最も簡単です。
1. URLによる共有と2. メモリ上の画像の共有の違い
URLで共有する場合、ファイル名、ファイル形式、ファイルサイズが表示されるため、プレビューの設定は不要です。しかし、メモリ上の画像の場合はカスタムプレビューの設定が必要です。
1. URLによる共有 | 2. メモリ上の画像の共有 |
---|---|
「画像を保存」アクションを表示させる方法
前述の方法により画像の共有は可能ですが、実機やシミュレータで確認すると、「画像を保存」アクションが表示されないことがあります。コピーやプリント、共有アルバムへの追加などのアクションは表示されますが、画像保存のアクションが欠けている場合があります。
多くのユーザーは、画像の共有ボタンを使ってSNSへの転送や端末への保存を期待しているため、この問題は重要です。これはURL
型、Image
型どちらのケースでも発生します。
解決方法
この問題は写真アプリへのアクセス許可が原因です。アプリのInfo.plist
にPrivacy - Photo Library Additions Usage Description
を追加することで、「画像を保存」アクションが表示されるようになります。
Info.plist Photo Library Add Usage Documentation
画像を保存アクション
この設定で指定する文字列は、初めて「画像を保存」アクションを実行した際に表示されるアラート文言です。
カスタムのImagePicker
などを使用していて、既にPrivacy - Photo Library Usage Description
を追加している場合はこの問題は発生しないはずです。
Info.plist Photo Library Usage Documentation
最近では、許可が不要でプライベートアクセスが可能なPhotosPicker
の利用が増えていますが、アクセス許可の設定を確認することが重要だと思います。
おまけ(補足)
補足として、SwiftUIのnavigationDocument
を紹介します。これもShareLink
と同様にTransferable
型を渡すことで、インラインスタイルのnavigationTitle
がボタンに変わり、共有オプションが表示されます。これを使うことでShareLink
と同等の機能を提供できます(ネイティブなファイルアプリに実装されています)。
再現可能なコード全文
import SwiftUI
struct TestView: View
let imageFromURL = Bundle.main.url(forResource: "logo.png", withExtension: nil)!
let imageFromAssets = Image("logo")
let uiImageFromAssets = UIImage(imageLiteralResourceName: "logo")
var body: some View {
NavigationStack{
VStack(spacing: 20) {
ShareLink(
"URLから共有",
item: imageFromURL
)
ShareLink(
"Assetsから共有(UIImage型)",
item: uiImageFromAssets,
preview: SharePreview(
"添付画像",
image: uiImageFromAssets
)
)
ShareLink(
"Assetsから共有(Image型)",
item: imageFromAssets,
preview: SharePreview(
"添付画像",
image: imageFromAssets
)
)
Spacer()
}
.navigationTitle("Title")
.navigationBarTitleDisplayMode(.inline)
.navigationDocument(imageFromURL)
}
}
}
extension UIImage: Transferable {
public static var transferRepresentation: some TransferRepresentation {
DataRepresentation(exportedContentType: .png) { image in
if let pngData = image.pngData() {
return pngData
} else {
throw ConversionError.failedToConvertToPNG
}
}
}
enum ConversionError: Error {
case failedToConvertToPNG
}
}
ここまでご覧いただきありがとうございました。間違い等ありましたらコメントにてご指摘お願いします。
個人的には、ShareLinkでUIActivityViewControllerのようにカスタムアクションを設定できるようになると良いなと思っているところです...