Skip to content

iPadOS 18 UITabBarの新しいスタイル無効化

問題

iPadOS 18でUITabBarControllerの見た目が変更され、タブバーが画面側面に表示されるサイドバースタイルが導入されました。多くのアプリケーションで、従来のように画面下部に固定されたデザインを維持する必要があります。

iPadOS 18 タブバー変更画像

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
}

注意点

この方法には制約があります:

  1. アプリ起動時にUI初期化前に設定する必要あり
  2. UserDefaultsの変更が反映されるのは次回起動時
  3. 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
        }
    }
}

ベストプラクティス

  1. サイズクラスオーバーライドが最優先: 副作用が少なく公式APIのみを使用
  2. 子ビューに影響が及ばないよう、サイズクラスの調整実装
  3. iOSバージョン分岐では#availableを必ず使用
  4. macOS対応するアプリはタブバーモードの設定実装

将来の互換性について

2025年以降のバージョンに対応するには、Info.plistに以下を追加:

xml
<key>UIDesignRequiresCompatibility</key>
<false/>

Appleが公式APIを提供する可能性が高いため、非公式ハックは最低限にし、新しいAPIが公開されたら移行することを推奨します。