今回はSwiftUIで画像を書き出して保存する方法について解説します。この記事では、UIImage
をドキュメントディレクトリに保存するための関数を実装し、JPEG、PNG、およびHEIC形式での保存方法について説明します。
1. モジュールをインポート
まずは必要なモジュールをインポートします。今回はSwiftUI
のみを使用します。
import SwiftUI
2. エラーハンドリング用の列挙型
次に、画像の保存時に発生する可能性のあるエラーを管理するための列挙型を定義し、エラーの種類を特定しやすくします。
enum ImageSaveError: Error {
case unableToConvertImage
case unsupportedFormat
}
3. 画像を保存する関数の定義
次に、画像を保存するための関数saveImageToDocumentsDirectory
を定義します。この関数は、画像と保存先のパス、フォーマットを受け取り、指定された形式で画像を保存します。
func saveImageToDocumentsDirectory(image: UIImage, path: URL, format: String, completion: @escaping (Result<Void, ImageSaveError>) -> Void) {
var data: Data?
switch format.uppercased() {
//指定された形式ごとに処理を実施します
}
}
パラメータの説明
image
: 保存したいUIImage
オブジェクトを指定します。path
: 保存先のURL
を指定します。format
: 保存する画像のフォーマット(”JPEG”, “PNG”, “HEIC”)をString
で指定します。completion
: 保存処理の結果を非同期に返すためのクロージャ。
3. JPEG形式での保存
JPEG形式の画像に変換し、data
変数に代入します。
case "JPEG":
data = image.jpegData(compressionQuality: 1.0)
compressionQuality
JPEGではcompressionQuality
を使用して、JPEG画像の圧縮率を指定します。値は0.0
から1.0
の範囲で、1.0
が最高品質、0.0
が最低品質を意味します。1.0
は圧縮なしを意味し、画像の品質が最も高くなりますが、ファイルサイズは大きくなります。
高品質の画像が必要な場合は1.0
を使用し、ファイルサイズを抑えたい場合は、品質を調整して低い値を指定することができます。
4. PNG形式での保存
次に、PNG形式で画像を変換する処理を追加します。
case "PNG":
data = image.pngData()
5. HEIC形式での保存
最後に、HEIC形式での画像の処理を追加します。HEIC形式への変換はiOS 17.0以上でサポートされています。ターゲットOSに合わせて記述を変更してください。iOS17.0以前でサポートするには、UIKitを使用して別のコードを記述する必要があります。
case "HEIC":
if #available(iOS 17.0, *) {
data = image.heicData()
} else {
completion(.failure(.unsupportedFormat))
return
}
6. サポートされないフォーマットが指定された時の処理
クロージャを呼び出し、エラーメッセージを伝えます。
default:
completion(.failure(.unsupportedFormat))
return
7. 画像の変換処理結果を確認
変換した画像データをimageData
変数に代入し、nil
などのエラーが発生しないことを確認します。
guard let imageData = data else {
completion(.failure(.unableToConvertImage))
return
}
8. ディレクトリへの保存
保存が成功したときは成功したことを、失敗した時はエラーをクロージャを通じて知らせます。
do {
try imageData.write(to: path)
print("Image saved to documents directory: \(path.absoluteString)")
completion(.success(()))
} catch {
print("Error saving image: \(error.localizedDescription)")
completion(.failure(.unableToConvertImage))
}
全体のコード
以上の部分をまとめると、以下のような関数ができます。
import SwiftUI
enum ImageSaveError: Error {
case unableToConvertImage
case unsupportedFormat
}
func saveImageToDocumentsDirectory(image: UIImage, path: URL, format: String, completion: @escaping (Result<Void, ImageSaveError>) -> Void) {
var data: Data?
switch format.uppercased() {
case "JPEG":
data = image.jpegData(compressionQuality: 1.0)
case "PNG":
data = image.pngData()
case "HEIC":
if #available(iOS 17.0, *) {
data = image.heicData()
} else {
completion(.failure(.unsupportedFormat))
return
}
default:
completion(.failure(.unsupportedFormat))
return
}
guard let imageData = data else {
completion(.failure(.unableToConvertImage))
return
}
do {
try imageData.write(to: path)
print("Image saved to documents directory: \(path.absoluteString)")
completion(.success(()))
} catch {
print("Error saving image: \(error.localizedDescription)")
completion(.failure(.unableToConvertImage))
}
}
Viewでの使用例
この例では、先ほど作成した関数を使用し、ボタンを押すと画像を保存する処理を行います。
import SwiftUI
struct ContentView: View {
@State private var selectedFormatIndex = 0
@State private var image: UIImage? = UIImage(named: "example")
@State private var saveResult: String = ""
let formats = ["JPEG", "PNG", "HEIC"]
var body: some View {
VStack {
if let image = image {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 200, height: 200)
}
Picker("Select Format", selection: $selectedFormatIndex) {
ForEach(0..<formats.count, id: \.self) { index in
Text(formats[index])
}
}
.pickerStyle(SegmentedPickerStyle())
.padding()
Button(action: {
if let image = image {
saveImage(image: image, format: formats[selectedFormatIndex])
}
}) {
Text("Save Image")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
Text(saveResult)
.padding()
}
}
func saveImage(image: UIImage, format: String) {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return
}
let fileName = "savedImage.\(format.lowercased())"
let fileURL = documentsDirectory.appendingPathComponent(fileName)
saveImageToDocumentsDirectory(image: image, path: fileURL, format: format) { result in
switch result {
case .success:
saveResult = "Image saved successfully!"
case .failure(let error):
switch error {
case .unableToConvertImage:
saveResult = "Unable to convert image."
case .unsupportedFormat:
saveResult = "Unsupported format."
}
}
}
}
}
#Preview {
ContentView()
}
ファイルAppからファイルを見つけられるようにする
ドキュメントディレクトリに保存したファイルを、ユーザーがファイルAppから開けるようにするには、Xcodeのinfo.plist
に以下の値を追加し、YES
に設定します。
UIFileSharingEnabled
(Application supports iTunes file sharing
)LSSupportsOpeningDocumentsInPlace
(Supports opening documents in place
)
まとめ
SwiftUIを使用して画像を指定したフォーマットで保存する方法を解説しました。実際のプロジェクトでこの関数を活用し、ユーザーが画像を保存できるようにしてみてください。
URLを変更することで、サブディレクトリやCachesディレクトリなどに保存することもできます。
サンプルプログラムは、MITライセンスのもとで公開しています。
以下の利用規約を参照の上、使用してください。
ソースコードの利用に関する規約