1

SwiftUI Changing State does not Dismiss Modal View

I have a basic master/detail app with SwiftUI life cycle. On the detail page, I have a button to toggle an @State to present a modal for editing the detail item. I am using the fullScreenCover modal. The @State variable in the Detail view is passed as an @Binding to the Edit view.

The Edit view has a "Done" button to dismiss itself. I have tried coding with both the binding and presentationMode methods.

This all works except that on RARE occasions tapping the "Done" button does not dismiss the Edit view. I then have to hard close the app and restart. The edits which were made are still saved as expected. There is simply no way to move from that screen.

The Detail view calls the Edit view like this:

    .fullScreenCover(isPresented: $showEditModalView) {//showEditView
        InvItemEditView(showEditModalView: $showEditModalView,
                        invItem: self.invItem,
                        inputCategory1: self.inputCategory1).environment(\.managedObjectContext, managedObjectContext)
    }//full screen

The Done button is coded as this:

    Button(action: {
        
        self.saveEditedRecord()
        
            print("Before change - Done button showEditModalView is \(self.showEditModalView)")
        self.showEditModalView = false
            print("I got passed showEditModalView = false")
        self.presentationMode.wrappedValue.dismiss()
            print("I got passed presentationMode dismiss")
            print("After change - Done button showEditModalView is \(self.showEditModalView)")
    }) {
        Text("Done")
            .font(.system(size: 20))
    }//trailing button
    .disabled(self.localDisableSaveButton)
    .disabled(self.dataStore.pubDisableEditButton)
    

The saveEditedRecord does just that - it issues the Core Data saveContext.

I can't replicate the error on demand. It just happens occasionally. I was curious to see that the Button action always executes all lines of code - I had expected it to terminate once the variable controlling the view presentation changed. I searched for others who may have had issues with fullScreenCover but found nothing relavant. I added the print statements to see if there was an issue setting the @State variable. Here is an example console output:

CoreData: debug: CoreData+CloudKit: -[NSCloudKitMirroringDelegate managedObjectContextSaved:](2092): <NSCloudKitMirroringDelegate: 0x2837a8680>: Observed context save: <NSPersistentStoreCoordinator: 0x2827ab4f0> - <NSManagedObjectContext: 0x2837a9790>
in invItemEditView saveEditedRecord, in do after context.save
Before change - Done button showEditModalView is true
I got passed showEditModalView = false
I got passed presentationMode dismiss
After change - Done button showEditModalView is false

When it fails, the first console statement after the Core Data item is:

Before change - Done button showEditModalView is false

Any guidance would be appreciated. Xcode 12.4 iOS 14.4

pawello2222
  • 46,897
  • 22
  • 145
  • 209
JohnSF
  • 3,736
  • 3
  • 36
  • 72

1 Answers1

0

I guess the problem is that you missed adding the presentation environment in the SwiftUI Modal View.

Here is a complete example in SwiftUI:

struct ContentView: View {

  @State private var showEditModalView = false
  @Environment(\.presentationMode) var presentationMode

  var body: some View {
    Button(action: {
        self.showEditModalView = true
    }) {
        Text("Show modal")
    }.fullScreenCover(isPresented: $showEditModalView) {
        InvItemEditView()
    }
  }
}


struct InvItemEditView: View {

  @Environment(\.presentationMode) private var presentationMode

  var body: some View {
    Group {
      Text("Modal view")
      Button(action: {
         self.presentationMode.wrappedValue.dismiss()
      }) {
          Text("Done")
            .font(.system(size: 20))
      }
    }
  }
}

I would also suggest reading this question: SwiftUI dismiss modal

Jonas Deichelmann
  • 3,513
  • 1
  • 30
  • 45
  • Thanks for the thoughts, Jonas. I do, in fact, include @Environment(\.presentationMode) with all three views - List, Detail, Edit. I also tried adding a separate dismiss button just to dismiss the Edit view. When I have a failure, that button produces no action, just like the Done button. I'm beginning to think this is a bug with the new .fullScreenCover. – JohnSF Jan 30 '21 at 19:28
  • Did you tried it without `self.showEditModalView = false` in the button action? My guess is, that you set the presentation bool to false and also trying to dismiss the presentation environment. That could be a little bit confusing. – Jonas Deichelmann Jan 31 '21 at 14:36
  • Yes. I have tried each, both and in any order. I'm going to run some tests - perhaps the Core Data save is causing some sort of async delay. If that does not lead anywhere, I'm pretty much out of ideas. – JohnSF Feb 01 '21 at 02:48
  • Yes, that could be. Try setting it to the end and running it async. Maybe that could be a solution. – Jonas Deichelmann Feb 01 '21 at 10:19