Reputation: 521
I want to change a view's transition dynamically after the view is created. I toggle a State variable isTransition1
by clicking a button to switch between transition1
and transition2
as the below. However, it doesn't work as intended if one of these transitions is opacity. The view to be removed immediately after changing transition always keeps its original transition. Surprisingly, if I change transition2
to slide, it will work without problem. The view to be removed will use the new transition. Is there any way to make opacity work here?
let transition1 = AnyTransition.asymmetric(insertion: .move(edge: .trailing),
removal: .move(edge: .leading))
let transition2 = AnyTransition.opacity
struct Wrapper1<Content: View>: View {
let content: Content
var body: some View {
content
}
}
struct Wrapper2<Content: View>: View {
let content: Content
var body: some View {
content
}
}
struct TextView: View {
let count: Int
let color: Color
var body: some View {
ZStack {
color
.edgesIgnoringSafeArea(.all)
.frame(maxWidth: UIScreen.main.bounds.width,
maxHeight: UIScreen.main.bounds.height)
Text("Count: \(count)")
.font(.title)
.offset(y: -200)
}
}
}
struct ContentView: View {
@State private var count = 0
@State private var isTransition1 = false
var body: some View {
ZStack {
if count % 2 == 0 {
Wrapper1(content: TextView(count: count, color: Color.green)
.transition(isTransition1 ? transition1 : transition2))
} else {
Wrapper2(content: TextView(count: count, color: Color.red)
.transition(isTransition1 ? transition1 : transition2))
}
HStack(spacing: 100) {
Button(action: {
self.isTransition1.toggle()
}) {
Text("Toggle Transition").font(.title)
}
Button(action: {
withAnimation(.linear(duration: 2)) {
self.count += 1
}
}) {
Text("Increase").font(.title)
}
}
}
}
}
Upvotes: 6
Views: 1194
Reputation: 585
I had the same issue recently where I wanted to change the transition inbetween states. Nothing seemed to work until I decided to create an intermediate internal state that updates the UI only after it has updated the transition. I am using a view model that is an observable object.
To solve the issue I created an internal state that is a currentValueSubject. I also made my transition a published Variable so as to update the UI after the transition changes.
I update the internal state, which in turn updates the transition, which then updates the UI before changing the state.
private var internalState: CurrentValueSubject<BookingWizardState, Never> = CurrentValueSubject(.date)
@Published var state: BookingWizardState
private let moveForwardTransition = AnyTransition.asymmetric(insertion: .move(edge: .trailing),
removal: .move(edge: .leading))
private let moveBackwardTransition = AnyTransition.asymmetric(insertion: .move(edge: .leading),
removal: .move(edge: .trailing))
@Published var transition: AnyTransition
func setupSubscriptions() {
//Set the transition based on the state change
internalState.map { [weak self] newState in
guard let self else { return .slide }
let isForward = self.state.rawValue <= newState.rawValue
return isForward ? self.moveForwardTransition : self.moveBackwardTransition
}
.assign(to: &$transition)
//Update the external state after a fraction of a second and after the transition has been updated.
internalState
.delay(for: .seconds(0.1), scheduler: RunLoop.main)
.assign(to: &$state)
}
Upvotes: 1
Reputation: 257563
Not sure if I correctly understood what effect you tried to achieve, but try to reset view hierarchy (at least this definitely resets transitions, so they don't affect each other):
var body: some View {
ZStack {
if count % 2 == 0 {
Wrapper1(content: TextView(count: count, color: Color.green)
.transition(isTransition1 ? transition1 : transition2))
} else {
Wrapper2(content: TextView(count: count, color: Color.red)
.transition(isTransition1 ? transition1 : transition2))
}
HStack(spacing: 100) {
Button(action: {
self.isTransition1.toggle()
}) {
Text("Toggle Transition").font(.title)
}
Button(action: {
withAnimation(.linear(duration: 2)) {
self.count += 1
}
}) {
Text("Increase").font(.title)
}
}
}.id(isTransition1) // << here !!
}
Upvotes: 1