Итак, многие компоненты SwiftUI имеют функции, которые имеют блок кода в качестве параметра и позже выполняются, например:
TextField("0",text: $text)
.onChange(of: text) { newValue in
// some code
}
или
VStack{}
.onAppear {
// some code
}
Я хочу создать функцию типа onChange, где представление, содержащее компонент, может реализовать блок, который будет выполнен позже в какой-то момент.
Мне удалось воспроизвести это поведение вот так, но я хочу знать, есть ли лучший способ сделать это.
import SwiftUI
struct Item: View {
@State var text: String = ""
var vm: VM = VM()
var body: some View {
VStack {
TextField("0",text: $text)
.frame(width: 10, alignment: .center)
.onChange(of: text) { newValue in
someValidation()
}
.onAppear()
Rectangle()
.frame(maxWidth: .infinity)
.frame(height: 2)
}
}
func someValidation() {
vm.block?()
}
func setBlock(perform: @escaping (() -> Void)) -> some View {
vm.block = perform
vm.block?()
return self
}
class VM {
var block: (() -> Void)?
}
}
struct Item_Previews: PreviewProvider {
static var previews: some View {
Item()
}
}
РЕДАКТИРОВАТЬ
Вот пример того, что я на самом деле пытаюсь сделать. Как я могу отправить своему родителю некоторые данные или просто выполнить некоторый код?
Вот представление, которое вернет строку родительскому элементу.
и вот представление, которое будет обрабатывать возврат строки из моего другого представления.
Разве вам не следует просто использовать для этого Binding?
Я добавил несколько изображений и попытался лучше объяснить, что я пытаюсь сделать. Спасибо за помощь в продвижении
@JoakimDanielson, я думаю, что привязка решила бы эту проблему, но есть ли способ реализовать такую функцию? Как функция .onChange
Просмотрите руководства по Apple SwiftUI, это просто State-Binding





Похоже, вам просто нужно использовать некоторые привязки при передаче данных. Создание Itemпредставления, а затем сообщение родителю об изменении ценностей — это не идеально, где же ваш источник истины? Вместо этого вы можете привязать представление Item к значению в вашем единственном истинном источнике данных.
import SwiftUI
import PlaygroundSupport
struct Item: View {
@Binding var string: String
var body: some View {
VStack {
TextField("", text: $string)
Rectangle().foregroundColor(.red).frame(height: 2)
}
}
}
struct MainView : View {
@State var data: [String]
var body: some View {
VStack {
HStack {
ForEach(Array(data.enumerated()), id: \.0) { _, string in
Text(string)
}
}
HStack {
ForEach(Array($data.enumerated()), id: \.0) { _, string in
Item(string: string)
}
}
}
.frame(width: 500, height: 500)
}
}
let startingValues: [String] = [
"0", "0", "0", "0", "0",
]
PlaygroundPage.current.setLiveView(MainView(data: startingValues))
Этот пример выходит за рамки простого типа, такого как String, и также может использоваться со Struct/Class.
Когда вы меняете одно из значений, массив обновляется, и вы можете увидеть это через верхний HStack. Все, что использует массив data, будет обновляться привязками SwiftUI. Эта парадигма должна быть вашей основой при использовании SwiftUI.
РЕДАКТИРОВАТЬ
Вы всегда можете добавить модификатор onChange к самому Item...
Item(string: string)
.onChange(of: string.wrappedValue, initial: false) {
print("new value! \(string)")
}
Я думаю, вам следует переосмыслить свой подход, было бы легче помочь, если бы мы знали, в чем заключается ваша реальная проблема. Это довольно нестандартно и, вероятно, станет источником проблем с многопоточностью.