Глобальные обновления с пользовательскими настройками по умолчанию в SwiftUI

Я использую @AppStorage и UserDefaults для обновлений в SwiftUI. Если я внесу изменения в соревнование с оболочкой @AppStorage, все будет работать нормально. Я не понимаю, как заставить эту работу работать в глобальном масштабе.

Я использую структуру, которая имеет вычисленные свойства и связанные средства форматирования. Идея состоит в том, чтобы проверить настройки пользователя по умолчанию и преобразовать единицы в фунты или кг. Проблема в том, что представления, использующие вычисленные свойства, не обновляются при обновлении UserDefaults. Есть ли способ создать глобальное изменение, которое обновило бы weightFormatted в SecondaryView ниже?

// Весовая структура

struct Weight {
var weight: Double
var weightFormatted: String {
return weightDecimalLbsOrKgFormatted2(weight)
}

// Метод форматирования

func weightDecimalLbsOrKgFormatted2(_ lbs: Double) -> String {
    if (!UserDefaults.standard.bool(forKey: "weightInKilograms")) {
        let weightString = decimalFormatterDecimal2(lbs)
        return weightString + "lbs"
        
    } else {
        let kg = toKg(lbs)
        let weightString = decimalFormatterDecimal2(kg)
        return weightString + "kg"
    }
}

// Где установлен вес в килограммах

struct AccountView: View {
    @AppStorage("weightInKilograms") var weightInKilograms = false

    let weight = Weight(weight: 9.0))
    
    var body: some View {
        VStack {
            Text(weight.weightFormatted)
            Toggle(isOn: $weightInKilograms) {
                        Text("Kilograms")
            }
        }
}
}

// Дополнительный вид не обновляется

struct SecondaryView: View {

    let weight = Weight(weight: 9.0))
    
    var body: some View {
         Text(weight.weightFormatted)
}
}

person jonthornham    schedule 25.06.2021    source источник


Ответы (1)


Ваша проблема в том, что weight не обернут никаким состоянием.

В вашем AccountView укажите weight оболочку @State:

struct AccountView: View {
  
  @AppStorage("weightInKilograms") var weightInKilograms = false
  
  @State var weight = Weight(weight: 9.0))
  
  var body: some View {
    //...
  }
}

В SecondaryView убедитесь, что weight заключен в @Binding:

struct SecondaryView: View {
  
  @Binding var weight: Weight
  
  var body: some View {
    // ...
  }
}

Затем передайте weight как переменную Binding<Weight> в SecondaryView в вашем первом представлении:

SecondaryView(weight: $weight)

Есть ли способ создать глобальное изменение, которое обновило бы weightFormatted в SecondaryView ниже?

Если вы хотите внести глобальное изменение, вам следует подумать о настройке глобального EnvironmentObject:

class MyGlobalClass: ObservableObject {

  // Published variables will update view states when changed.
  @Published var weightInKilograms: Bool
  { get {
    // Get UserDefaults here
  } set {
    // Set UserDefaults here
  }}

  @Published var weight: Weight
}

Если вы передадите экземпляр MyGlobalClass как EnvironmentObject вашему основному представлению, а затем второстепенному представлению, любые изменения, внесенные в свойства в глобальном экземпляре, будут обновлять состояние представлений через оболочку @Published:

let global = MyGlobalClass()

/* ... */

// In your app's lifecycle, or where AccountView is instantiated
AccountView().environmentObject(global)
struct AccountView: View {
  @EnvironmentObject var global: MyGlobalClass

  var body: some View {
    // ...
    Text(global.weight.weightFormatted)
    // ...
    SecondaryView().environmentObject(global)
  }
}
person Ben Myers    schedule 26.06.2021
comment
Большое спасибо за этот ответ. Я добавляю эту информацию и код Combine, чтобы исправить это. - person jonthornham; 30.06.2021