我希望让用户能够分享位置,但我不知道如何在SwiftUI中显示UIActivityViewController
。
我希望让用户能够分享位置,但我不知道如何在SwiftUI中显示UIActivityViewController
。
struct ShareSheet: UIViewControllerRepresentable {
// To setup the share sheet
struct Config {
let activityItems: [Any]
var applicationActivities: [UIActivity]?
var excludedActivityTypes: [UIActivity.ActivityType]?
}
// Result object
struct Result {
let error: Error?
let activityType: UIActivity.ActivityType?
let completed: Bool
let returnedItems: [Any]?
}
@Binding var isPresented: Bool
private let shareSheet: UIActivityViewController
init(
isPresented: Binding<Bool>,
config: Config,
onEnd: ((Result) -> Void)? = nil
) {
self._isPresented = isPresented
shareSheet = UIActivityViewController(
activityItems: config.activityItems,
applicationActivities: config.applicationActivities
)
shareSheet.excludedActivityTypes = config.excludedActivityTypes
shareSheet.completionWithItemsHandler = { activityType, completed, returnedItems, error in
onEnd?(
.init(
error: error,
activityType: activityType,
completed: completed,
returnedItems: returnedItems
)
)
// Set isPresented to false after complete
isPresented.wrappedValue = false
}
}
func makeUIViewController(context: Context) -> UIViewController {
UIViewController()
}
func updateUIViewController(
_ uiViewController: UIViewController,
context: Context
) {
if isPresented, shareSheet.view.window == nil {
uiViewController.present(shareSheet, animated: true, completion: nil)
} else if !isPresented, shareSheet.view.window != nil {
shareSheet.dismiss(animated: true)
}
}
}
extension View {
func shareSheet(
isPresented: Binding<Bool>,
config: ShareSheet.Config,
onEnd: ((ShareSheet.Result) -> Void)? = nil
) -> some View {
self.background(
ShareSheet(isPresented: isPresented, config: config, onEnd: onEnd)
)
}
}
有一个名为 SwiftUIX 的库,它已经为 UIActivityViewController
提供了包装器。以下是如何通过 .sheet()
快速呈现它的基本结构,应该放置在 var body: some View {}
中的某个位置。
import SwiftUIX
/// ...
@State private var showSocialsInviteShareSheet: Bool = false
// ...
.sheet(isPresented: $showSocialsInviteShareSheet, onDismiss: {
print("Dismiss")
}, content: {
AppActivityView(activityItems: [URL(string: "https://www.apple.com")!])
})
只需使用内省即可。然后您就可以轻松编写类似这样的代码:
YourView().introspectViewController { controller in
guard let items = viewModel.inviteLinkParams, viewModel.isSharePresented else { return }
let activity = UIActivityViewController(activityItems: items, applicationActivities: nil)
controller.present(activity, animated: true, completion: nil)
}
introspectViewController
的?我在 SwiftUI 标准库中找不到它。 - kelin感谢这个帖子中的有用回答。
我尝试解决“陈旧数据”问题。问题出在没有在“UIViewControllerRepresentable”中实现“updateUIViewController”。SwiftUI只调用一次“makeUIViewController”来创建视图控制器。而“updateUIViewController”方法负责根据SwiftUI视图的更改对视图控制器进行更改。
由于“UIActivityViewController”不允许更改“activityItems”和“applicationActivities”,所以我使用了一个包装视图控制器。“UIViewControllerRepresentable”将更新包装器,而包装器将根据需要创建一个新的“UIActivityViewController”来执行更新。
以下是我在应用程序中实现“分享”按钮的代码。该代码在iOS 13.4 beta上经过测试,该版本修复了几个SwiftUI的错误 - 不确定它是否适用于早期版本。
struct Share: View {
var document: ReaderDocument // UIDocument subclass
@State var showShareSheet = false
var body: some View {
Button(action: {
self.document.save(to: self.document.fileURL, for: .forOverwriting) { success in
self.showShareSheet = true
}
}) {
Image(systemName: "square.and.arrow.up")
}.popover(isPresented: $showShareSheet) {
ActivityViewController(activityItems: [ self.document.text, self.document.fileURL,
UIPrintInfo.printInfo(), self.printFormatter ])
.frame(minWidth: 320, minHeight: 500) // necessary for iPad
}
}
var printFormatter: UIPrintFormatter {
let fontNum = Preferences.shared.readerFontSize.value
let fontSize = ReaderController.readerFontSizes[fontNum < ReaderController.readerFontSizes.count ? fontNum : 1]
let printFormatter = UISimpleTextPrintFormatter(text: self.document.text)
printFormatter.startPage = 0
printFormatter.perPageContentInsets = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72)
return printFormatter
}
}
struct ActivityViewController: UIViewControllerRepresentable {
var activityItems: [Any]
var applicationActivities: [UIActivity]? = nil
@Environment(\.presentationMode) var presentationMode
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityViewController>)
-> WrappedViewController<UIActivityViewController> {
let controller = WrappedViewController(wrappedController: activityController)
return controller
}
func updateUIViewController(_ uiViewController: WrappedViewController<UIActivityViewController>,
context: UIViewControllerRepresentableContext<ActivityViewController>) {
uiViewController.wrappedController = activityController
}
private var activityController: UIActivityViewController {
let avc = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
avc.completionWithItemsHandler = { (_, _, _, _) in
self.presentationMode.wrappedValue.dismiss()
}
return avc
}
}
class WrappedViewController<Controller: UIViewController>: UIViewController {
var wrappedController: Controller {
didSet {
if (wrappedController != oldValue) {
oldValue.removeFromParent()
oldValue.view.removeFromSuperview()
addChild(wrappedController)
view.addSubview(wrappedController.view)
wrappedController.view.frame = view.bounds
}
}
}
init(wrappedController: Controller) {
self.wrappedController = wrappedController
super.init(nibName: nil, bundle: nil)
addChild(wrappedController)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
super.loadView()
view.addSubview(wrappedController.view)
wrappedController.view.frame = view.bounds
}
}
简单易用,带有完成回调和本地SwiftUI @Binding支持
import SwiftUI
struct ShareSheet: UIViewControllerRepresentable {
typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
@Binding var isPresented: Bool
@Binding var activityItem: String
let applicationActivities: [UIActivity]? = nil
let excludedActivityTypes: [UIActivity.ActivityType]? = nil
let callback: Callback?
func makeUIViewController(context: Context) -> UIActivityViewController {
let controller = UIActivityViewController(
activityItems: [activityItem],
applicationActivities: applicationActivities)
controller.excludedActivityTypes = excludedActivityTypes
controller.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in
callback?(activityType, completed, returnedItems, error)
isPresented = false
}
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
}
}
使用示例:
ShareSheet(isPresented: $showShareSheet, activityItem: $sharingUrl, callback: { activityType, completed, returnedItems, error in
print("ShareSheet dismissed: \(activityType) \(completed) \(returnedItems) \(error)")
})