rustproofFish
rustproofFish

Reputation: 999

Animation not applied to SwiftUI view state change

I'm trying to animate a change to the overlay applied to a SwiftUI TextField. The state of the overlay is controlled by a state variable (simple boolean) and most references suggest that adding .animation() to the binding of this variable within the Toggle control should do the trick. However, it doesn't seem to have an effect. I've tried adding the same modifier to the view used for the overlay and wrapping the component views for the overlay in animation blocks - again, no joy.

Interestingly I have noted an error when running the code either in the canvas or the simulator which may well be significant:

2020-02-25 22:00:19.360009+0000 Sandbox[9392:671490] invalid mode 'kCFRunLoopCommonModes' provided to CFRunLoopRunSpecific - break on _CFRunLoopError_RunCalledWithInvalidMode to debug. This message will only appear once per execution.

The code is as follows (it's prototyping so a bit rough and ready). Can anyone tell me what I'm missing?

import SwiftUI

struct ContentView: View {
    @State private var isValid = true

    var body: some View {
        VStack {
            Form {
                TextField("Placeholder", text: .constant(""))
                    .padding(6)
                    .background(ErrorView(isValid: $isValid)
                    .overlay(RoundedRectangle(cornerRadius: 4)
                            .stroke(Color(.systemGray3), lineWidth: 1))
                    .padding()

                Toggle(isOn: $isValid.animation(.easeInOut), label: { Text("Toggle Valid") })
            }
        }
    }
}

struct ErrorView: View {
    @Binding var isValid: Bool

    var body: some View {
        if isValid {
            return AnyView(EmptyView())
        } else {
            return AnyView(
                ZStack {
                    HStack {
                        Spacer()
                        Image(systemName: "exclamationmark.triangle.fill")
                            .foregroundColor(Color.red)
                            .padding(.trailing, 8)
                    }

                    HStack {
                        Spacer()
                        Text("error message")
                            .font(.caption)
                            .foregroundColor(Color.red)
                            .offset(x: 0, y: -28)
                    }
                }
            )
        }
    }
}

Upvotes: 1

Views: 2482

Answers (1)

Asperi
Asperi

Reputation: 257543

Here is possible approach, and a bit simplified, (tested & works with Xcode 11.2 / iOS 13.2)

demo

struct ContentView: View {
    @State private var isValid = true

    var body: some View {
        VStack {
            Form {
                TextField("Placeholder", text: .constant(""))
                    .padding(6)
                    .background(ErrorView().opacity(isValid ? 0.0 : 1.0))
                    .overlay(RoundedRectangle(cornerRadius: 4)
                            .stroke(Color(.systemGray3), lineWidth: 1))
                    .padding()

                Toggle(isOn: $isValid.animation(), label: { Text("Toggle Valid") })
            }
        }
    }
}

struct ErrorView: View {

    var body: some View {
        ZStack {
            HStack {
                Spacer()
                Image(systemName: "exclamationmark.triangle.fill")
                    .foregroundColor(Color.red)
                    .padding(.trailing, 8)
            }

            HStack {
                Spacer()
                Text("error message")
                    .font(.caption)
                    .foregroundColor(Color.red)
                    .offset(x: 0, y: -28)
            }
        }
    }
}

Upvotes: 2

Related Questions