Передать привязку к дочернему представлению в инициализации SwiftUI

#swiftui

Вопрос:

Мне нужно сохранить экземпляр дочернего представления в переменной, чтобы потом я мог вызвать для него метод.

Однако мне нужно передать привязку в это дочернее представление при его инициализации. Как мне это сделать?

  struct EditImageView: View {   @State private var currentSelectedText:String  @State private var currentSelectedFilter:Filter  var imageCanvasView: ImageCanvasView   init() {  currentSelectedText = "Hello"  currentSelectedFilter = Filter.noFilter  imageCanvasView = ImageCanvasView(imageText: $currentSelectedText, filter: $currentSelectedFilter)   //Error: 'self' used before all stored properties are initialized  }   var body: some View {  imageCanvasview  Button("Take screenshot") {  imageCanvasview.takeScreenshot()  }  }  }  

Ответ №1:

Один из способов-объявить imageCanvasView в body , как:

 struct EditImageView: View {   @State private var currentSelectedText = "Hello"  @State private var currentSelectedFilter = Filter.noFilter    var body: some View {  let imageCanvasView = ImageCanvasView(imageText: $currentSelectedText, filter: $currentSelectedFilter)  VStack {  imageCanvasView  Button("Take screenshot") {  imageCanvasView.takeScreenshot()  }  }  } }  

Комментарии:

1. Хороший подход. Но что, если мое представление более сложное? например, если imageCanvasView вложен в другой HStack внутри этого VStack, поэтому он не является одноранговым элементом Button. Таким образом, локальная переменная imageCanvasView будет находиться вне области действия, когда мы будем на кнопке.

2. @thecanteen imageCanvasView всегда находится в области body действия , независимо от того, сколько там вложенности.

3. Ах, извините, я неправильно понял и подумал, что вы определили imageCanvasView внутри VStack. В этом есть большой смысл. Я понятия не имел, что вы можете определять переменные внутри тела, но я думаю, что это все еще кошерно, так как тело все еще остается чистой функцией

4. @thecanteen вы также можете объявлять переменные в ViewBuilder функциях (также допускается условное ветвление)

Ответ №2:

Все, что вам нужно сделать, это изменить префикс оболочки свойств. Например, если бы вы хотели сдать свой currentSelectedText экзамен, вы бы сдали его вот так.

 var currentSelectedText: Bindinglt;Stringgt;  // Effectively is the equivalent of `@State`  

То же самое можно сделать и в вашем init()

 init(someString: Bindinglt;Stringgt;) { ....  

Ответ №3:

Вероятно, лучший способ-использовать модель представления, которая одновременно EditImageView и ImageCanvasView используется, что-то вроде:

 class EditImageViewModel: ObservableObject {  @Published var currentSelectedText: String = "Hello"  @Published var currentSelectedFilter = Filter.noFilter    func takeScreenshot() {    } }  struct ImageCanvasView: View {    @EnvironmentObject var editImage: EditImageViewModel    var body: some View {} }   struct EditImageView: View {    @StateObject var editImage = EditImageViewModel()    var body: some View {  VStack {  ImageCanvasView()  Button("Take screenshot") {  editImage.takeScreenshot()  }  }  .environmentObject(editImage)  } }  

Комментарии:

1. Не могли бы вы объяснить, почему вы добавляете takeScreenshot() в модель представления?

2. @thecanteen да, это был бы правильный подход MVVM, представление сообщает модели представления, что у пользователя было намерение, которое затем указывает модели что-то сделать. Любые изменения в модели в результате этого будут затем перенесены с виртуальной машины в представление, если это необходимо.