从 SwiftUI 视图呈现 UIActivityViewController

Kil*_*ian 16 ios uiactivityviewcontroller swift swiftui

我正在尝试UIActivityViewController从 SwiftUI 视图中呈现(共享表)。我创建了一个名为ShareSheetconformed to的视图UIViewControllerRepresentable来配置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>) {

    }
}
Run Code Online (Sandbox Code Playgroud)

通过天真​​地这样做.sheet会导致以下情况。

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

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

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

Azh*_*mil 11

希望能帮到你,

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

  • 这是一个糟糕的解决方案,不应标记为已接受。` UIApplication.shared.windows.first?.rootViewController?.present(av, animation: true, completion: nil)` 在很多场景下都无法获取到正确的vc。 (2认同)

kba*_*all 5

至少在 iOS 14、Swift 5、Xcode 12.5 中,我只需将 包装在另一个视图控制器中就可以相当轻松地完成此任务UIActivityViewController。它不需要检查视图层次结构或使用任何第三方库。唯一的黑客部分是异步呈现视图控制器,这甚至可能不是必需的。具有更多 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
        }
    }
    
}
Run Code Online (Sandbox Code Playgroud)
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)
    }
}
Run Code Online (Sandbox Code Playgroud)


fuz*_*uzz 2

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

case pageSheet

部分覆盖底层内容的演示风格。

应用演示风格的方式:

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

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

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

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

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

  • 即使您将 SwiftUI 视图包装在“UIHostingController”中并将包装器的“modalPresentationStyle”设置为“pageSheet”,然后使用顶部控制器呈现上述内容,仍然几乎是全屏的(就像问题中的图片所示) 。不过,“pageSheet”当然受到尊重,因为设置为其他内容确实会改变外观。唉,我无法让床单半覆盖屏幕。 (4认同)
  • 啊,有趣的是,我什至没有想到在创建视图控制器时设置它。看来,当通过“.sheet()”显示它时,这并没有得到尊重。我也会研究链接的选项。 (3认同)