Я создаю UI-компонент с SwiftUI, который должен иметь триггер извне, чтобы включить анимацию и некоторые внутренние приготовления к ней. В следующих примерах это функция prepareArray ().
Мой первый подход состоял в том, чтобы использовать привязки, но я обнаружил, что нет способа прослушивать изменения @Binding var для запуска чего-либо:
struct ParentView: View {
@State private var animated: Bool = false
var body: some View {
VStack {
TestView(animated: $animated)
Spacer()
Button(action: {
self.animated.toggle()
}) {
Text("Toggle")
}
Spacer()
}
}
}
struct TestView: View {
@State private var array = [Int]()
@Binding var animated: Bool {
didSet {
prepareArray()
}
}
var body: some View {
Text("\(array.count): \(animated ? "Y" : "N")").background(animated ? Color.green : Color.red).animation(Animation.easeIn(duration: 0.5).delay(0.1))
}
private func prepareArray() {
array = [1]
}
}
Почему тогда он позволяет прослушивателю didSet для @Binding var, если он не работает ?! Затем я переключился на простой сигнал объединения, так как он может быть пойман при закрытии onReceive. Но @State on signal не делал недействительным представление при передаче значения:
struct ParentView: View {
@State private var animatedSignal = CurrentValueSubject<Bool, Never>(false)
var body: some View {
VStack {
TestView(animated: animatedSignal)
Spacer()
Button(action: {
self.animatedSignal.send(!self.animatedSignal.value)
}) {
Text("Toggle")
}
Spacer()
}
}
}
struct TestView: View {
@State private var array = [Int]()
@State var animated: CurrentValueSubject<Bool, Never>
var body: some View {
Text("\(array.count): \(animated.value ? "Y" : "N")").background(animated.value ? Color.green : Color.red).animation(Animation.easeIn(duration: 0.5).delay(0.1)).onReceive(animated) { animated in
if animated {
self.prepareArray()
}
}
}
private func prepareArray() {
array = [1]
}
}
Таким образом, мой последний подход заключался в том, чтобы вызвать внутреннее состояние var при значении сигнала:
struct ParentView: View {
@State private var animatedSignal = CurrentValueSubject<Bool, Never>(false)
var body: some View {
VStack {
TestView(animated: animatedSignal)
Spacer()
Button(action: {
self.animatedSignal.send(!self.animatedSignal.value)
}) {
Text("Toggle")
}
Spacer()
}
}
}
struct TestView: View {
@State private var array = [Int]()
let animated: CurrentValueSubject<Bool, Never>
@State private var animatedInnerState: Bool = false {
didSet {
if animatedInnerState {
self.prepareArray()
}
}
}
var body: some View {
Text("\(array.count): \(animatedInnerState ? "Y" : "N")").background(animatedInnerState ? Color.green : Color.red).animation(Animation.easeIn(duration: 0.5).delay(0.1)).onReceive(animated) { animated in
self.animatedInnerState = animated
}
}
private func prepareArray() {
array = [1]
}
}
Что работает нормально, но Я не могу поверить, что такая простая задача требует такой сложной конструкции! Я знаю, что SwiftUI декларативный, но, может быть, мне не хватает более простого подхода к этой задаче? На самом деле в реальном коде этот анимированный триггер должен быть передан еще на один уровень глубже (