Mac Catalystアプリを良い感じにする固有テクニック2(+6)選
Mac CatalystはiPadOS(+iOS)対応アプリを超低コストでMacへ移植するためのツールです。
私はMac Catalystを活用して2つのiPhone/iPadアプリをMacアプリとしてリリースしました。
既存のコードをMac Catalyst向けにそのままビルドし、動作確認後に少しだけ手直しするだけで簡単に移植作業がほぼ完了しました。
良い感じのアプリにするためにMac Catalyst固有のテクニックを2つだけ用いる事となりました。
また、Mac Catalyst固有ではないiPadOS/macOS向けのテクニックをいくつか用いました。
今回はそれらを紹介します。
不要なビルドインのメニューバー項目を取り除く
MacにはiPhone/iPadと異なり「メニューバー」が存在します。Mac Catalystではメニューバーに多くのコマンドがビルドインされています。
対象アプリで利用しないビルドインコマンドを非表示にして見た目をスッキリさせましょう。
class アプリモデル: UIResponder, UIApplicationDelegate {
override func buildMenu(with builder: UIMenuBuilder) {
builder.remove(menu: .services)
builder.remove(menu: .file)
builder.remove(menu: .edit)
builder.remove(menu: .format)
builder.remove(menu: .toolbar)
builder.remove(menu: .sidebar)
builder.remove(menu: .help)
}
}
タイトルバーを調整する
Mac Catalystアプリのウインドウには「タイトルバー」が追加されています。
アプリによってはこのタイトルバーの見た目やタイトルテキストが邪魔に感じるかもしれません。私のアプリではタイトルテキストを隠しました。
(UIApplication.shared.connectedScenes.first as? UIWindowScene)?
.titlebar?
.titleVisibility = .hidden
Mac Catalyst固有ではないiPadOS向けテクニック
Mac Catalyst固有のテクニックというわけではないですが、iPadOS向けテクニックをいくつか用いました。Mac CatalystきっかけでiPadアプリも一緒に改善されるというわけです。
Commands
アプリの主要な機能だけでもCommands対応するとユーザーは嬉しいでしょう。
WindowGroup { /* ... */ }
.commands {
CommandMenu("アクション") {
Button("リロード") { /* ... */ }
.keyboardShortcut("r")
}
}
ボタンにkeyboardShortcut
あるボタンに対して予想しやすい標準的なキーボード操作にはkeyboardShortcutを設定する。
例えば「シートを閉じるボタン」であればescapeキーで呼び出せるようにしましょう。
Button {
self.dismiss()
} label: {
Image(systemName: "xmark.circle.fill")
}
.keyboardShortcut(.cancelAction)
Multiple WindowsをNO
iPhoneと異なりiPadやMac Catalystでは複数のウインドウを持つことが出来ます。Xcodeのテンプレート構成やデフォルト設定では複数ウインドウが可能になっています。
ユーザーからの想定外の入力による不具合を回避するために複数ウインドウを無効にしましょう。Info.plistの「Enable Multiple Windows」をNOにするだけです。
コンテンツを横に間延びさせない
iPhone/iPadアプリが横長ウインドウとして表示される際に起きる典型的な課題の1つに「コンテンツが横に間延びしすぎて不自然になる事」です。
理想としてはアプリの構成を見直してUIを再設計する事ですが、それでは非常に多くの開発リソースを消費します。
今回は多くのiPadアプリで採用されている「コンテンツの左右に余白を追加するアプローチ」を紹介します。
struct 余白追加: ViewModifier {
func body(content: Content) -> some View {
GeometryReader {
content
.safeAreaPadding(.horizontal,
$0.size.width > 1100 ? 200 : 0)
}
}
}
//===================================
struct ContentView: View {
var body: some View {
TabView {
NavigationStack {
List {
/* 省略 */
}
.modifier(余白追加())
/* 省略 */
}
/* 省略 */
}
}
}
Mac Catalyst固有ではないmacOS向けテクニック
Mac Catalyst固有のテクニックというわけではないですが、macOS向けテクニックをいくつか用いました。これらのテクニックはフルネイティブMacアプリを開発する際にそのまま活かせます。
アプリ名をちゃんとローカライズ
iOS/iPadOSでアプリ名のローカライズをしている場合、メニューバーやアプリウインドウ内でのアプリ名は同様にローカライズが適用されます。しかしLaunchpadやDock等での表記がローカライズされません。
Info.plistのApplication has localized display name(LSHasLocalizedDisplayName)
をYESにすれば解決します。
ウインドウサイズの指定
iPhone/iPadには「ウインドウサイズの指定」という概念は無いですが、Macアプリとしては必要になることもあるかもしれません。
Mac Catalystでウインドウのサイズを制限したい場合、数年前まではワークアラウンドな知識(UIKitやAppKit)が必要だったっぽいです。
Mac Catalyst17.0以降ではMacアプリ同様にwindowResizabilityやdefaultSizeでウインドウサイズの指定が簡単に可能になりました。
WindowGroup {
ContentView()
}
.windowResizability(.contentMinSize)
.defaultSize(width: 360, height: 600)
ウインドウの最大サイズも指定したい場合
windowResizabilityをcontentSizeにして、ViewのframeでmaxWidth/maxHeightを設定すればウインドウの最大サイズを指定出来ます。
WindowGroup {
ContentView()
.frame(minWidth: 360, maxWidth: 600, minHeight: 600, maxHeight: 800)
}
.windowResizability(.contentSize)
しかし例外があります。ウインドウをフルスクリーンにした場合に破綻してしまうのです。
ワークアラウンドっぽい実装になりますが、UIWindowScene.sizeRestrictions.allowsFullScreenをfalseにすればフルスクリーンを無効に出来ます。
(UIApplication.shared.connectedScenes.first as? UIWindowScene)?
.sizeRestrictions?.allowsFullScreen = false
補足: Mac Catalystの最初の1歩
Supported Destinations
にMac(Mac Catalyst)
を追加します。
補足: Mac Catalystだけで実行するコード
#if targetEnvironment(macCatalyst)
print("ここはMacでのみ実行される")
#else
print("ここはiPhone/iPadでのみ実行される")
#endif
#if !targetEnvironment(macCatalyst)
print("ここはMacで実行されません")
#endif
関連リンク
Discussion