Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A complete SwiftUI UIActivityViewController implementation.
import SwiftUI
import LinkPresentation
import CoreServices
struct ActivityView: UIViewControllerRepresentable {
private let activityItems: [Any]
private let applicationActivities: [UIActivity]?
private let completion: UIActivityViewController.CompletionWithItemsHandler?
@Binding var isPresented: Bool
init(isPresented: Binding<Bool>, items: [Any], activities: [UIActivity]? = nil, onComplete: UIActivityViewController.CompletionWithItemsHandler? = nil) {
_isPresented = isPresented
activityItems = items
applicationActivities = activities
completion = onComplete
}
func makeUIViewController(context: Context) -> ActivityViewControllerWrapper {
ActivityViewControllerWrapper(isPresented: $isPresented, activityItems: activityItems, applicationActivities: applicationActivities, onComplete: completion)
}
func updateUIViewController(_ uiViewController: ActivityViewControllerWrapper, context: Context) {
uiViewController.isPresented = $isPresented
uiViewController.completion = completion
uiViewController.updateState()
}
}
final class ActivityViewControllerWrapper: UIViewController {
var activityItems: [Any]
var applicationActivities: [UIActivity]?
var isPresented: Binding<Bool>
var completion: UIActivityViewController.CompletionWithItemsHandler?
init(isPresented: Binding<Bool>, activityItems: [Any], applicationActivities: [UIActivity]? = nil, onComplete: UIActivityViewController.CompletionWithItemsHandler? = nil) {
self.activityItems = activityItems
self.applicationActivities = applicationActivities
self.isPresented = isPresented
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
updateState()
}
fileprivate func updateState() {
let isActivityPresented = presentedViewController != nil
if isActivityPresented != isPresented.wrappedValue {
if !isActivityPresented {
let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
controller.popoverPresentationController?.sourceView = view
controller.completionWithItemsHandler = { [weak self] (activityType, success, items, error) in
self?.isPresented.wrappedValue = false
self?.completion?(activityType, success, items, error)
}
present(controller, animated: true, completion: nil)
}
}
}
}
struct ActivityViewTest: View {
@State private var isActivityPresented = false
var body: some View {
return Button("Share") {
self.isActivityPresented = true
}.background(ActivityView(isPresented: $isActivityPresented, items: ["Mock text"]))
}
}
struct ActivityView_Previews: PreviewProvider {
static var previews: some View {
ActivityViewTest()
.previewDevice("iPhone 8 Plus")
}
}
@shaps80

This comment has been minimized.

Copy link
Owner Author

@shaps80 shaps80 commented Nov 27, 2020

Note how the ActivityView is added as a background rather than via a modifier. This is intentional to get around some interoperability issues between UIKit and SwiftUI.

Also, popovers on iPad will 'just work' and even nested ActivityView's work without issue 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment