这是一个可能的解决方案,它使用UIHostingController
在 的背景中插入的rootViewController
:
func convertViewToData<V>(view: V, size: CGSize, completion: @escaping (Data?) -> Void) where V: View {
guard let rootVC = UIApplication.shared.windows.first?.rootViewController else {
completion(nil)
return
}
let imageVC = UIHostingController(rootView: view.edgesIgnoringSafeArea(.all))
imageVC.view.frame = CGRect(origin: .zero, size: size)
DispatchQueue.main.async {
rootVC.view.insertSubview(imageVC.view, at: 0)
let uiImage = imageVC.view.asImage(size: size)
imageVC.view.removeFromSuperview()
completion(uiImage.pngData())
}
}
Run Code Online (Sandbox Code Playgroud)
您还需要kontiki在这里asImage
提出的扩展的修改版本(设置是必要的,因为新设备可以拥有或扩展):UIGraphicsImageRendererFormat
2x
3x
extension UIView {
func asImage(size: CGSize) -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = 1
return UIGraphicsImageRenderer(size: size, format: format).image { context in
layer.render(in: context.cgContext)
}
}
}
Run Code Online (Sandbox Code Playgroud)
假设您有一些测试视图:
var testView: some View {
ZStack {
Color.blue
Circle()
.fill(Color.red)
}
}
Run Code Online (Sandbox Code Playgroud)
您可以将此视图转换为Data
可用于返回Image
(或UIImage
):
convertViewToData(view: testView, size: CGSize(width: 300, height: 300)) {
guard let imageData = $0, let uiImage = UIImage(data: imageData) else { return }
return Image(uiImage: uiImage)
}
Run Code Online (Sandbox Code Playgroud)
该Data
对象还可以保存到文件、共享...
struct ContentView: View {
@State var imageData: Data?
var body: some View {
VStack {
testView
.frame(width: 50, height: 50)
if let imageData = imageData, let uiImage = UIImage(data: imageData) {
Image(uiImage: uiImage)
}
}
.onAppear {
convertViewToData(view: testView, size: .init(width: 300, height: 300)) {
imageData = $0
}
}
}
var testView: some View {
ZStack {
Color.blue
Circle()
.fill(Color.red)
}
}
}
Run Code Online (Sandbox Code Playgroud)
尽管SwiftUI不提供将视图转换为图像的直接方法,但您仍然可以做到。这有点骇人听闻,但效果很好。
在下面的示例中,每当点击两个VStack时,代码就会捕获它们的映像。它们的内容将转换为UIImage(以后可以根据需要将其保存到文件中)。在这种情况下,我只在下面显示它。
请注意,可以对代码进行改进,但是它提供了入门的基础。我使用GeometryReader来获取要捕获的VStack的坐标,但是可以使用Preferences对其进行改进,使其更强大。如果需要了解更多信息,请查看提供的链接。
另外,为了将屏幕的区域转换为图像,我们确实需要一个UIView。该代码用于UIApplication.shared.windows[0].rootViewController.view
获取顶视图,但是根据您的情况,您可能需要从其他地方获取它。
祝好运!
这是代码(在iPhone Xr模拟器上测试过,Xcode 11 beta 4):
import SwiftUI
extension UIView {
func asImage(rect: CGRect) -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: rect)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
struct ContentView: View {
@State private var rect1: CGRect = .zero
@State private var rect2: CGRect = .zero
@State private var uiimage: UIImage? = nil
var body: some View {
VStack {
HStack {
VStack {
Text("LEFT")
Text("VIEW")
}
.padding(20)
.background(Color.green)
.border(Color.blue, width: 5)
.background(RectGetter(rect: $rect1))
.tapAction { self.uiimage = UIApplication.shared.windows[0].rootViewController?.view.asImage(rect: self.rect1) }
VStack {
Text("RIGHT")
Text("VIEW")
}
.padding(40)
.background(Color.yellow)
.border(Color.green, width: 5)
.background(RectGetter(rect: $rect2))
.tapAction { self.uiimage = UIApplication.shared.windows[0].rootViewController?.view.asImage(rect: self.rect2) }
}
if uiimage != nil {
VStack {
Text("Captured Image")
Image(uiImage: self.uiimage!).padding(20).border(Color.black)
}.padding(20)
}
}
}
}
struct RectGetter: View {
@Binding var rect: CGRect
var body: some View {
GeometryReader { proxy in
self.createView(proxy: proxy)
}
}
func createView(proxy: GeometryProxy) -> some View {
DispatchQueue.main.async {
self.rect = proxy.frame(in: .global)
}
return Rectangle().fill(Color.clear)
}
}
Run Code Online (Sandbox Code Playgroud)
按照kontiki 的回答,这里是 Preferences 方式
import SwiftUI
struct ContentView: View {
@State private var uiImage: UIImage? = nil
@State private var rect1: CGRect = .zero
@State private var rect2: CGRect = .zero
var body: some View {
VStack {
HStack {
VStack {
Text("LEFT")
Text("VIEW")
}
.padding(20)
.background(Color.green)
.border(Color.blue, width: 5)
.getRect($rect1)
.onTapGesture {
self.uiImage = self.rect1.uiImage
}
VStack {
Text("RIGHT")
Text("VIEW")
}
.padding(40)
.background(Color.yellow)
.border(Color.green, width: 5)
.getRect($rect2)
.onTapGesture {
self.uiImage = self.rect2.uiImage
}
}
if uiImage != nil {
VStack {
Text("Captured Image")
Image(uiImage: self.uiImage!).padding(20).border(Color.black)
}.padding(20)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
extension CGRect {
var uiImage: UIImage? {
UIApplication.shared.windows
.filter{ $0.isKeyWindow }
.first?.rootViewController?.view
.asImage(rect: self)
}
}
extension View {
func getRect(_ rect: Binding<CGRect>) -> some View {
self.modifier(GetRect(rect: rect))
}
}
struct GetRect: ViewModifier {
@Binding var rect: CGRect
var measureRect: some View {
GeometryReader { proxy in
Rectangle().fill(Color.clear)
.preference(key: RectPreferenceKey.self, value: proxy.frame(in: .global))
}
}
func body(content: Content) -> some View {
content
.background(measureRect)
.onPreferenceChange(RectPreferenceKey.self) { (rect) in
if let rect = rect {
self.rect = rect
}
}
}
}
extension GetRect {
struct RectPreferenceKey: PreferenceKey {
static func reduce(value: inout CGRect?, nextValue: () -> CGRect?) {
value = nextValue()
}
typealias Value = CGRect?
static var defaultValue: CGRect? = nil
}
}
extension UIView {
func asImage(rect: CGRect) -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: rect)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
347 次 |
最近记录: |