如何在 SwiftUI 中保存带有定位签名的图像?

问题描述 投票:0回答:1

我正在 SwiftUI 中开发 iOS 应用程序,我正在尝试实现一项功能,用户可以将签名添加到图像并保存结果。目前,我有一个 SignatureAddingView,它允许用户将签名叠加层添加到图像并对其进行操作(缩放、旋转和拖动)。但是,我正在努力弄清楚如何使用添加的签名覆盖来保存图像。

这是我的代码的相关部分:

// View with image and signature

VStack {
            GeometryReader { geometry in
                let magnificationGesture = MagnificationGesture()
                    .onChanged{ gesture in
                        scaleAnchor = .center
                        scale = lastScale * gesture
                    }
                    .onEnded { _ in
                        fixOffsetAndScale(geometry: geometry)
                    }
                
                let dragGesture = DragGesture()
                    .onChanged { gesture in
                        var newOffset = lastOffset
                        newOffset.width += gesture.translation.width
                        newOffset.height += gesture.translation.height
                        offset = newOffset
                    }
                    .onEnded { _ in
                        fixOffsetAndScale(geometry: geometry)
                    }
                
                Image(uiImage: scannedImage)
                    .resizable()
                    .scaledToFit()
                    .position(x: geometry.size.width / 2,
                              y: geometry.size.height / 2)
                    .scaleEffect(scale, anchor: scaleAnchor)
                    .offset(offset)
                    .gesture(dragGesture)
                    .gesture(magnificationGesture)
            }
            .overlay (
                
                ZStack {
                    
                    if let image = loadImageFromDocumentDirectory(filename: "signature.png") {
                        
                        ZStack {
                            
                            Rectangle()
                                .stroke(style: StrokeStyle(lineWidth: 1, dash: [5]))
                                .fill(.blue)
                            
                            Image(uiImage: image)
                                .resizable()
                                .scaledToFit()
                            
                        }
//                        .overlay(
//                            VStack {
//                                HStack {
//
//                                    Spacer()
//
//                                    Circle()
//                                        .fill(Color.red) // Change the circle color as needed
//                                        .frame(width: 20, height: 20) // Adjust the size of the circle as needed
//                                }
//                                .padding(.trailing, -18)
//                                .padding(.top, -13)
//                                Spacer()
//                            }
//                        )
                        .overlay(
                            VStack {
                                Spacer()
                                HStack {
                                    
                                    Spacer()
                                    
                                    Circle()
                                        .fill(Color.green) // Change the circle color as needed
                                        .frame(width: 20, height: 20) // Adjust the size of the circle as needed
                                }
                                .padding(.trailing, -18)
                                .padding(.bottom, -13)
                            }
                        )
                        .position(location)
                        .gesture(
                            simpleDrag.simultaneously(with: fingerDrag)
                        )
                        .gesture(RotationGesture().onChanged { angle in
                            // Rotate the image here based on the angle value
                            // You can apply the rotation transformation to the image
                            // using .rotationEffect(angle, anchor: .center) or simila
                            DispatchQueue.main.async {
                                withAnimation {
                                    rotationAngle = Double(angle.degrees)
                                }
                            }
                        })
                        .gesture(
                            MagnificationGesture()
                                .onChanged { scaleValue in
                                    DispatchQueue.main.async {
                                        withAnimation {
                                            scaling = scaleValue
                                        }
                                        
                                    }
                                }
                        )
                        
                    }
                        
                    
                }
                .frame(width: 100, height: 100)
                .rotationEffect(.degrees(Double(rotationAngle)), anchor: .center)
                .scaleEffect(scaling)
                
            )
        }

// Function to save the modified image with the signature
func saveImageWithSignature() {
    // Capture the current state of the view as an image
    if let image = captureViewAsImage() {
        // Save the image to the document directory
        saveImageToDocumentDirectory(image: image, filename: "modified_image.png")
    }
}

// Function to capture the current state of the view as an image
func captureViewAsImage() -> UIImage? {
    let rect = UIScreen.main.bounds
    let window = UIApplication.shared.windows.first { $0.isKeyWindow }
    
    if let window = window {
        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
        window.drawHierarchy(in: rect, afterScreenUpdates: true)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
    
    return nil
}

// Function to save an image to the document directory
func saveImageToDocumentDirectory(image: UIImage, filename: String) {
    if let data = image.jpegData(compressionQuality: 1.0) {
        let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let fileURL = documentsDirectory.appendingPathComponent(filename)
        do {
            try data.write(to: fileURL)
            print("Image saved to document directory: \(fileURL)")
        } catch {
            print("Error saving image: \(error)")
        }
    }
}
swift swiftui overlay
1个回答
0
投票

总体思路是使用

GeometryReader
来传递要捕获的屏幕部分的框架。

这是一个使用屏幕捕获技术的简单示例,kontiki善意地提供了作为对另一篇文章的回答。从技术上讲,相机按钮也在

GeometryReader
的框架内,因为
GeometryProxy
需要位于点击回调的范围内。应用了 y 偏移,使其看起来按钮位于框架之外。或者,您可以将其保留在框架中并在快照期间将其隐藏。

struct ContentView: View {

    @State private var snapshot: UIImage?

    private var mainImage: some View {
        Image(systemName: "fish.fill")
            .resizable()
            .scaledToFit()
            .padding()
            .foregroundColor(Color(red: 0.9, green: 0.45, blue: 0.0))
            .background(Circle().foregroundColor(.white).frame(width: 40, height: 40).offset(x:80))
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background { Color(red: 0.47, green: 0.80, blue: 0.91) }
    }

    private var signature: some View {
        Image(systemName: "signature")
            .resizable()
            .scaledToFit()
            .foregroundColor(.black)
            .frame(height: 40)
            .padding()
            .background(.white.opacity(0.3))
            .shadow(color: .white, radius: 6)
            .padding()
    }

    private var cameraButton: some View {
        Image(systemName: "camera.fill")
            .resizable()
            .scaledToFit()
            .foregroundColor(Color(UIColor.systemBackground))
            .padding(10)
            .frame(height: 50)
            .background(Color(UIColor.secondaryLabel))
            .cornerRadius(8)
            .accessibilityAddTraits(.isButton)
    }

    private func captureSnapshot(rect: CGRect) -> UIImage? {
        var result: UIImage?
        if let scene = UIApplication.shared.connectedScenes.first(
            where: { $0.activationState == .foregroundActive }
        ) as? UIWindowScene {
            result = scene.windows[0].rootViewController?.view.asImage(rect: rect)
        }
        return result
    }

    @ViewBuilder
    private var framedSnapshot: some View {
        if let snapshot {
            Image(uiImage: snapshot)
                .resizable()
                .scaledToFit()
                .frame(width: 250)
                .padding(10)
                .border(.gray, width: 2)
                .background(.white)
        }
    }

    var body: some View {
        VStack {
            GeometryReader { proxy in
                ZStack(alignment: .bottom) {
                    mainImage
                        .overlay(alignment: .bottomTrailing) {
                            signature
                        }

                    cameraButton
                        .offset(y: 70)
                        .onTapGesture {
                            snapshot = captureSnapshot(rect: proxy.frame(in: .global))
                        }
                }
            }
            .frame(width: 350, height: 250)
            .padding(.bottom, 120)

            framedSnapshot

            Spacer()
        }
        .padding(.top, 60)
    }
}

// Credit to kontiki for the screenshot solution
// https://stackoverflow.com/a/57206207/20386264
extension UIView {
    func asImage(rect: CGRect) -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: rect)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }
}

Snapshot

© www.soinside.com 2019 - 2024. All rights reserved.