Update issue with ScrollView + ForEach

Hi,


when using an ForEach within an ScrollView the UI does not get updated if the ObservedObject, which is used in ForEach, changes. If the ForEach is not embedded in a ScrollView the update works.

IMHO the ForEach within the ScrollView should work, or am I wrong?


To illustrate the issue here's some example coding to paste in an (iOS) Playground.


import PlaygroundSupport
import SwiftUI

struct User: Hashable {
  var name: String
}

typealias Users = [User]

let usersData = [
  User(name: "Alpha"),
  User(name: "Beta"),
  User(name: "Gamma"),
]

class UserViewModel: ObservableObject {
  @Published var users: Users = []

  func get() {
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
      print("Update")
      self.users = usersData
    }
  }
}

// If the ScrollView is present nothing is shown after the model is updated.
// If a screen refresh is triggered (for example when running this on device and switch
// from portrait to landscape the UI shows up.
struct ContentView: View {
  @ObservedObject var user = UserViewModel()

  var body: some View {
    ScrollView { // comment scrollview out and it works
      ForEach(user.users, id: \.self) { user in
        Text("\(user.name)")
      }
    }
    .onAppear {
      self.user.get() // to simulate model update
    }
  }
}

PlaygroundPage.current.liveView = UIHostingController(rootView: ContentView())


Thanks for your Feeback!


Cheers, Michael

Answered by milutz in 419119022

To answer to myself with a workaround (in case anybody has the same issue and is looking for one), please see below:

Still I think the original code should work, right?


It seems that a ScrollView can't can't be initially empty:


struct ContentView: View {
  @ObservedObject var user = UserViewModel()

  var body: some View {
    Group {
      if user.users.isEmpty {
        EmptyView()
      } else {
        ScrollView { // comment scrollview out and it works
          ForEach(user.users, id: \.self) { user in
            Text("\(user.name)")
          }
        }
      }
    }
    .onAppear {
      self.user.get() // to simulate model update
    }
  }
}
Accepted Answer

To answer to myself with a workaround (in case anybody has the same issue and is looking for one), please see below:

Still I think the original code should work, right?


It seems that a ScrollView can't can't be initially empty:


struct ContentView: View {
  @ObservedObject var user = UserViewModel()

  var body: some View {
    Group {
      if user.users.isEmpty {
        EmptyView()
      } else {
        ScrollView { // comment scrollview out and it works
          ForEach(user.users, id: \.self) { user in
            Text("\(user.name)")
          }
        }
      }
    }
    .onAppear {
      self.user.get() // to simulate model update
    }
  }
}

Thanks for the feedback.


Don't forget to close the thread on your own answer.

@Claude31: There was one thing I needed to do before...

Issue is logged as FB7693466


.... hm it seems I can't close it right now. I always get an error. I will try again later.

Update issue with ScrollView + ForEach
 
 
Q