【SwiftUI】画面の向きに応じてレイアウトを変える方法

こんにちはコーヤです。

このページでは、SwiftUIで画面の向きに応じてレイアウトを変える方法をご紹介します。

以下のバージョンで動作確認しています。

  • Xcode 14.2
  • Swift 5.7.2

固定レイアウトの問題点

縦画面に合わせて四角を3つ並べましたが、横画面にするとはみ出してしまいます。

画面の向きに応じたレイアウト

縦画面用のレイアウト、横画面用のレイアウトをそれぞれ用意し、画面の向きに応じてレイアウトを変更させます。

ソースコード

import SwiftUI

struct ContentView: View {
    
    @State var orientation: UIDeviceOrientation
    
    var body: some View {
        ZStack {
            if orientation.isPortrait {
                PortraitView()
            } else if orientation.isLandscape {
                LandscapeView()
            } else if orientation.isFlat {
                Text("orientation is flat")
            } else {
                Text("orientation is unknown")
            }
        }
        .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
            orientation = UIDevice.current.orientation
        }
    }
}

struct PortraitView: View {
    var body: some View {
        VStack {
            BlueView()
            RedView()
            GreenView()
        }
    }
}

struct LandscapeView: View {
    var body: some View {
        HStack {
            BlueView()
            VStack {
                RedView()
                GreenView()
            }
        }
    }
}

struct BlueView: View {
    var body: some View {
        Text("Blue")
            .font(.largeTitle)
            .frame(width: longLength, height: longLength)
            .background(Color.blue)
    }
}

struct RedView: View {
    var body: some View {
        Text("Red")
            .font(.largeTitle)
            .frame(width: longLength, height: shortLength)
            .background(Color.red)
    }
}

struct GreenView: View {
    var body: some View {
        Text("Green")
            .font(.largeTitle)
            .frame(width: longLength, height: shortLength)
            .background(Color.green)
    }
}

let longLength: CGFloat = 300
let shortLength: CGFloat = longLength * 0.5

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(orientation: .unknown)
    }
}

onRecieve()のモディファイアで画面の向きを検知しています。それに応じてif文が分岐し、レイアウトが変更される仕組みになっています。

コーディング時の注意点

アプリをビルドする前に@mainの部分に画面の向きを指定する必要があります。

下記のコードの7行目の部分です。

import SwiftUI

@main
struct SampleAppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView(orientation: .unknown)
        }
    }
}

また、Xcodeのキャンパス(画面右側のプレビューするエリア)にもどの向きの画面をプレビューするか指定する必要があります。

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(orientation: .unknown)
    }
}

.unknownの部分を変更すると、キャンパスに表示されるプレビューが変化します。

【補足】下記ページで画面の向きの変化の履歴を取る方法をご紹介しています。

以上です。ご参考になれば幸いです。

コメント欄