I wanted to make a little view that slides in with an error message:
enum SomeStates {
case good
case bad
var errorMessage: String? {
switch self {
case .good: return nil
case .bad: return "a sad error has occured"
}
}
}
struct ErrorForState: View {
@State var errorMessage: String?
@Binding var state: SomeStates
var body: some View {
return Group {
if let message = errorMessage {
Text(message)
.foregroundColor(Color(hex: 0x874b4b))
.padding(15)
.background(Color(hex: 0xffefef))
.cornerRadius(10)
.transition(.scale)
}
}
.onChange(of: state) { _ in
withAnimation(.spring()) {
errorMessage = state.errorMessage
}
}
}
}
And this works! My view detects when the binding changes and sets errorMessage
on itself in an withAnimation
block.
But I don't like using withAnimation
here because it's quite verbose, requires me to have an extra state variable errorMessage
and I'm not a fan of how it decouples details about the animation /from/ the thing being animated and /to/ the thing triggering the animation. I originally wanted to do something like this:
struct ErrorForState: View {
@Binding var state: SomeStates
var body: some View {
return Group {
if let message = state.errorMessage {
Text(message)
.foregroundColor(Color(hex: 0x874b4b))
.padding(15)
.background(Color(hex: 0xffefef))
.cornerRadius(10)
.transition(.scale)
.animation(Animation.easeInOut(duration: 1.0), value: state.errorMessage)
}
}
}
}
however this does not work, and instead appears/disappears instantly. In fact, every solution attempt I've made that makes use of .animation
on iOS 15 does not seem to work.
I suspect I'm not understanding how .animation
works - maybe I have it in the wrong spot above (maybe it's because my Text
view is rendered conditionally?). It also doesn't seem to work when attached to the Group
either.
CodePudding user response:
Here is an approach fro you:
Using Animation inside if or if let does not give you any benefit or result! because animation works with value changes, when you put it inside if, it got just initialized, do not use group unless you are sure you can replace it with group, use VStack instead.
struct ErrorForState: View {
@Binding var state: SomeStates
var body: some View {
return VStack(spacing: .zero) {
if let message = state.errorMessage {
Text(message)
.foregroundColor(Color(hex: 0x874b4b))
.padding(15)
.background(Color(hex: 0xffefef))
.cornerRadius(10)
.transition(.scale)
}
}
.animation(Animation.easeInOut(duration: 1.0), value: state.errorMessage)
}
}
CodePudding user response:
Here is an approach fro you:
Using Animation inside if or if let does not give you any benefit or result! because animation works with value changes, when you put it inside if, it got just initialized, do not use group unless you are sure you can replace it with group, use VStack instead.
struct ErrorForState: View {
@Binding var state: SomeStates
var body: some View {
return VStack(spacing: .zero) {
if let message = state.errorMessage {
Text(message)
.foregroundColor(Color(hex: 0x874b4b))
.padding(15)
.background(Color(hex: 0xffefef))
.cornerRadius(10)
.transition(.scale)
}
}
.animation(Animation.easeInOut(duration: 1.0), value: state.errorMessage)
}
}