0

Is there a more natural way (now that it's 2022) of initializing some internal variables that depend on an @EnvironmentObject (or other @ObservedObject) within a view's init() function?

For example, the following shows what I'm trying to do (commented out) versus what works. Unfortunately the "what works" code is considerably more unwieldly (bulky, repetitive). Instead of just using a diary var I have to sprinkle my code with try? log.readDiary(for: state.now) or wrap it all in a subviews. Wondering what the best practice is.

struct NutritionView: View {
   let log: LogProvider
   @ObservedObject private var state: StateService

//   @StateObject private var diary: DiaryReader? // init depends on using state (above)

   init(log: LogProvider) {
//      self.diary = try? log.readDiary(for: state.now) // would like to init here
        // unfortunately `self.state` not available inside init()
   }
   
   var body: some View {
//    let remaining = remainingCalories(diary: diary, goals: goals) // and use `diary` here
      let remaining = remainingCalories(diary: try? log.readDiary(for: state.now), goals: goals)
      
      VStack {
         ...
      }
   }
}

There's a related post here: Swiftui - How do I initialize an observedObject using an environmentobject as a parameter?, but I'd rather not create internal subviews to accomplish what seems to be a simple initialization. That just seems... wasteful, repetitive, unwieldly. Hoping the API has evolved a bit since then.

4

1 回答 1

1

创建多个超快速值类型的 View 数据结构并没有什么浪费。我们应该创建许多小的 View 结构,这些结构只有少数属性被主体使用。SwiftUI 不断地重新计算这些,并使用 diff 的结果来更新屏幕上的 UILabel。

无论如何,您的设计似乎出了点问题。EnvironmentObject不用于状态,它用于作为 ObservableObject 类的数据模型,因此是引用类型,因此它的生命周期比视图更长。在里面我们应该使用值类型来建模我们的数据,例如结构。我们将这些作为让或@Binding用于写访问的视图传递给视图。我们还可以在我们的模型对象中使用 func,它可以做一些工作,然后改变值类型(通常通过 ID 查找),这会导致 SwiftUI 检测到更改并重新计算依赖于数据的所有视图主体。

于 2022-02-20T21:43:22.167 回答