在SwiftUI中展示“UIActivityViewController”

77

我希望让用户能够分享位置,但我不知道如何在SwiftUI中显示UIActivityViewController

15个回答

2
提出另一种解决方法
你可以创建一个“空视图控制器”来呈现该表单。
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)
    )
  }
}

2

使用 SwiftUIX 的示例

有一个名为 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")!])
})

这在iPhone上占据了整个屏幕,而不是你期望看到的正常的半屏幕。 - Mark Bridges

1

只需使用内省即可。然后您就可以轻松编写类似这样的代码:

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
1
抱歉,这是一个库:https://github.com/siteline/SwiftUI-Introspect - Paulo Cesar

0

感谢这个帖子中的有用回答。

我尝试解决“陈旧数据”问题。问题出在没有在“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
    }
}

-1

Swift 5 / SwiftUI原生

简单易用,带有完成回调和本地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)")
                    
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接