4

I am still relatively new to SwfitUI and Combine so maybe I am trying to do something very incorrectly but I just cannot see how to achieve what I am aiming to do with SwiftUI and MVVM. Here is the scenario:

  • ViewModel class with a property @Published var items = [String]()
  • Main view (HomeView) that has a ForEach showing the items from its view model
  • HomeView has a @StateObject var viewModel: ViewModel
  • The HomeView ForEach uses the items from viewModel
  • ViewModel changes the items (in my case core data changes)
  • The HomeView ForEach reflects the change immediately

This all works, but what I want to do is animate the elements in the ForEach changing due to the viewModel.items changing.

What I can do is import SwiftUI into the ViewModel and use withAnimation to wrap the setting of new items. But this beats the purpose of the ViewModel as it now has a direct reference to UI code.

Here some code I have:

struct HomeView: View {
    @StateObject var viewModel: ViewModel

    var body: some View {
         ForEach(items) { item in
            Text(item)
        }
    }
}
import SwiftUI // This should not be imported as it breaks MVVM patter

class ViewModel {
    @Published var items = [String]()

    func onItemsChanged(_ newItems: [String]) {
       withAnimation { // This works but will break MVVM patter
          items = newItems
       }
    }
}

Any ideas if this can be achieve to make MVVM happy and work with SwiftUI?

Vladimir Amiorkov
  • 2,652
  • 3
  • 17
  • 35
  • Your View can observe some bool property at your ViewModel with Toggle e.g. Toggle("", isOn: $viewModel.someBoolProperty). This way you transfer the work to VIEW – Ethan Halprin May 31 '21 at 12:19

1 Answers1

4

Add animation to the container which hold your view items, like below

var body: some View {
  VStack {                      // << container
     ForEach(items) { item in
        Text(item)
    }
  }
  .animation(.default)          // << animates changes in items
}

See next posts for complete examples: https://stackoverflow.com/a/60893462/12299030, https://stackoverflow.com/a/65776506/12299030, https://stackoverflow.com/a/63364795/12299030.

Asperi
  • 228,894
  • 20
  • 464
  • 690
  • `.animation(Animation?)` has been deprecated with iOS 15. For iOS 16 you now need to use `.animation(Animation?, value: Equatable)`. So it would be `.animation(.default, value: items)`. You can stack multiple animation modifiers on one View and you need to specify this animation modifer for each property that you want to be animated. – InvisibleGorilla Jan 29 '23 at 15:30