启动共享表以共享图像之前,请确保已渲染图像?

时间:2020-11-01 21:59:36

标签: swift swiftui

在启动尝试共享该图像的表格之前,是否有办法确保图像(应用程序的屏幕截图)已渲染?

我要启动一个共享表,该共享表在显示一叠纸牌的视图中使用当前的“纸牌”,然后对存储在变量“ questionScreenShot”中的纸牌截屏。该卡应该与链接一起共享-我正在使用来自这个问题的Asperi解决方案的一种变体来生成图像: How do I render a SwiftUI View that is not at the root hierarchy as a UIImage?

我第一次单击“共享问题”按钮时,该卡不共享(我仅获得链接)。但是,如果我继续运行该应用程序,然后再次单击“共享问题”,那么我会得到一个图像。

我有一个if语句,仅当图像具有值时才应该启动共享表。在ShareSheet启动之前,我有断点,断点应该朝不同的方向进行,因为根据下图中“ questionScreenShot”的值,该图像似乎仍然为空。 enter image description here

但是,当应用程序实际启动工作表时,它说我的打印输出中的图像有一些价值:

sharesheet has some value
sharesheet equals Optional(<UIImage:0x282fdb330 anonymous {300, 380}>)

这是我的文件,带有生成图像的视图扩展名:

import SwiftUI
import UIKit


extension UIView {
    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
            
                self.layer.render(in: rendererContext.cgContext)

        }
    }
}


extension View {
   
    func asImage() -> UIImage {
        let controller = UIHostingController(rootView: self)
        
        // locate far out of screen
        controller.view.frame = CGRect(x: 0, y: CGFloat(Int.max), width: 1, height: 1)
        UIApplication.shared.windows.first!.rootViewController?.view.addSubview(controller.view)
        
        let size = controller.sizeThatFits(in: UIScreen.main.bounds.size)
        controller.view.bounds = CGRect(origin: .zero, size: size)
        controller.view.sizeToFit()
        controller.view.backgroundColor = .clear
        
        let image = controller.view.asImage()
        controller.view.removeFromSuperview()
        return image
    }
    
}

这是我的页面结构,带有共享表单按钮,以及仅在image var具有值的情况下才应该启动共享表单的if语句:

struct TopicPage: View {
    //inherit current topic from the PackTopics page, and create a state variable that gets set to a random question.
    var currentTopic: Topic
    @State private var currentQuestions: [String]
    @State private var showShareSheet = false
    @State private var rect: CGRect = .zero
    let appURL: String = "https://apps.apple.com/us/app/im-curious/id1518506383"
    @State var questionScreenShot: UIImage? = nil
    @State var shareQuestionVisible: Bool = true
    
      
    var body: some View {
        GeometryReader { geometry in
            Blah Blah Blah
                Spacer()
                    .sheet(isPresented: $showShareSheet, onDismiss: {
                        questionScreenShot = nil
                        print("Question Screenshot reset to \(questionScreenShot ?? nil)")
                    }) {
                        ShareSheet(activityItems: [questionScreenShot ?? nil, appURL])
                }

        }
    //Set the background image.
            .navigationBarItems(trailing:
                                    Button(action: {
                                        self.questionScreenShot = render()
                if self.questionScreenShot != nil {
                    print("sharesheet has some value")
                    print("sharesheet equals \(questionScreenShot)")
                    self.showShareSheet = true
                } else {
                    print("Did not set screenshot")
                }

            }) {
                Text("Share Question").bold()
                                    }.disabled(!shareQuestionVisible)
            )
        }
        // Geometry Reader ignores all safe edges
        .edgesIgnoringSafeArea(.all)
    }
    func removeQuestion(at question: Int) {
        currentQuestions.remove(at: question)
        if currentQuestions.count == 0 {
            self.shareQuestionVisible = false
        }
    }
   

    private func render() -> UIImage {
        Card(color: currentTopic.color, text: self.currentQuestions[currentQuestions.count-1]).asImage()
    }
}

编辑:刚刚尝试过Asperi的解决方案,将启动共享工作表调用包装在DispatchQueue.main.async中,其行为似乎大致相同:

我尝试更改对ShareSheet的调用以匹配Asperi的建议,其行为大致相同。它不会第一次启动屏幕截图,但是会第二次启动屏幕截图。

                Spacer()
                    .sheet(isPresented: $showShareSheet, onDismiss: {
                        questionScreenShot = nil
                        print("Question Screenshot reset to \(questionScreenShot ?? nil)")
                    }) {
                        ShareSheet(activityItems: [questionScreenShot ?? nil, appURL])
                }

        }
    //Set the background image.
            .navigationBarItems(trailing:
                Button(action: {
                    self.questionScreenShot = render()
                    print("Question Screenshot now set to \(questionScreenShot ?? nil)")
                    DispatchQueue.main.async {
                        self.showShareSheet = true
                    }
            }) {

这是我得到的输出,因此看来第一次和第二次都正确设置了屏幕截图,然后在我关闭共享表时将其重置为nil。我确实收到CFMessagePort错误,但不确定是否与此有关。

Question Screenshot now set to Optional(<UIImage:0x162e24c80 anonymous {300, 380}>)
2020-11-02 07:20:02.917212-0600 Travel Conversation Starters[6492:694831] [PPT] Error creating the CFMessagePort needed to communicate with PPT.
Question Screenshot reset to nil
2020-11-02 07:20:12.731769-0600 Travel Conversation Starters[6492:695053] [ShareSheet] connection invalidated
Question Screenshot now set to Optional(<UIImage:0x162e1b970 anonymous {300, 380}>)
2020-11-02 07:20:20.276303-0600 Travel Conversation Starters[6492:694831] [PPT] Error creating the CFMessagePort needed to communicate with PPT.

注意:我确实更改了结构中的变量,使其指向我的Images.xcassets文件夹中的实际图像,并且它第一次加载了该图像。因此,似乎在第一次加载图像时速度过慢存在一些问题。

    @State var questionScreenShot: UIImage? = UIImage(named: "Test")

1 个答案:

答案 0 :(得分:0)

这与我对其他问题的回答有关:Sharing Screenshot of SwiftUI view causes crash

但是基本上,我只需要确保在视图中添加了一个OnAppear代码块,该代码块也将shareQuestion图像设置为render()。然后我将其重新设置为视图的OnDisappearnil。