web-dev-qa-db-fra.com

Comment convertir une vue (pas UIView) en image?

Similaire à ce fil

Je voudrais convertir un SwiftUI View plutôt qu'un UIView à une image.

7
JHack

J'ai trouvé une solution lorsque vous pouvez enregistrer dans UIImage une vue SwiftUI qui n'est pas à l'écran. La solution semble un peu bizarre, mais fonctionne bien.

Créez d'abord une classe qui sert d'interaction entre UIHostingController et vous SwiftUI. Dans cette classe, définissez une fonction que vous pouvez appeler pour copier l'image de votre "Vue". Après cela, il vous suffit de "Publier" une nouvelle valeur pour mettre à jour vos vues.

class Controller:ObservableObject {

    @Published var update=false

    var img:UIImage?

    var hostingController:MySwiftUIViewHostingController?

    init() {

    }

    func copyImage() {
        img=hostingController?.copyImage()
        update=true
    }
}

Enveloppez ensuite votre vue SwiftUI que vous souhaitez copier via UIHostingController

class MySwiftUIViewHostingController: UIHostingController<TestView> {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func copyImage()->UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: self.view.bounds)

        return renderer.image(actions: { (c) in
            self.view.layer.render(in: c.cgContext)
        })
    }

}

La fonction copyImage () renvoie la vue du contrôleur comme UIImage

Vous devez maintenant présenter UIHostingController:

struct MyUIViewController:UIViewControllerRepresentable {

    @ObservedObject var cntrl:Controller

    func makeUIViewController(context: Context) -> MySwiftUIViewHostingController {
        let controller=MySwiftUIViewHostingController(rootView: TestView())
        cntrl.hostingController=controller
        return controller
    }

    func updateUIViewController(_ uiViewController: MySwiftUIViewHostingController, context: Context) {

    }

}

Et le reste comme suit:

struct TestView:View {

    var body: some View {
        VStack {
            Text("Title")
            Image("img2")
                .resizable()
                .aspectRatio(contentMode: .fill)
            Text("foot note")
        }
    }
}

import SwiftUI

struct ContentView: View {
    @ObservedObject var cntrl=Controller()
    var body: some View {
        ScrollView {
            VStack {
                HStack {
                    Image("img1")
                        .resizable()
                        .scaledToFit()
                        .border(Color.black, width: 2.0)
                        .onTapGesture(count: 2) {
                            print("tap registered")
                            self.cntrl.copyImage()
                    }
                    Image("img1")
                        .resizable()
                        .scaledToFit()
                        .border(Color.black, width: 2.0)
                }

                TextView()
                ImageCopy(cntrl: cntrl)
                    .border(Color.red, width: 2.0)
                TextView()
                TextView()
                TextView()
                TextView()
                TextView()
                MyUIViewController(cntrl: cntrl)
                    .aspectRatio(contentMode: .fit)
            }

        }
    }
}

struct ImageCopy:View {
    @ObservedObject var cntrl:Controller
    var body: some View {
        VStack {
            Image(uiImage: cntrl.img ?? UIImage())
                .resizable()
                .frame(width: 200, height: 200, alignment: .center)
        }

    }
}

struct TextView:View {

    var body: some View {
        VStack {
            Text("Bla Bla Bla Bla Bla ")
            Text("Bla Bla Bla Bla Bla ")
            Text("Bla Bla Bla Bla Bla ")
            Text("Bla Bla Bla Bla Bla ")
            Text("Bla Bla Bla Bla Bla ")

        }

    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Vous avez besoin d'img1 et img2 (celui qui est copié). J'ai tout mis dans un scrollview pour que l'on puisse voir que l'image se copie bien même lorsqu'elle n'est pas sur l'écran.

0
matyasl

Après réponse kontiki , voici la manière Préférences

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 {
        InviteView()
    }
}

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)
        }
    }
}

0
kumar shivang