I've been researching this for ages and cannot find any answers on here or online that addresses how to create custom transitions for when a subview appears/disappears.
What I'm trying to do... I have a badge view TestBadgeView and a TestBadgeInsideHStackView, which as the name suggests, is just a HStack with an embedded TestBadgeView. On the preview screen, using TestTransitionView, I have a textfield that allows updating of the text that is displayed inside the badge. The badge at the middle of the screen is always visible but can be hidden/made visible using the Show/Hide Me button. That transition uses a scale animation by calling .transition(.scale) modifier. For the badge at the top right of the screen (to the right of the text badge--->, which is inside the TestBadgeInsideHStackView, I'm trying to get that badge to only display when the user types in hello world into the text field. That works but I can't get the transition animation to work. This is a simplified version of my code but I'm ultimately trying to get the parent view to pass data down to a subview which then, based on the logic is either visible or not, but that visibility should be animated with a transition. Any ideas?
You should be able to cut and paste the code below to test/trial.
import SwiftUI
struct TestTransitionView: View {
@State var showView: Bool = true
@State var text: String = "hello"
var body: some View {
VStack {
TextField("textfield", text: $text)
.padding()
.border(.gray)
.padding()
TestBadgeInsideHStackView(shouldShowBadge: text == "hello world") {
Text(text.uppercased())
}
HStack {
if showView {
Text(text.uppercased())
.padding(.horizontal, 10)
.padding(.vertical, 7)
.background(Rectangle()
.fill(.gray).opacity(0.1)
.cornerRadius(6)
)
.font(.caption)
.fontWeight(.bold)
.transition(.scale)
}
}
.frame(height: 100)
.padding()
Spacer()
Button("Show/Hide Me") {
withAnimation {
showView.toggle()
}
}.padding(50)
}
}
}
struct TestBadgeInsideHStackView<Content: View> : View {
let content: Content
private var shouldShowBadge: Bool
init(shouldShowBadge: Bool, @ViewBuilder content: () -> Content) {
self.content = content()
self.shouldShowBadge = shouldShowBadge
}
var body: some View {
HStack {
Text("badge ---> ")
Spacer()
if shouldShowBadge {
TestBadgeView {
content
}
.transition(.scale)
}
}
.frame(height: 100)
.padding()
}
}
struct TestBadgeView<Content: View>: View {
let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
content
.padding(.horizontal, 10)
.padding(.vertical, 7)
.background(Rectangle()
.fill(.gray).opacity(0.1)
.cornerRadius(6)
)
.font(.caption)
.fontWeight(.bold)
.transition(.scale)
}
}
#Preview {
TestTransitionView()
}
The flag
showViewis being toggledwithAnimation, but changes to the text field are not associated with any form of animation. This is why the transition is not animated when a change is triggered due to the text being edited.Here are two ways to fix:
.animation()to the binding passed to theTextField:.animationmodifier to theHStackinTestBadgeInsideHStackView:BTW, I would suggest defining both the properties in
TestBadgeInsideHStackViewusingprivate let: