🗂

【SwiftUI】モーダルにカラースキームが反映されない件について

2024/01/25に公開

今回の件について

現在以下の環境では、モーダルでカラースキームを変更した際に、そのモーダルにカラースキームが反映されません。(仕様かもしれませんが)

Xcode: 15.2
iOS : 17.2

問題のコード

以下のコードで実際に確認いただけます。

@main
struct ColorSchemeApp: App {
    @State private var colorScheme = "light"

    var body: some Scene {
        WindowGroup {
            ContentView(colorScheme: $colorScheme)
                .preferredColorScheme({
                    return switch colorScheme {
                    case "light": .light
                    case "dark": .dark
                    default: .none
                    }
                }())
        }
    }
}

struct ContentView: View {
    @State private var isShow = false
    @Binding private(set) var colorScheme: String

    var body: some View {
        Button("Show") {
            isShow = true
        }
        .font(.largeTitle)
        .sheet(isPresented: $isShow) {
            ModalView(colorScheme: $colorScheme)
        }
    }
}

struct ModalView: View {
    @Binding private(set) var colorScheme: String

    var body: some View {
        VStack(spacing: 30) {
            Button("light") {
                colorScheme = "light"
            }
            Button("dark") {
                colorScheme = "dark"
            }
            Button("system") {
                colorScheme = "system"
            }
        }
        .font(.title)
    }
}

#Preview {
    ContentView(colorScheme: Binding.constant("light"))
}

解決方法

モーダルの呼び出し元のViewで環境変数colorSchemeを取得し、それをpreferredColorSchemeでモーダルに設定します。

@main
struct ColorSchemeApp: App {
    @State private var bindingColorScheme = "light"

    var body: some Scene {
        WindowGroup {
            ContentView(bindingColorScheme: $bindingColorScheme)
                .preferredColorScheme({
                    return switch bindingColorScheme {
                    case "light": .light
                    case "dark": .dark
                    default: .none
                    }
                }())
        }
    }
}

struct ContentView: View {
    @State private var isShow = false
    @Binding private(set) var bindingColorScheme: String
    /// Additional code
    @Environment(\.colorScheme) private var environmentColorScheme

    var body: some View {
        Button("Show") {
            isShow = true
        }
        .font(.largeTitle)
        .sheet(isPresented: $isShow) {
            ModalView(bindingColorScheme: $bindingColorScheme)
                /// Additional code
                .preferredColorScheme(environmentColorScheme)
        }
    }
}

struct ModalView: View {
    @Binding private(set) var bindingColorScheme: String

    var body: some View {
        VStack(spacing: 30) {
            Button("light") {
                bindingColorScheme = "light"
            }
            Button("dark") {
                bindingColorScheme = "dark"
            }
            Button("system") {
                bindingColorScheme = "system"
            }
        }
        .font(.title)
    }
}

#Preview {
    ContentView(bindingColorScheme: Binding.constant("light"))
}

蛇足

去年の暮あたりに今回の問題を別の方法で解決している動画を見つけたのですが、再度探したところ見つかりませんでした。もしその動画に心当たりのある方は一声お声掛けいただけると幸いです。

Discussion