繁体   English   中英

从 SwiftUI 视图呈现 UIActivityViewController

[英]Presenting UIActivityViewController from SwiftUI View

我正在尝试从 SwiftUI 视图中呈现UIActivityViewController (共享表)。 我创建了一个符合UIViewControllerRepresentable的名为ShareSheet的视图来配置UIActivityViewController ,但事实证明它并不是那么简单。

struct ShareSheet: UIViewControllerRepresentable {
    typealias UIViewControllerType = UIActivityViewController

    var sharing: [Any]

    func makeUIViewController(context: UIViewControllerRepresentableContext<ShareSheet>) -> UIActivityViewController {
        UIActivityViewController(activityItems: sharing, applicationActivities: nil)
    }

    func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ShareSheet>) {

    }
}

通过.sheet天真地这样做会导致以下结果。

.sheet(isPresented: $showShareSheet) {
    ShareSheet(sharing: [URL(string: "https://example.com")!])
}

控制器演示失败的屏幕截图

有没有办法像通常呈现的那样呈现这个? 就像覆盖一半屏幕一样?

希望对你有帮助,

struct ShareSheetView: View {
    var body: some View {
        Button(action: actionSheet) {
            Image(systemName: "square.and.arrow.up")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(width: 36, height: 36)
        }
    }
    
    func actionSheet() {
        guard let data = URL(string: "https://www.apple.com") else { return }
        let av = UIActivityViewController(activityItems: [data], applicationActivities: nil)
        UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true, completion: nil)
    }
}

在此处输入图像描述

有一个UIModalPresentationStyle可用于显示某些演示文稿:

case pageSheet

一种部分覆盖基础内容的演示样式。

应用演示风格的方式:

func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
    let v = UIActivityViewController(activityItems: sharing, applicationActivities: nil)
    v.modalPresentationStyle = .pageSheet
    return v
}

可以在此处找到演示文稿列表:

https://developer.apple.com/documentation/uikit/uimodalpresentationstyle

我还没有自己测试过它们,所以如果最终没有像你预期的那样工作,我提前道歉。

或者,您可以查看他们提到第三方库的这个答案,这将允许您以通常呈现的方式创建半模态。

它不漂亮,但您可以像这样直接调用它(考虑到您的应用程序只有 1 个窗口):

UIApplication.shared.windows.first?.rootViewController?.present(activityViewController, animated: true, completion: nil)

如果你得到一些警告blablabla:

警告:尝试呈现……已经在呈现……

你可以做这样的事情来获得最顶部的视图 controller并调用它。

In iOS 14, Swift 5, Xcode 12.5 at least, I was able to accomplish this fairly easily by simply wrapping the UIActivityViewController in another view controller. 它不需要检查视图层次结构或使用任何 3rd 方库。 唯一骇人听闻的部分是异步呈现视图 controller,这甚至可能没有必要。 有更多 SwiftUI 经验的人可能能够提供改进建议。

import Foundation
import SwiftUI
import UIKit

struct ActivityViewController: UIViewControllerRepresentable {
        
    @Binding var shareURL: URL?
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIViewController(context: Context) -> some UIViewController {
        let containerViewController = UIViewController()
        
        return containerViewController

    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        guard let shareURL = shareURL, context.coordinator.presented == false else { return }
        
        context.coordinator.presented = true

        let activityViewController = UIActivityViewController(activityItems: [shareURL], applicationActivities: nil)
        activityViewController.completionWithItemsHandler = { activity, completed, returnedItems, activityError in
            self.shareURL = nil
            context.coordinator.presented = false

            if completed {
                // ...
            } else {
                // ...
            }
        }
        
        // Executing this asynchronously might not be necessary but some of my tests
        // failed because the view wasn't yet in the view hierarchy on the first pass of updateUIViewController
        //
        // There might be a better way to test for that condition in the guard statement and execute this
        // synchronously if we can be be sure updateUIViewController is invoked at least once after the view is added
        DispatchQueue.main.asyncAfter(deadline: .now()) {
            uiViewController.present(activityViewController, animated: true)
        }
    }
    
    class Coordinator: NSObject {
        let parent: ActivityViewController
        
        var presented: Bool = false
        
        init(_ parent: ActivityViewController) {
            self.parent = parent
        }
    }
    
}
struct ContentView: View {
    
    @State var shareURL: URL? = nil
    
    var body: some View {
        ZStack {
            Button(action: { shareURL = URL(string: "https://apple.com") }) {
                Text("Share")
                    .foregroundColor(.white)
                    .padding()
            }
            .background(Color.blue)
            if shareURL != nil {
                ActivityViewController(shareURL: $shareURL)
            }
        }
        .frame(width: 375, height: 812)
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM