6

In a project I have a ScrollView surrounding a VStack of items which each have a button to trigger the Activity View via the UIActivityViewController, but the Activity View doesn't appear.

I've reduced the project to the following code, which successfully displays the Activity View, but when I uncomment the ScrollView, the Activity View no longer appears when pressing the "Open Activity View" button.

Thanks!

import SwiftUI

class UIActivityViewControllerHost: UIViewController {
    var url: String = ""
    var completionWithItemsHandler: UIActivityViewController.CompletionWithItemsHandler? = nil

    override func viewDidAppear(_ animated: Bool) {
        let vc = UIActivityViewController(
            activityItems: [NSURL(string: url)!],
            applicationActivities: nil
        )
        vc.completionWithItemsHandler = completionWithItemsHandler
        present(vc, animated: true, completion: nil)
        super.viewDidAppear(animated)
    }
}

struct ActivityView: UIViewControllerRepresentable {
    var url: String
    @Binding var showing: Bool

    func makeUIViewController(context: Context) -> UIActivityViewControllerHost {
        let result = UIActivityViewControllerHost()
        result.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in
            self.showing = false
        }
        return result
    }
    func updateUIViewController(_ uiViewController: UIActivityViewControllerHost, context: Context) {
        uiViewController.url = url
    }
}

struct ContentView: View {
    @State var showSheet = false

    var body: some View {
//        ScrollView {
        Group {
            Button(action: {
                self.showSheet.toggle()
              }) {
                  Text("Open Activity View")
              }
            if showSheet {
                ActivityView(url: "https://www.wikipedia.org", showing: $showSheet)
                    .frame(width: 0, height: 0)
            }
        }
//    }
    }
}
sbdchd
  • 580
  • 1
  • 7
  • 18

1 Answers1

5

Here is the approach that works (tested with Xcode 11.2/iOS 13.2)... The extra wrapper host controller is not needed, and it is better to use native SwiftUI instruments for presentation.

import SwiftUI

struct ActivityView: UIViewControllerRepresentable {
    var url: String
    @Binding var showing: Bool

    func makeUIViewController(context: Context) -> UIActivityViewController {
        let vc = UIActivityViewController(
            activityItems: [NSURL(string: url)!],
            applicationActivities: nil
        )
        vc.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in
            self.showing = false
        }
        return vc
    }

    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
    }
}

struct TestUIActivityView: View {
    @State var showSheet = false

    var body: some View {
        ScrollView {
        Group {
            Button(action: {
                self.showSheet.toggle()
              }) {
                  Text("Open Activity View")
              }
            .sheet(isPresented: $showSheet) {
                ActivityView(url: "https://www.wikipedia.org", showing: self.$showSheet)
            }
        }
    }
    }
}

struct TestUIActivityView_Previews: PreviewProvider {
    static var previews: some View {
        TestUIActivityView()
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Thanks for the answer. Do you know how to put in the native variation. See also the following questions: https://stackoverflow.com/q/56700752/8642838 Your solution causes the sheet to run across the entire display – Jonas Deichelmann May 15 '20 at 23:15