ハイライト
iOS 27 では、すべての UIKit App がハードコードされたデバイスタイプや画面方向を捨て、Trait Collection と Size Classes によって任意のウィンドウサイズへ対応する必要があります。iPhone App は iPad と Mac 上で完全に自由リサイズ可能なウィンドウになり、UIScene ライフサイクルへ移行していない App は起動できません。
主要内容
「固定デバイス」から「任意のウィンドウ」へ
以前 UIKit App を書く時、多くの人は次のようにレイアウトを判定していました。
if UIDevice.current.userInterfaceIdiom == .pad {
// iPad 用レイアウト
} else {
// iPhone 用レイアウト
}
あるいは画面方向によって UI 構造を決めていました。このロジックは iOS 27 では完全に破綻します。
iPhone App は Mac の iPhone Mirroring 内で、ユーザーが任意の比率へ自由にリサイズできるようになりました。iPad 上で動く iPhone-only App も、固定比率の「大きなスマートフォン」ではなく、他の iPad App と同じように完全にサイズ変更できます。つまり App は 16:9 の Mac ウィンドウで動くこともあれば、縦長の iPhone 画面で動くこともあります。それでも userInterfaceIdiom は常に .phone のままです。
Apple の提案は明快です。デバイスタイプでレイアウト判断をするのをやめ、Size Classes を使います。
UIScene ライフサイクルは必須要件になる
(02:22)
最新 SDK でビルドした App が UIScene ライフサイクルを採用していない場合、その App は起動できません。これは適応型レイアウトの基盤です。Scene がなければ、システムは複数のウィンドウサイズやライフサイクル状態を管理できません。
まだ AppDelegate ベースの構成を使っているなら、できるだけ早く移行する必要があります。Apple は WWDC25 の session “Make your UIKit app more flexible” とドキュメント “Transitioning to the UIKit scene-based life cycle” を参考資料として挙げています。
UIScreen.main との別れ
(03:02)
App が Mac へミラーリングされたり、iPad の外部ディスプレイへ移動されたりすると、Scene に関連付く画面は変わります。UIScreen.main は常に誤った画面情報を返します。
正しい方法は window scene から動的に取得することです。
let screen = window?.windowScene?.screen
さらに良い方法は、screen への依存を完全に取り除き、Trait Collection の代替 API を使うことです。
画面スケールと利用可能領域の正しい取得方法
(03:49)
以前は UIScreen.main.scale でスケールを取得していましたが、今は traitCollection.displayScale を使います。
override func layoutSubviews() {
super.layoutSubviews()
let displayScale = traitCollection.displayScale
// displayScale に基づいて描画ロジックを調整
}
UIKit は layoutSubviews や draw(_:) などのよく使われるメソッド内での trait 読み取りを自動追跡します。displayScale が変わると、システムはこれらのメソッドを自動的に再呼び出しします。手動の監視は不要です。
自動追跡で覆えない場所では registerForTraitChanges を使います。
let displayScaleTrait: [UITrait] = [UITraitDisplayScale.self]
registerForTraitChanges(displayScaleTrait) {
(view: GalleryView, previousTraitCollection: UITraitCollection) in
view.cache.invalidate()
}
(04:36)
利用可能領域も UIScreen.main.bounds ではなく、Scene の有効ジオメトリか View 自身の bounds を見ます。
// SceneDelegate でジオメトリ変更を監視
func windowScene(
_ windowScene: UIWindowScene,
didUpdateEffectiveGeometry previousEffectiveGeometry: UIWindowScene.Geometry
) {
let geometry = windowScene.effectiveGeometry
let availableSpace = geometry.coordinateSpace.bounds
}
// ViewController で view の bounds を直接見る
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let availableSpace = view.bounds.size
}
Tab Bar と Navigation Bar の新機能
(09:51)
iPhone App は大きなウィンドウで、下部 Tab Bar ではなくサイドバーを表示できるようになりました。
tabBarController.sidebar.preferredPlacement = .sidebar
システムは水平 Size Class に基づいてサイドバーを表示するか判断します。現在の環境が対応しているかは tabBarController.sidebar.isAvailable で確認できます。
常に表示される prominent tab も設定できます。
tabBarController.prominentTabIdentifier = "cart"
(10:53)
Navigation Bar にはスクロール時の最小化制御が追加されました。
navigationItem.barMinimizationBehavior = .always
navigationItem.barMinimizationSafeAreaAdjustment = .never
(11:30)
CoreMotion と CoreLocation の Body プロトコル
(08:12)
UIView は CoreMotion と CoreLocation の Body プロトコルに準拠するようになりました。View を motion manager に直接代入すれば、システムが座標空間の変換を自動処理します。
override func viewDidLoad() {
super.viewDidLoad()
motionManager.deviceMotionBody = view
locationManager.headingBody = view
}
画面方向に基づいて座標の回転を手動計算する必要はもうありません。
Apple Intelligence との統合
(12:56)
iOS 27 の Menu は Ask Siri ボタンを自動表示します。新しい View Annotations API で特定の View に AppEntity を注釈付けすると、Siri はより正確なコンテキストを得られます。
App がドラッグアンドドロップをサポートしている場合、Siri は drag delegate から直接リソースを読み込めます。注意点として、ドラッグは Siri によってジェスチャなしで開始される場合があります。sessionWillBegin でモーダル UI を表示してはいけません。状態を持つ UI 初期化コードは sessionDidMove に置きます。
Xcode 27 の App Modernization Skill
(14:16)
Xcode 27 には App モダン化 skill が組み込まれており、移行作業の多くを自動化できます。
UIScreen.main呼び出しをtraitCollectionまたは scene bounds のチェックに置き換える- デバイスタイプのチェックを Size Class のチェックに置き換える
- App ライフサイクルを Scene ライフサイクルへ変換する
- 複雑な作業では質問し、大きな作業では残作業を示すコメントを追加する
skill をエクスポートして他のツールで使えます。
xcrun agent skills export
(15:05)
詳細
コード例1:window scene から画面情報を取得する
App が Mac へミラーリングされたり、iPad の外部ディスプレイへ移動されたりすると、Scene に関連付く画面は変わります。UIScreen.main は常に誤った画面情報を返します。
正しい方法は window scene から動的に取得することです。
let screen = window?.windowScene?.screen
重要な点:UIScreen.main は iOS 27 で非推奨です。複数画面のシナリオに対応できないためです。windowScene.screen は現在の Scene が実際に存在する画面を常に返し、iPhone Mirroring や外部ディスプレイ切り替え時にも自動更新されます。
コード例2:registerForTraitChanges で trait 変化を監視する
UIKit は layoutSubviews や draw(_:) などのメソッド内での trait 読み取りを自動追跡し、displayScale が変わると自動的に再描画します。しかしキャッシュ無効化やバックグラウンドでのデータ再計算など、自動追跡で覆えない場所では明示的な登録が必要です。
let displayScaleTrait: [UITrait] = [UITraitDisplayScale.self]
registerForTraitChanges(displayScaleTrait) {
(view: GalleryView, previousTraitCollection: UITraitCollection) in
view.cache.invalidate()
}
複数 trait の組み合わせも監視できます。
let geometryTraits: [UITrait] = [
UITraitHorizontalSizeClass.self,
UITraitVerticalSizeClass.self
]
registerForTraitChanges(geometryTraits) { (vc: MyViewController, _) in
vc.updateLayoutForCurrentSizeClass()
}
重要な点:registerForTraitChanges のクロージャ引数の型は、監視対象オブジェクトの実際の型(GalleryView や MyViewController など)と一致している必要があります。システムは型推論でコールバックを分配します。クロージャは trait 変化時に同期的に呼ばれるため、軽量な状態更新に向きます。非同期処理や複雑なロジックが必要な場合は、クロージャ内で自前の状態機械を起動するのがよいでしょう。
コード例3:Tab Bar のサイドバー対応
iPhone App は大きなウィンドウで自動的にサイドバーナビゲーションへ切り替えられるため、下部領域を節約できます。
tabBarController.sidebar.preferredPlacement = .sidebar
システムは水平 Size Class に基づいてサイドバーを表示するか判断します。現在の環境が対応しているかは tabBarController.sidebar.isAvailable で確認できます。
常に表示される prominent tab も設定でき、サイドバーが折りたたまれても中核機能へアクセスできるようにできます。
tabBarController.prominentTabIdentifier = "cart"
重要な点:preferredPlacement = .sidebar は、条件が合う時はサイドバーを優先するようシステムへ伝えます。ウィンドウが compact な水平 Size Class まで縮むと、システムは自動的に下部 Tab Bar へ戻します。手動対応は不要です。prominentTabIdentifier に対応する tab はサイドバーモードで目立つ表示になり、折りたたみ時にはフローティングボタンとして残ります。
適応型レイアウトの主要 API 移行
| 古い書き方 | 新しい書き方 | 説明 |
|---|---|---|
UIScreen.main | window?.windowScene?.screen | window scene から動的に取得 |
UIScreen.main.scale | traitCollection.displayScale | Trait Collection が自動追跡 |
UIScreen.main.bounds | view.bounds.size または windowScene.effectiveGeometry | View または Scene の実際の空間を見る |
UIDevice.current.userInterfaceIdiom | traitCollection.horizontalSizeClass | Size Class でレイアウト判断する |
UIApplication.shared.statusBarOrientation | traitCollection.horizontalSizeClass / verticalSizeClass | 方向チェックはもう意味を持たない |
ゲーム App の特別対応
ゲームは通常、固定方向を必要とします。iOS 27 では UIRequiresFullscreen の動作が調整されました。完全にリサイズを禁止するのではなく、離散的リサイズ(discrete resizing)を有効にします。ウィンドウサイズが変わるたびに、システムは新しいサイズに合う画面設定へ切り替え、ゲームは常に完全な品質で描画します。
テストのすすめ
Xcode 27 の Device Hub と Xcode Previews は「リサイズモードに入る」機能に対応しており、複数シミュレータを切り替えなくても、デバイス端をドラッグして自由にサイズ変更できます。初期検証が終わったら、必ず実機で iPhone Mirroring と iPad 外部ディスプレイのシナリオをテストしてください。
重要な示唆
-
古いプロジェクトの
UIScreen.mainをすべて置き換える何をするか:プロジェクト内のすべての
UIScreen.main参照を検索し、traitCollection.displayScaleまたは window scene 参照に置き換えます。なぜ価値があるか:iOS 27 では
UIScreen.mainが誤ったデータを返し、画像のぼやけやレイアウトのずれを引き起こします。どう始めるか:Xcode の Find Navigator で
UIScreen.mainを検索し、1つずつ置き換えます。あるいは Xcode 27 の App modernization skill で自動処理します。 -
Tab Bar にサイドバー対応を追加する
何をするか:
viewDidLoadでtabBarController?.sidebar.preferredPlacement = .sidebarを設定します。なぜ価値があるか:iPhone App が Mac 上で大きなウィンドウにリサイズされた時、下部 Tab Bar は横方向の空間を大きく浪費します。サイドバーならより多くのナビゲーション階層を表示できます。
どう始めるか:1行のコードで有効化し、
sidebar.isAvailableで現在サイドバーが表示されているかを判定して関連 UI を調整します。 -
prominent tab で中核ナビゲーションを守る
何をするか:最も重要な Tab(カート、ホームなど)に
prominentTabIdentifierを設定します。なぜ価値があるか:ウィンドウが小さくなるとサイドバーは折りたたまれることがあります。prominent tab は中核機能を常にアクセス可能にします。
どう始めるか:
UITabBarControllerを作成した後、tabBarController.prominentTabIdentifier = "your-tab-id"を設定します。 -
スクロール時に Navigation Bar を自動的に畳む
何をするか:
navigationItem.barMinimizationBehavior = .alwaysを設定します。なぜ価値があるか:コンテンツ領域が広がり、読書体験が良くなります。システムのデフォルトでは特定条件でしか畳まれないため、強制的に有効化すると挙動を統一できます。
どう始めるか:ViewController の初期化コードでこの属性を設定します。safe area を自前で扱っている場合は、
barMinimizationSafeAreaAdjustment = .neverも設定します。 -
Xcode skill を自分のワークフローへエクスポートする
何をするか:
xcrun agent skills exportを実行し、Xcode の modernization skill を markdown ファイルとしてエクスポートします。なぜ価値があるか:Cursor や Claude Code など他のツールでも Apple 公式のモダン化ルールを再利用でき、チームの移行方針を揃えられます。
どう始めるか:Terminal でコマンドを実行します。エクスポートされたファイルは他の AI コーディングツールへ直接インポートできます。
関連 Session
- UIKit App をより柔軟にする — UIKit の適応型レイアウトをさらに深く扱うガイドと、Scene ライフサイクル移行の詳しい手順
- iPadOS の Tab とサイドバー体験を高める — Tab Bar とサイドバーの深い統合、Tab Group 管理のベストプラクティス
- Device Hub を最大限に活用する — Xcode 27 の新しい Device Hub の全体像。自由なリサイズテストも含みます
- Siri と Apple Intelligence 向けの高度な App Intents 機能を探る — View Annotations API と Siri 統合の詳しい使い方
- SwiftUI — SwiftUI コードも保守しているなら、SwiftUI の適応型レイアウトに関する最新動向を理解できます
コメント
GitHub Issues · utterances