共享 SwiftUI 视图的屏幕截图导致崩溃

Posted

技术标签:

【中文标题】共享 SwiftUI 视图的屏幕截图导致崩溃【英文标题】:Sharing Screenshot of SwiftUI view causes crash 【发布时间】:2020-10-10 16:17:52 【问题描述】:

我正在我的 SwiftUI 视图中抓取子视图的屏幕截图,以便立即传递到共享表以共享图像。

该视图是一组来自文本数组的问题,呈现为一堆卡片。我正在尝试获取问题的屏幕截图并使其与应用程序链接一起共享(使用愤怒的小鸟链接进行测试)。

我已经能够使用基本 Asperi 对以下问题的回答来捕获屏幕截图: How do I render a SwiftUI View that is not at the root hierarchy as a UIImage?

我的分享表启动,并且我已经能够使用“复制”功能来复制图像,所以我知道它实际上是在获取屏幕截图,但是每当我点击“消息”将其发送给某人时,或者如果我只是打开共享表,应用程序崩溃了。

消息说这是内存问题,但没有给出太多的问题描述。有没有解决此类问题的好方法?我认为这一定与在这种情况下如何保存屏幕截图有关。

这是我的 View 和 UIView 的扩展来渲染图像:

extension UIView 
    func asImage() -> UIImage 
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image  rendererContext in
            
            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
    

这是我的视图的缩略版本 - 按钮大约在中间,应该调用底部的私有函数,从 View/UIView 扩展中渲染图像,并将“questionScreenShot”变量设置为渲染图像,然后显示在共享表中。

struct TopicPage: View 
    var currentTopic: Topic
    @State private var currentQuestions: [String]
    @State private var showShareSheet = false
    @State var questionScreenShot: UIImage? = nil
    
    var body: some View 
        GeometryReader  geometry in
                Button(action: 
                    self.questionScreenShot = render()
                    if self.questionScreenShot != nil 
                        self.showShareSheet = true
                     else 
                        print("Did not set screenshot")
                    

                ) 
                    Text("Share Question").bold()
                
                .sheet(isPresented: $showShareSheet) 
                    ShareSheet(activityItems: [questionScreenShot!])
                
        
    

    
    private func render() -> UIImage 
        QuestionBox(currentQuestion: self.currentQuestions[0]).asImage()
    

【问题讨论】:

我正在寻找同样的东西,但我只想转换一个堆栈而不是整个屏幕。祝你好运。 你的情况如何?我认为,如果您将堆栈作为单独的 View 结构拉出,则此解决方案将起作用,就像我的示例中如何设置“QuestionBox”一样。我重置了我的手机并再次尝试,这个问题实际上已经解决了。所以这个可能有用。 仍在努力。当我弄清楚时会告诉你的。祝你好运。 嘿@AndiAna 你在哪里出现了重新声明错误?想知道您是否在某个地方有另一个同名的函数? 【参考方案1】:

我找到了一个似乎在这里工作的解决方案。我将 questionScreenShot 存储为 nil 的变量开始:

    @State var questionScreenShot: UIImage? = nil

然后我确保在视图出现时将其设置为“渲染”,这意味着它会加载 UIImage,因此如果用户单击“共享问题”,它将准备好被加载(我认为之前有一个问题共享完成后 UIImage 没有及时加载的地方)。

它还会在消失时将该变量设置回 nil。

.onAppear 
                self.currentQuestions = currentTopic.questions.shuffled()
                self.featuredQuestion = currentQuestions.last!
                self.questionScreenShot = render()
            
            .onDisappear 
                self.questionScreenShot = nil
                self.featuredQuestion = nil
            

【讨论】:

以上是关于共享 SwiftUI 视图的屏幕截图导致崩溃的主要内容,如果未能解决你的问题,请参考以下文章

iOS SwiftUI:ScrollView 忽略顶部安全区域

使用我的应用程序当前视图的 ShareActionProvider 共享屏幕截图

屏幕截图显示为空白 - Swift

如何使用 Eclipse 给 Android 模拟器截屏

delphi不用getdc如何屏幕截图

当相机在Android中的表面视图上以编程方式截屏