fer0n
fer0n

Reputation: 1164

SwiftUI Animation on property change?

I want to animate the appearance of an item in a list. The list looks like this:

Text("Jim")
Text("Jonas")
TextField("New Player")
TextField("New Player") //should be animated when appearing (not shown until a name is typed in the first "New Player")

The last TextField should be hidden until newPersonName.count > 0 and then appear with an animation.

This is the code:

struct V_NewSession: View {
    @State var newPersonName: String = ""
    @State var participants: [String] = [
        "Jim",
        "Jonas"
    ]
    var body: some View {
        VStack(alignment: .leading) {
            ForEach(0...self.participants.count + 1, id: \.self) { i in
                // Without this if statement, the animation works
                // but the Textfield shouldn't be shown until a name is typed
                if (!(self.newPersonName.count == 0 && i == self.participants.count + 1)) {
                    HStack {
                        if(i < self.participants.count) {
                            Text(self.participants[i])
                        } else {
                            TextField("New Player",
                                      text: $newPersonName,
                                      onEditingChanged: { focused in
                                if (self.newPersonName.count == 0) {
                                    if (!focused) {
                                        handleNewPlayerEntry()
                                    }
                                }
                           })
                        }
                    }
                }
            }
            .transition(.scale)
            Spacer()
            }
    }
    
    func handleNewPlayerEntry() {
        if(newPersonName.count > 0) {
            withAnimation(.spring()) {
                participants.append(newPersonName)
                newPersonName = ""
            }
        }
    }
}

I know withAnimation(...) only applies to participants.append(newPersonName), but how can I get the animation to work on the property change in the if-statement? if ((!(self.newPersonName.count == 0 && i == self.participants.count + 1)).animation()) doesn't work.

Upvotes: 0

Views: 1420

Answers (2)

Hans Rietmann
Hans Rietmann

Reputation: 416

Is that approaching what you're attempting to build ?

struct ContentView: View {
    @State var newPersonName: String = ""
    @State var participants: [String] = [
        "Jim",
        "Jonas"
    ]
    @State var editingNewPlayer = false

    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            ForEach(participants, id: \.self) { participant in
                Text(participant)
                    .padding(.trailing)
                Divider()
            }
        
            Button(action: handleNewPlayerEntry, label: {
                TextField("New Player", text: $newPersonName, onEditingChanged: { edit in
                    editingNewPlayer = edit
                }, onCommit: handleNewPlayerEntry)
            })
        
            if editingNewPlayer {
                Button(action: handleNewPlayerEntry, label: {
                    TextField("New Player", text: $newPersonName) { edit in
                        editingNewPlayer = false
                    }
                })
            }
        }
        .padding(.leading)
        .frame(maxHeight: .infinity, alignment: .top)
        .transition(.opacity)
        .animation(.easeIn)
    }

    func handleNewPlayerEntry() {
        if newPersonName.count > 0 {
            withAnimation(.spring()) {
                participants.append(newPersonName)
                newPersonName = ""
                editingNewPlayer = false
            }
        }
    }
}

Upvotes: 0

jnpdx
jnpdx

Reputation: 52625

Your example code won't compile for me, but here's a trivial example of using Combine inside a ViewModel to control whether a second TextField appears based on a condition:


import SwiftUI
import Combine

class ViewModel : ObservableObject {
    @Published var textFieldValue1 = ""
    @Published var textFieldValue2 = "Second field"
    @Published var showTextField2 = false
    
    private var cancellable : AnyCancellable?
    
    init() {
        cancellable = $textFieldValue1.sink { value in
            withAnimation {
                self.showTextField2 = !value.isEmpty
            }
        }
    }
}

struct ContentView: View {
    @StateObject var viewModel = ViewModel()
    
    var body: some View {
        VStack {
            TextField("", text: $viewModel.textFieldValue1)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            if viewModel.showTextField2 {
                TextField("", text: $viewModel.textFieldValue2)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .transition(.scale)
            }
        }
    }
}

Upvotes: 2

Related Questions