SwiftUI如何在ContentView加载完所有网络数据后执行Snapshot函数
问题根因
你当前定义的contentView计算属性每次调用都会生成一个全新的ContentView实例,这个新实例会重新初始化数据请求对象,还未完成数据加载,截图得到的是这个新实例的初始加载状态,和屏幕上已经渲染完成的带数据视图不是同一个对象,所以和预期结果不符。
修正方案
1. 调整ContentView结构,抽取待截图的视图
不要创建新的ContentView实例,直接复用当前已经加载好数据的视图作为截图对象:
extension View { func snapshot() -> UIImage { let controller = UIHostingController(rootView: self) let view = controller.view let targetSize = controller.view.intrinsicContentSize view?.bounds = CGRect(origin: .zero, size: targetSize) view?.backgroundColor = .clear let renderer = UIGraphicsImageRenderer(size: targetSize) return renderer.image { _ in view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true) } } } struct ContentView: View { // 小提示:Swift编码规范要求属性名用小驼峰,类名用大驼峰,后续可把fetchResults类改名为FetchResultsViewModel更符合规范 @ObservedObject var fetchResults = fetchResults() // 抽取待截图的、已经绑定了当前加载完成数据的视图 var contentToSnapshot: some View { VStack { Text(fetchResults.clout.postFound?.body ?? "n/a") .padding() .background(Color.blue) .mask(RoundedRectangle(cornerRadius: 30, style: .continuous)) .shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.3), radius: 10, x: 0, y:10) .padding() .frame(maxWidth: 300) } } var body: some View { Group { if fetchResults.dataHasLoaded { contentToSnapshot Button(action: { let image = contentToSnapshot.snapshot() print("手动截图得到的图片:\(image)") }, label:{Text("点击打印视图截图")}) } else { Text("加载数据中") } } // 如果需要数据加载完成后自动截图,添加下面这个修饰符即可 .onChange(of: fetchResults.dataHasLoaded) { hasLoaded in guard hasLoaded else { return } // 等待视图渲染完成后再执行截图,加极短延迟避免截到未渲染完成的视图 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { let autoCaptureImage = contentToSnapshot.snapshot() print("自动截图得到的图片:\(autoCaptureImage)") } } } }
2. 逻辑说明
dataHasLoaded标记为true后,SwiftUI需要极短时间完成视图重渲染,通过DispatchQueue.main.asyncAfter加0.1秒的延迟,可以保证截图时视图已经完成全部渲染,得到和屏幕显示一致的结果。
内容的提问来源于stack exchange,提问作者DeveloperDanK
相关产品推荐
相关产品推荐

