#ios #swift #swiftui #observedobject
#iOS #swift #swiftui #наблюдаемый объект
Вопрос:
В приложении SwiftUI у меня есть следующее. В принципе, у меня есть некоторые настройки (класс настроек), которые я хотел бы использовать во всем приложении. У меня есть представление настроек, в котором отображается средство выбора для выбора значения одного из параметров. И другие представления приложения будут использовать только текущее установленное значение настроек. Следующая настройка работает в том смысле, что в contentView я вижу правильное значение firstLevel
параметра. Но проблема в том, что в SettingsView, я думаю, поскольку selectedFirstLevel
это не @State, его правильное значение не отображается в средстве выбора, которое я выбираю, чтобы выбрать либо even
или odd
(как ни странно, в первый раз это правильно). Этот выбор правильно переносится в contentView, но он неправильно отображается в SettingsView. Как я могу исправить эту проблему?
Настройки.swift
import Foundation
class Settings: ObservableObject {
static let shared: Settings = Settings()
@Published var firstLevel: FirstLevel = .even
}
enum FirstLevel: String, CaseIterable, Identifiable {
case even
case odd
var id: String { self.rawValue }
}
contentView.swift
import SwiftUI
struct ContentView: View {
@State private var showSettings: Bool = false
@ObservedObject var settings = Settings.shared
var body: some View {
VStack {
SettingsButton(showSettings: $showSettings, settings: settings)
Text(settings.firstLevel.id)
.padding()
}
}
}
struct SettingsButton: View {
@Binding var showSettings: Bool
var settings: Settings
var firstLevel: Binding<FirstLevel> {
return Binding<FirstLevel>(
get: {
return self.settings.firstLevel
}) { newFirstLevel in
self.settings.firstLevel = newFirstLevel
}
}
var body: some View {
Button(action: { self.showSettings = true }) {
Image(systemName: "gear").imageScale(.large)
}
.sheet(isPresented: $showSettings) {
SettingsView(selectedFirstLevel: self.firstLevel)
}
}
}
SettingsView.swift
import SwiftUI
struct SettingsView: View {
@Binding var selectedFirstLevel: FirstLevel
var body: some View {
NavigationView {
Form {
Picker("First Level", selection: $selectedFirstLevel) {
ForEach(FirstLevel.allCases) { level in
Text(level.rawValue).tag(level)
}
}
}
.navigationBarTitle("Settings", displayMode: .inline)
}
}
}
Ответ №1:
Это выглядит слишком сложным, более того, привязка ненадежна как связь между различными иерархиями представлений (в вашем случае это лист).
Вот упрощенный и проработанный вариант. Протестировано с Xcode 12 / iOS 14.
struct ContentView: View {
@ObservedObject var settings = FLevelSettings.shared
var body: some View {
VStack {
SettingsButton(settings: settings)
Text(settings.firstLevel.id)
.padding()
}
}
}
struct SettingsButton: View {
@State private var showSettings: Bool = false
var settings: FLevelSettings
var body: some View {
Button(action: { self.showSettings = true }) {
Image(systemName: "gear").imageScale(.large)
}
.sheet(isPresented: $showSettings) {
FLevelSettingsView(settings: self.settings)
}
}
}
struct FLevelSettingsView: View {
@ObservedObject var settings: FLevelSettings
var body: some View {
NavigationView {
Form {
Picker("First Level", selection: $settings.firstLevel) {
ForEach(FirstLevel.allCases) { level in
Text(level.rawValue).tag(level)
}
}
}
.navigationBarTitle("Settings", displayMode: .inline)
}
}
}
Примечание: это может быть еще более упрощено, если вы хотите, из-за наличия FLevelSettings.shared
, поэтому вы можете использовать его FLevelSettingsView
непосредственно внутри. На всякий случай.