【お知らせ】
SwiftUIで作った macOS Todo アプリ
ToDone
を100ダウンロードまで無料にしました。マニュアルページは、ToDone サポートページ です。
【本文】
Xcode で macOS の Document App テンプレートを使って画像ビューワを作って見ようと思っています。前回のテキストエディタと違い、今回は難しいだろうなと思っています。
私の開発環境は次のとおりです。
更新履歴
2022/07/24 Xcode での Info.plist の設定を追加しました。
2022/07/25 コード説明をつけました。まだまだ雑ですが、徐々に改訂していこうと思っています。
2022/07/26 コード説明を少し改訂しました。
Document App テンプレートは、ドキュメント・ベースド・アプリケーション(Document-Based Application)を作るテンプレートです。
ドキュメント・ベースド・アプリケーションとは、 内容の違う複数のデータ(ドキュメント)を同時に開くことのできるアプリケーションです。それぞれぞれのデータは編集することもでき、保存することもできます。また新規にデータ(ドキュメント)を作ることもできます。開くデータの場所や、新規データの保存場所は SandBox にとらわれることがなく、お使いのマックの自由な場所に保存できます。
Xdoeを起動して表示される Welcome to Xcode 画面で Create a new Xcode Project をクリックするか、File メニュー → New → Project... を選びます。
次の画面で macOS の Document App テンプレートを選び Next をクリックします。
次の画面で次のように設定して Next をクリックします。
次の画面で保存場所を決めて、Create をクリックします。 私の場合は、Create Git repository on may Mac のチェックは外しています。
Xcode では、プロダクト名App.swift です。
import SwiftUI
@main
struct BarApp: App {
var body: some Scene {
DocumentGroup(viewing: BarDocument.self) { file in
ContentView(document: file.$document)
}
}
}
DocumentGroup(newDocument: BarDocument()) {
ContentView(document: $0.$document)
}
// もしくは、
DocumentGroup(newDocument: BarDocument()) { file in
ContentView(document: file.$document)
}
編集と閲覧の両方ができるようにするとオープンダイアログに「New Document」ボタンが追加され、「File」メニューの「New」や「Save」などが使えるようになります。ただし今回の場合は、編集可能にしても、「New」や「Save」を選んでも、実際にはドキュメントは変更されません。
Xcode では、プロダクト名Document.swift です。
import SwiftUI
import UniformTypeIdentifiers
struct BarDocument: FileDocument {
var image: NSImage
/*
init(image: NSImage = NSImage()) {
self.image = image
}
*/
static var readableContentTypes: [UTType] { [
UTType(importedAs: "public.png"),
UTType(importedAs: "public.jpeg")
] }
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let image = NSImage(data: data)
else {
throw CocoaError(.fileReadCorruptFile)
}
self.image = image
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
throw CocoaError(.fileWriteUnknown)
}
}
必須項目 | 役割 |
---|---|
init(configuration:) | ファイルの読み込みを担当 |
readableContentTypes: | ドキュメントが開くことができるタイプを設定 |
fileWrapper(configuration:) | ファイルの保存を担当 |
writableContentTypes: | ドキュメンが保存できるタイプを設定 ※ただしデフォルトで実装されるので、実際の記述は不要 |
Xcode では、ContentView.swift です。
import SwiftUI
struct ContentView: View {
@Binding var document: BarDocument
var body: some View {
ScrollView([.horizontal, .vertical]) {
Image(nsImage: document.image)
}
}
}
/*
struct ContentView_Previews: PreviewProvider で始まる、
プレビュー用のコードは削除します。
*/
Product メニューの Run をクリックするか、Xcode の左上の右三角 ▶︎ をクリックします。⌘ + R でも OK です。しばらく待つとアプリケーションが起動します。
ドキュメントによっては、Info.plist に Jpeg と Png に対応するための項目を設定しなければならないと書かれていますが、Info.plist に新たな設定を加えなくても動作します。SwiftUI はどんどん進化しているのかもしれませんし、画像の閲覧だけじゃなく、画像に変更を加える場合などは Info.plist の設定も必要なのかもしれません。
Xcode に次のような注意が出ました。
Type "public.png" was expected to be declared and imported in the Info.plist of Bar.app, but it was not found.
タイプ「public.png」は、Bar.appのInfo.plistで宣言およびインポートされることが期待されていましたが、見つかりませんでした。
やっぱり Info.plist の設定もしたほうが良いのですね。
Xcode 上で Info.plist を表示します。初期設定では次のようになっています。
Document Types には、Document App テンプレートを使った場合、デフォルトで com.example.plain-text が書かれていますが、それを書き換えて、さらにもう一つ増やして、合計二つになります。
Exported Type Identifiers は、アプリ独自のタイプを作った場合に設定します。今回は設定しません。Imported Type Identifiers は、既存のタイプを使った場合に設定します。 デフォルトで設定してある Example Text を書き換えて PNG を設定して、もう一つ JPEG を設定して、合計二つ設定します。
以上で Xcode に表示されていた注意(警告?)が消えます。
上記の三つのファイルを任意のテキストエディタで記述してください。ファイル名は一応それなりの名前にしていますが、大文字で始まっていればどんな名前でも OK みたいです。
macOS では、アプリケーションを、「アプリケーションバンドル」 とよばれる特殊なフォルダで管理します。アプリケーションバンドルを使うと ダブルクリックでターミナルを開かずにアプリケーションを起動できたり、 アプリケーションにアイコンや画像をつけることができます。
アプリケーションバンドルは次のようなディレクトリ構成になります。
Bar.app
-Contents
--Info.plist
--MacOS
---bar
--Resources
---アイコン.icns
Bar.app/Contents
私は次のサイトから .icns ファイルをダウンロードしました。
フリーアイコンSVG、PNG、ICO、ICNS
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>bar</string>
<key>CFBundleIconFile</key>
<string>icon-macbook.icns</string>
<key>CFBundleName</key>
<string>ImageViewer</string>
</dict>
</plist>
Info.plist には、いろいろな設定を書き込めますが、今回は三つだけ設定しました。
もし、今回の PNG と JPEG に対応した Info.plist を手書きしようとすれば次のようになります。ただし、前述の Info.plist でも問題なく動作します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>PNG</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>public.png</string>
</array>
<key>NSUbiquitousDocumentUserActivityType</key>
<string>com.yourname.Bar.example-document</string>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>JPEG</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>public.jpeg</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>bar</string>
<key>CFBundleIconFile</key>
<string>icon-macbook.icns</string>
<key>CFBundleName</key>
<string>ImageViewer</string>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.png</string>
</array>
<key>UTTypeDescription</key>
<string>PNG</string>
<key>UTTypeIcons</key>
<dict/>
<key>UTTypeIdentifier</key>
<string>public.png</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>png</string>
</array>
<key>public.mime-type</key>
<array>
<string>image/png</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.jpeg</string>
</array>
<key>UTTypeDescription</key>
<string>JPEG</string>
<key>UTTypeIcons</key>
<dict/>
<key>UTTypeIdentifier</key>
<string>public.jpeg</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>jpeg</string>
</array>
<key>public.mime-type</key>
<array>
<string>image/jpeg</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>
先ほど作った三つの .swift ファイルと Bar アプリケーションバンドルを同じディレクトリに置きます。ターミナルを起動して、そのディレクトリに移動して次のようにコマンドしてください。
// ビルド
swiftc BarApp.swift BarDoc.swift BarView.swift -o bar
// ビルドされた実行ファイルをアプリケーションバンドルの MacOS ディレクトリに移動
mv bar Bar.app/Contents/MacOS