4

Working with Beta4, it seems that the bug is still existing. The following sequence of views (a list, where a tap on a list entry opens another list) allows to present the ListView exactly once; the onDisappear is never called, so the showModal flag changes, but does not triggers the redisplay of ListView when tapped again. So, for each GridCellBodyEntry, the .sheet presentation works exactly once, and then never again.

I tried around with several suggestions and workarounds, but none worked (e.g., encapsulating with a NavigationViewModel). I even tried to remove the List, because there was an assumption that the List causes that behaviour, but even this did not change anything.

Are there any ideas around?

The setup:

  1. A GridCellBody with this view:
var body: some View {

        GeometryReader { geometry in

            VStack {

                List {

                    Section(footer: self.footerView) {

                        ForEach(self.rawEntries) { rawEntry in

                            GridCellBodyEntry(entityType: rawEntry)
                        }
                    }
                }
                .background(Color.white)
            }
        }
    }
  1. A GridCellBodyEntry with this definition:
struct GridCellBodyEntry: View {


    let entityType: EntityType
    let viewModel: BaseViewModel


    init(entityType: EntityType) {

        self.entityType = entityType
        self.viewModel = BaseViewModel(entityType: self.entityType)
    }


    @State var showModal = false {

        didSet {

            print("showModal: \(showModal)")
        }
    }


    var body: some View {

        Group {

            Button(action: {

                self.showModal.toggle()
            },
                   label: {

                    Text(entityType.localizedPlural ?? "")
                        .foregroundColor(Color.black)
            })
            .sheet(isPresented: $showModal, content: {

                ListView(showModal: self.$showModal,
                         viewModel: self.viewModel)
            })
        }.onAppear{
            print("Profile appeared")
        }.onDisappear{
            print("Profile disappeared")
        }
    }
}
  1. A ListView with this definition:
struct ListView: View {


    // MARK: - Private properties


    // MARK: - Public interface


    @Binding var showModal: Bool
    @ObjectBinding var viewModel: BaseViewModel


    // MARK: - Main view


    var body: some View {

        NavigationView {

            VStack {

                List {

                    Section(footer: Text("\(viewModel.list.count) entries")) {

                        ForEach(viewModel.list, id: \.objectID) { item in

                            NavigationLink(destination: ItemView(),
                                           label: {

                                            Text("\(item.objectID)")
                            })
                        }
                    }
                }
            }
            .navigationBarItems(leading:

                Button(action: {

                    self.showModal = false
                }, label: {

                    Text("Close")
                }))

            .navigationBarTitle(Text(viewModel.entityType.localizedPlural ?? ""))
        }
    }
}
  1. The BaseViewModel (excerpt):
class BaseViewModel: BindableObject {

    /// The binding support. 
    var willChange = PassthroughSubject<Void, Never>()

    /// The context.
    var context: NSManagedObjectContext

    /// The current list of typed items.
    var list: [NSManagedObject] = []

    // ... other stuff ...
}

where willChange.send() is called whenever something changes (create, modify, delete operations).

Hardy
  • 4,344
  • 3
  • 17
  • 27
  • I've *never* had this issue. Notsaying it isn't happening, but.... A thought. Could you try the following? (1) A very simple project - in fact, I posted a full answer with a repo today that works - anyways, one that simply shows a sheet and dismisses it, repeatedly. (2) Add a simply `List` that doesn't do much, only exhibits master/detail and state update. Then and only then, add in doing a modal sheet. Again, I'm not saying this isn't a bug (and if in fact it is a bug, have you file it with Apple?) I'm merely trying to rule out some things I haven't seen since beta 1. –  Jul 23 '19 at 20:05
  • Here's a link to my answer, with a link there to a beta 4 project, that uses a model, a sheet with a dismiss button, and is written for beta 4: https://stackoverflow.com/questions/57148220/swiftui-present-alert-with-input-field/57163461#57163461 –  Jul 23 '19 at 20:07
  • I took your repo project, and it worked. Then I changed ```VStack``` to ```List```, and it broke. Then I moved ```.sheet``` after ```padding()```(i.e., outside the ```List``` brackets, and it worked again. Put everything in a ```NavigationView```, still working. – Hardy Jul 24 '19 at 06:50
  • Wrapped the stuff inside the ```List``` into a ```ForEach```, still working. That is really strange, as this is now similar to my current code, which is not working. There is one major difference: While you submit the view model via ```environmentObject```, I hand it over as parameter. Will change that and see if it makes a difference. (Actually, from my understanding, this should not have an impact... but you never know...) – Hardy Jul 24 '19 at 06:53
  • Well, tried around a bit more: in the master, I took out ```GeometryReader```, ```VStack```, ```Section```- no change. In the detail, I took out ```NavigationView```, ```VStack```- no change. Before, I change to ```EnvironmentObject``` for handing over the view model - no change. Very strange... – Hardy Jul 24 '19 at 07:02
  • When looking into the debug statements, then we see that the flag is toggled correctly. For me it seems that the flag toggling does not trigger a reset of the ```.sheet``` internals, so that a subsequent change to ```true``` leads to an action there. – Hardy Jul 24 '19 at 07:12

1 Answers1

2

This is a variant of swiftUI PresentaionLink does not work second time

The following simplified code exhibits the behavior you're experiencing (the sheet only displays once):

import SwiftUI

struct ContentView: View {
    @State var isPresented = false
    @State var whichPresented = -1

    var body: some View {
        NavigationView {
            List {
                ForEach(0 ..< 10) { i in
                    Button(action: {
                            self.whichPresented = i
                            self.isPresented.toggle()
                        })
                        { Text("Button \(i)") }
                    }.sheet(isPresented: $isPresented, content: { 
Text("Destination View \(self.whichPresented)") })
                }
            }
    }
}

There appears to be a bug in SwiftUI when you put the .sheet inside a List or a ForEach. If you move the .sheet outside of the List, you should be able to get the correct behavior.

import SwiftUI

struct ContentView: View {
    @State var isPresented = false
    @State var whichPresented = -1

    var body: some View {
        NavigationView {
            List {
                ForEach(0 ..< 10) { i in
                    Button(action: {
                            self.whichPresented = i
                            self.isPresented.toggle()
                        })
                        { Text("Button \(i)") }
                    }
                }
            }.sheet(isPresented: $isPresented, content: { Text("Destination View \(self.whichPresented)") })
    }
}
Zain
  • 1,569
  • 1
  • 13
  • 19