RogerTheShrubber
RogerTheShrubber

Reputation: 1185

SwiftUI - Intermittent crash when presenting sheet on a sheet

I have a crash happening unpredictably when I'm presenting a sheet over a view that is already in a sheet. I have been able to slowly strip out parts of my view and custom types until I've got the very simple and generic view structure below that still exhibits the crash.

The crash happens when I interact with the TextField then interact with one of the buttons that shows the sub-sheet. It sometimes takes a lot of tapping around between the buttons and the text field to trigger the crash, and sometimes it happens right away (as in the GIF below). Sometimes I can't get the crash to happen at all, but my users keep reporting it.

In the gif below the crash occurs the minute the bottom button is pressed. You can see the button never comes out of its "pressed" state and the sheet never appears.

Xcode doesn't give any helpful info about the crash (screenshots included below).

I've only gotten it to happen on an iPhone XR running 13.4.1 and Xcode 11.4.1. I have tried on an iPhone 6s and several simulators and can't trigger the crash, but users have reported it on several devices.

struct ContentView: View {

    @State var showingSheetOne: Bool = false

    var body: some View {
        Button(action: { self.showingSheetOne = true }) {
            Text("Show")
        }
        .sheet(isPresented: $showingSheetOne) {
            SheetOne(showingSheetOne: self.$showingSheetOne)
        }
    }
}

struct SheetOne: View {

    @Binding var showingSheetOne: Bool
    @State var text = ""

    var body: some View {
        VStack {
            SheetTwoButton()
            SheetTwoButton()
            SheetTwoButton()
            TextField("Text", text: self.$text)
        }
    }

}

struct SheetTwo: View {

    @Binding var showing: Bool

    var body: some View {
        Button(action: {
            self.showing = false
        }) {
            Text("Hide")
                .frame(width: 300, height: 100)
                .foregroundColor(.white)
                .background(Color.blue)
        }
    }

}

struct SheetTwoButton: View  {

    @State private var showSheetTwo: Bool = false

    var body: some View {
        Button(action: { self.showSheetTwo = true } ) {
            Image(systemName: "plus.circle.fill")
                .font(.headline)
        }.sheet(isPresented: self.$showSheetTwo) {
            SheetTwo(showing: self.$showSheetTwo)
        }
    }

}

Crash screen recording Xcode Crash 01 Xcode Crash 02 Xcode Crash 03 Xcode Crash 04

Upvotes: 0

Views: 1276

Answers (2)

kwiknik
kwiknik

Reputation: 885

I'm using this in Swift 5.7, iOS 16.0:

#if !os(watchOS)
import UIKit
func dismissKeyboard() {
    // Dismiss text editing context menu
    UIMenuController.shared.hideMenu()
    // End any text editing - dismisses keyboard.
    UIApplication.shared.endEditing()
}
#endif

However, since iOS 16.0 there's now a warning:

'UIMenuController' was deprecated in iOS 16.0:
UIMenuController is deprecated. Use UIEditMenuInteraction instead.

I haven't yet figured out how to use UIEditMenuInteraction to do the same.

Upvotes: 0

Pedro Cavaleiro
Pedro Cavaleiro

Reputation: 932

I ran into a similar problem a few weeks ago. Turns out that when I presented the new sheet with the keyboard open it would lead to a crash.

I found using UIApplication.shared.endEditing() before showing the second sheet would solve the problem

UPDATE

For iOS 14 I’ve created an extension because the above function is no longer available

extension UIApplication {
    
    static func endEditing() {
        let resign = #selector(UIResponder.resignFirstResponder)
        UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil)
    }
    
}

The usage is similar UIApplication.endEditing()

Upvotes: 3

Related Questions