🙆
SwiftUIで外観モードを変更する
本文
SwiftUIを使ったiOSアプリで、外観モードを変える例の紹介です。@AppStorage
を使って、UserDefaults
に保存します。次のように、設定画面とApp
の両方で@AppStorage
を呼び出しておくと、設定ファイルで変更されると、全体の外観モードが切り替わります。tag
にはInt
よりもUIUserInterfaceStyle
を使うと良いと思います。
import SwiftUI
struct AppearanceSettingView: View {
@AppStorage(wrappedValue: .init(), "storage") var storage: Persisted
var body: some View {
List {
Section(content: {
Picker("Appearance setting", selection: $storage.colorMode) {
Text("System")
.tag(UIUserInterfaceStyle.unspecified)
Text("Light")
.tag(UIUserInterfaceStyle.light)
Text("Dark")
.tag(UIUserInterfaceStyle.dark)
}
.pickerStyle(.segmented)
}, header: {
Text("\(Image(systemName: "smartphone")) Mode")
})
.listRowInsets(.init(.zero))
.listRowBackground(Color.clear)
}
.headerProminence(.increased)
.listStyle(.insetGrouped)
.navigationTitle("Appearance")
.navigationBarTitleDisplayMode(.inline)
}
}
UIUserInterfaceStyle
でColorScheme
が生成できます。.unspecified
の場合はちゃんとColorScheme
はnil
になります。
import SwiftUI
@main
@MainActor
struct ExampleApp: App {
@AppStorage(wrappedValue: .init(), "storage") var storage: Persisted
var body: some Scene {
WindowGroup {
MainTabView()
.preferredColorScheme(ColorScheme(storage.colorMode))
}
}
}
Persisted
は次のようにします(Persisted
も名前を考え直した方が良いでしょう)。実際には、colorMode
以外の設定もここに入れたら良いと思います(省略していますが、他の設定も増やした場合は==
の定義をカスタムしたほうが良いようです)。
import UIKit
struct Persisted: Codable {
var colorMode: UIUserInterfaceStyle
enum CodingKeys: String, CodingKey {
case colorMode
}
init(colorMode: UIUserInterfaceStyle = .unspecified) {
self.colorMode = colorMode
}
init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
colorMode = try .init(rawValue: container.decode(Int.self, forKey: .colorMode)) ?? .unspecified
}
func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(colorMode.rawValue, forKey: .colorMode)
}
}
extension Persisted: RawRepresentable {
init?(rawValue: String) {
do {
self = try JSONDecoder().decode(Self.self, from: Data(rawValue.utf8))
} catch {
return nil
}
}
var rawValue: String {
(try? String(decoding: JSONEncoder().encode(self), as: UTF8.self)) ?? ""
}
}
参考
Discussion