5 Replies
      Latest reply on Nov 11, 2019 3:16 AM by adib
      ldidi Level 1 Level 1 (0 points)

        I would like to present an action sheet on iPad using SwiftUI, but it crashes with the following error:

         

        Terminating app due to uncaught exception 'NSGenericException',
        reason: 'Your application has presented a UIAlertController ()
        of style UIAlertControllerStyleActionSheet from _TtGC11AppAlpha17HostingControllerVS_11ContentView_
        (<_TtGC11AppAlpha17HostingControllerVS_11ContentView_: 0x105702dc0>).
        The modalPresentationStyle of a UIAlertController with this style is UIModalPresentationPopover.
        You must provide location information for this popover through the alert controller's popoverPresentationController.
        You must provide either a sourceView and sourceRect or a barButtonItem.
        If this information is not known when you present the alert controller, you may provide it
        in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'

         

        My code:

         

        .actionSheet(isPresented: $isShowingActionSheet) {
            ActionSheet(
                title: Text("Add a photo"),
                message: Text("Please select a source"),
                buttons: [
                    .cancel { },
                    .default(Text("Photo from library")) {
                        self.imageSource = .photoLibrary
                        self.isShowingImagePicker.toggle()
                    },
                    .default(Text("Take a picture")) {
                        self.imageSource = .camera
                        self.isShowingImagePicker.toggle()
                    }
                ]
            )
        }

         

        I understand that I have to pass a source view for the action sheet to layout properly, but I don't have access to the underlying UIKit element. How should it be done?

         

        Target: iPad

        Xcode 11.2 beta 2 (11B44)

        • Re: ActionSheet crash on iPad
          Jim Dovey Level 3 Level 3 (210 points)

          On the iPad, it seems you need to use .popover rather than .actionSheet. Sadly, ActionSheet() isn't a View type, so you can't just return your ActionSheet from the .popover content block. I've been playing around with it a bit, but I don't see an easy way to replicate the action sheet look-and-feel exactly. For example, I don't see any way to customize the popover's dimensions or background color at all; the view returned from the .popover builder block is simply placed inside an existing container of a static size (I'm guessing about 200x400 or something like that).

           

          Here's the code I've used to play with this so far:

           

          import SwiftUI
          import UIKit
          
          struct ContentView: View {
              @State var showingActionSheet = false
              @State var imageSource: UIImagePickerController.SourceType = .photoLibrary
              @State var showingPicker = false
          
              var body: some View {
                  VStack {
                      Button(action: {
                          self.showingActionSheet.toggle()
                      }) {
                          Text("Show action sheet")
                      }
                  }
                  .popover(isPresented: $showingActionSheet) {
                      VStack {
                          Text("Add a photo").bold().font(.largeTitle).foregroundColor(.secondary)
                          Text("Select a source").font(.title).foregroundColor(.secondary)
          
                          Divider()
          
                          Button(action: {
                              self.showingActionSheet = false
                          }) {
                              Text("Cancel").bold().font(.subheadline)//(.system(size: 18.0))
                          }
          
                          Divider()
          
                          Button(action: {
                              self.imageSource = .photoLibrary
                              self.showingPicker.toggle()
                              self.showingActionSheet = false
                          }) {
                              Text("Photo from library").font(.system(size: 18.0))
                          }
          
                          Divider()
          
                          Button(action: {
                              self.imageSource = .camera
                              self.showingPicker.toggle()
                              self.showingActionSheet = false
                          }) {
                              Text("Take a picture").font(.title)
                          }
                      }
                      .padding(.vertical)
                      .background(Color.green.opacity(0.6))
                      Spacer()
                  }
                  .padding(40)
                  .background(Color.yellow)
              }
          }
          
          struct ContentView_Previews: PreviewProvider {
              static var previews: some View {
                  ContentView()
              }
          }

           

          I'm planning to file a bug report on this; I strongly suggest you do the same.

          • Re: ActionSheet crash on iPad
            mprudhom Level 1 Level 1 (0 points)

            We've run into this one as well (FB #FB7397761, FWIW). You can see our workaround at https://stackoverflow.com/questions/56910941/present-actionsheet-in-swiftui-on-ipad/58490096#58490096

            • Re: ActionSheet crash on iPad
              adib Level 1 Level 1 (0 points)

              You could ask your own UIKit components to display the action sheet.

               

              Use view controller containment to enclose your SwiftUI view.  Have your own UIViewController subclass to enclose SwiftUI's view controller. In turn, have a property in the SwiftUI struct that is a closure that gets called to invoke the action sheet. Finally, initialize this closure from the enclosing UIViewController subclass to display that action sheet – in the closure you should have easy access to a UIView instance.