Swiftui:如何快照视图然后共享?

Swiftui: How to snapshot a view then share?

我找到 this code for taking a snapshot of a view in SwiftUI, and also found this gist 如何在 SwiftUI 中调出 UIActivityController。它工作正常,但我遇到的最大问题是当你点击共享时 UIActivityController 是空白的,如果你再次点击共享它会按预期工作但我无法弄清楚为什么它第一次不起作用?如果我更改为静态图像或文本来共享,它会按预期工作吗?有什么想法吗?

 import SwiftUI

//construct enum to decide which sheet to present:
enum ActiveSheet: String, Identifiable { // <--- note that it's now Identifiable
    case photoLibrary, shareSheet
    var id: String {
        return self.rawValue
    }
}

struct ShareHomeView: View {
    
    @State private var shareCardAsImage: UIImage? = nil
    
    @State var activeSheet: ActiveSheet? = nil // <--- now an optional property
    
    var shareCard: some View {
        ZStack {
            VStack {
                Spacer()
                LinearGradient(
                    gradient: Gradient(colors: [.black, .red]),
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing
                )
                    .cornerRadius(10.0)
                    .padding(.horizontal)
                Spacer()
            }
            SubView()
                .padding(.horizontal)
            VStack {
                HStack {
                    HStack(alignment: .center) {
                        Image(systemName: "gamecontroller")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(height: 40)
                            .padding(.leading)
                        VStack(alignment: .leading, spacing: 3) {
                            Text("My App")
                                .foregroundColor(.white)
                                .font(.headline)
                                .fontWeight(.bold)
                            Text("Wed 30 Mar 22")
                                .foregroundColor(.white)
                                .font(.headline)
                            // .fontWeight(.bold)
                        }
                    }
                    Spacer()
                }
                .padding([.leading, .top])
                Spacer()
            }
            
        } //End of ZStack
        .frame(height: 350)
    }
    var body: some View {
        NavigationView {
            VStack {
                HStack {
                    Spacer()
                    Button {
                        self.activeSheet = .photoLibrary
                    } label: {
                        Image(systemName: "photo")
                            .resizable()
                            .scaledToFit()
                            .frame(height: 40)
                    }
                    .padding(.trailing)
                }
                //GeometryReader { geometry in
                shareCard
                // } //End of GeometryReader
                Button(action: {
                    
                    shareCardAsImage = shareCard.asImage()
                    self.activeSheet = .shareSheet
                    
                }) {
                    HStack {
                        Image(systemName: "square.and.arrow.up")
                            .font(.system(size: 20))
                        Text("Share")
                            .font(.headline)
                    }
                    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 50, maxHeight: 50)
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(20)
                }
                .padding(.horizontal)
            } //End of Master VStack
            //sheet choosing view to display based on selected enum value:
            .sheet(item: $activeSheet) { sheet in // <--- sheet is of type ActiveSheet and lets you present the appropriate sheet based on which is active
                switch sheet {
                case .photoLibrary:
                    Text("TODO")
                case .shareSheet:
                    if let unwrappedImage = shareCardAsImage {
                        ShareSheet(photo: unwrappedImage)
                    }
                    
                }
            }
            //Needed to Wrap in a Navigation View and hide title so that dark mode would work, otherwise this sheet was always in the iPhone's light or dark mode
            .navigationBarHidden(true)
            .navigationTitle("")
        }
    }
}

struct RecoveryShareHomeView_Previews: PreviewProvider {
    static var previews: some View {
        ShareHomeView().preferredColorScheme(.dark)
        ShareHomeView().preferredColorScheme(.light)
    }
}


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()
        
        let image = controller.view.asImage()
        controller.view.removeFromSuperview()
        return image
    }
}

extension UIView {
    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
            // [!!] Uncomment to clip resulting image
            //             rendererContext.cgContext.addPath(
            //                UIBezierPath(roundedRect: bounds, cornerRadius: 20).cgPath)
            //            rendererContext.cgContext.clip()
            
            // As commented by @MaxIsom below in some cases might be needed
            // to make this asynchronously, so uncomment below DispatchQueue
            // if you'd same met crash
            //            DispatchQueue.main.async {
            layer.render(in: rendererContext.cgContext)
            //            }
        }
    }
}






import LinkPresentation


//This code is from https://gist.github.com/tsuzukihashi/d08fce005a8d892741f4cf965533bd56

struct ShareSheet: UIViewControllerRepresentable {
    let photo: UIImage
    
    func makeUIViewController(context: Context) -> UIActivityViewController {
        //let text = ""
        //let itemSource = ShareActivityItemSource(shareText: text, shareImage: photo)
        
        let activityItems: [Any] = [photo]
        
        let controller = UIActivityViewController(
            activityItems: activityItems,
            applicationActivities: nil)
        
        return controller
    }
    
    func updateUIViewController(_ vc: UIActivityViewController, context: Context) {
        
    }
}



struct SubView: View {
    var body: some View {
        HStack {
            Image(systemName: "star")
            Text("Test View")
            Image(systemName: "star")
        }
        
        
    }
}

添加 [shareCardAsImage] 以便在 sheet:

中捕获当前值
.sheet(item: $activeSheet) { [shareCardAsImage] sheet in

这是必要的,因为您的 item 没有明确捕获它,这通常是 item 的用法。您还可以通过在 ActiveSheet 上添加关联值来解决此问题,该值将图像存储在 item.