iPadOS 18 UITabBarの新しいスタイル無効化
問題
iPadOS 18でUITabBarController
の見た目が変更され、タブバーが画面側面に表示されるサイドバースタイルが導入されました。多くのアプリケーションで、従来のように画面下部に固定されたデザインを維持する必要があります。
Apple公式ドキュメントで公開された新しいデザイン
解決方法
🔧 方法1: 水平サイズクラスのオーバーライド(推奨)
最も安全な方法は、タブバーの水平サイズクラスを.compact
に設定することです。
UIKit 実装例
swift
class CustomTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 18.0, *), UIDevice.current.userInterfaceIdiom == .pad {
traitOverrides.horizontalSizeClass = .compact // タブバーを下部に固定
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateTraitOverrides()
}
private func updateTraitOverrides() {
guard #available(iOS 18.0, *),
UIDevice.current.userInterfaceIdiom == .pad,
let window = UIApplication.shared.windows.first else { return }
// 子ビューコントローラに正しいサイズクラスを設定
viewControllers?.forEach {
$0.traitOverrides.horizontalSizeClass = window.traitCollection.horizontalSizeClass
}
}
}
SwiftUI 実装例
swift
struct MainTabView: View {
@Environment(\.horizontalSizeClass) private var originalSizeClass
var body: some View {
TabView {
// 各タブの定義
Text("ホーム").tabItem { Label("ホーム", systemImage: "house") }
Text("設定").tabItem { Label("設定", systemImage: "gear") }
}
.environment(\.horizontalSizeClass, .compact) // タブバーを下部に固定
// 子ビューで元のサイズクラスを復元
.onAppear {
if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
originalSizeClass = scene.windows.first?.traitCollection.horizontalSizeClass
}
}
}
}
サイズクラスの選択
- タブアイコン5個以下:
.compact
が最適 - タブアイコン6個以上:
.unspecified
を使用すると「詳細」オプションが表示されます
⚠️ 方法2: UserDefaultsを使用した設定(非推奨)
非公式な方法としてUserDefaults
を使用できますが、App Storeの審査でリジェクトされる可能性があります。
swift
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 18.0, *) {
// アプリ起動時に設定
UserDefaults.standard.register(defaults: ["UseFloatingTabBar": false])
}
return true
}
注意点
この方法には制約があります:
- アプリ起動時にUI初期化前に設定する必要あり
UserDefaults
の変更が反映されるのは次回起動時- AppleのプライベートAPIを使用する可能性あり
回転レイアウトの問題解決
デバイス回転時に表示が乱れる場合の修正方法:
swift
override func viewWillTransition(to size: CGSize,
with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { _ in
if #available(iOS 18.0, *) {
self.traitOverrides.horizontalSizeClass = .compact
}
})
}
macOS Sequoia対応
iPadアプリをmacOSで実行する場合の二重表示問題解決:
swift
class MacCompatibleTabBarController: UITabBarController {
init() {
super.init(nibName: nil, bundle: nil)
if #available(iOS 18.0, *), ProcessInfo.processInfo.isiOSAppOnMac {
self.mode = .tabSidebar
self.sidebar.isHidden = true
self.traitOverrides.horizontalSizeClass = .compact
}
}
}
ベストプラクティス
- サイズクラスオーバーライドが最優先: 副作用が少なく公式APIのみを使用
- 子ビューに影響が及ばないよう、サイズクラスの調整実装
- iOSバージョン分岐では
#available
を必ず使用 - macOS対応するアプリはタブバーモードの設定実装
将来の互換性について
2025年以降のバージョンに対応するには、Info.plistに以下を追加:
xml
<key>UIDesignRequiresCompatibility</key>
<false/>
Appleが公式APIを提供する可能性が高いため、非公式ハックは最低限にし、新しいAPIが公開されたら移行することを推奨します。