2

The below code is found on examples in several places and used to work for me, but now it crashes. The problem seems to be on iPadOS since it seems to work on iPhones, both simulators and physical devices.

The error is:

'UIPopoverPresentationController (<UIPopoverPresentationController: 0x155e1b5f0>) should have a non-nil sourceView or barButtonItem set before the presentation occurs.' terminating with uncaught exception of type NSException

Is this a bug on iPadOS or am I doing something wrong?

import SwiftUI

struct ContentView: View {
    @State var showSheet = false
    var body: some View {
        Button(action: actionSheet)
        {
            Label("", systemImage: "square.and.arrow.up")
        }
    }
    
    func actionSheet() {
        let av = UIActivityViewController(activityItems: ["Testing"], applicationActivities: nil)
        UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true, completion: nil)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Russ
  • 467
  • 3
  • 10
  • 2
    The error is pretty descriptive. Look [at this](https://stackoverflow.com/questions/67982691/ios-15-using-uisheetpresentationcontroller-in-swiftui/67994468#67994468) for an example on how to set the `sourceView` in a custom view controller. The `popover.adaptiveSheetPresentationController` code is the iOS 15 code you can skip all that to make it work for iOS 14 and below. – lorem ipsum Sep 08 '21 at 18:47
  • Check out https://stackoverflow.com/questions/56533564/showing-uiactivityviewcontroller-in-swiftui – jnpdx Sep 08 '21 at 19:53

1 Answers1

2

After seeing lorem ipsum's reply, I changed thing so the following and it works for both iOS 15 and and before.

func actionSheet() {
        let av = UIActivityViewController(activityItems: ["Testing"], applicationActivities: nil)
        
        av.popoverPresentationController?.sourceView = HostingView(rootView: self)
        UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true, completion: nil)
    }
    
    class HostingView<T: View>: UIView {
        
        private(set) var hostingController: UIHostingController<T>
        
        var rootView: T {
            get { hostingController.rootView }
            set { hostingController.rootView = newValue }
        }
        
        init(rootView: T, frame: CGRect = .zero) {
            hostingController = UIHostingController(rootView: rootView)
            
            super.init(frame: frame)
            
            backgroundColor = .clear
            hostingController.view.backgroundColor = backgroundColor
            hostingController.view.frame = self.bounds
            hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
            
            addSubview(hostingController.view)
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
Russ
  • 467
  • 3
  • 10
  • 1
    Take the if out. Only the adaptive part is iOS 15 your code doesn’t use it. – lorem ipsum Sep 08 '21 at 21:00
  • Thank you so much. I tried the other solution and it too worked, but this is more straightforward. – Russ Sep 08 '21 at 21:31
  • Im glad you figured out a solution. Just FYI the UIApplication part doesn’t work if you are on a sheet of any kind. That is because SwiftUI presents in that same spot. – lorem ipsum Sep 08 '21 at 22:17