#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, представление сообщает модели представления, что у пользователя было намерение, которое затем указывает модели что-то сделать. Любые изменения в модели в результате этого будут затем перенесены с виртуальной машины в представление, если это необходимо.