Offscreen StateObject are deallocated in a LazyVStack when contained within a Button, causing state to be lost

Originator:eric.horacek
Number:rdar://FB9900814 Date Originated:2/11/21
Status:Open Resolved:
Product:SwiftUI Framework Product Version:iOS 15.2
Classification:Incorrect/Unexpected Behavior Reproducible:Yes
 
The `Label` view of a `Button` that's contained within a `LazyVStack` loses any state stored in a `StateObject` when scrolled offscreen. Specifically, the `Label`'s `StateObject`-wrapped `ObservableObject` are deallocated once offscreen, and then are only re-initialized again when scrolled back onscreen with the `StateObject` reset to its initial value. 

Based on the definition of `StateObject`, the expected behavior would be that the `StateObject` continues to exist when offscreen. This is how `LazyVStack` content behaves when the the `Label` view is not contained within a `Button` and instead is added directly as content of the `LazyVStack`.

The attached project and video demonstrate the issue in practice. To reproduce the issue, enable one of the toggles, scroll it offscreen and then back onscreen, noting that the enabled state is lost. The following code reproduces the issue:

```
struct Example: View {
  var body: some View {
    ScrollView {
      LazyVStack {
        ForEach(0..<100) { index in
          Button { print("row \(index) tapped") } label: {
            Content(index: index)
          }
        }
      }
    }
  }
}

struct Content: View {
  var index: Int
  @StateObject var state = State()

  var body: some View {
    Toggle(String(index), isOn: $state.isOn)
  }

  class State: ObservableObject {
    @Published var isOn = false
  }
}
```

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!