ios – SwiftUI – Place and Measurement Situation with Signature Overlay on Zoomable Picture

Spread the love


I am engaged on an iOS app utilizing SwiftUI the place I’ve a picture that may be zoomed out and in. I am making an attempt so as to add a signature overlay to this picture that ought to scale and place accurately because the picture is zoomed. Nevertheless, I am encountering two points:

  1. The signature is all the time centered on the picture, and I need it to be
    positioned on the location I specify.
  2. The scale of the signature stays the identical whatever the zoom
    degree of the picture. I need it to scale together with the picture like right here.
  3. After every snapshot my picture on the background is shrinking bit by
    bit.

Picture for Drawback 1 and three offered right here.

I’ve offered the related code beneath. Can somebody please assist me perceive what I am doing incorrect and information me on the way to repair these points?

// MARK: - MY SNAPSHOT LOGIC
extension View {
    func snapshot(withSize targetSize: CGSize) -> UIImage {
        let controller = UIHostingController(rootView: self)
        let view = controller.view
        
        view?.bounds = CGRect(origin: .zero, dimension: targetSize)
        view?.backgroundColor = .clear
        
        let renderer = UIGraphicsImageRenderer(dimension: targetSize)
        
        return renderer.picture { _ in
            view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
        }
    }
}

// STARTING VIEW
struct AddingSignatureView3: View {
    
    @State var choosenSignature: UIImage = UIImage()
    @Binding var scannedImage: UIImage
    
    @State personal var signatureOffset: CGSize = .zero
    
    var physique: some View {
        VStack {
            
            ZStack {
                
                ScannedImageView(scannedImage: $scannedImage, signatureOffset: $signatureOffset)
                    .onTapGesture {
// That is to have the ability to see the quick results of snapshot
                        scannedImage = ScannedImageView(scannedImage: $scannedImage, signatureOffset: $signatureOffset).snapshot(withSize: scannedImage.dimension)
                    }
            }
            
            
        }

    }
}

// IMAGE WITH SIGNATURE IN ZSTACK
struct ScannedImageView: View {
    
    @Binding var scannedImage: UIImage
    @Binding var signatureOffset: CGSize
    
    @State personal var scale: CGFloat = 1
    @State personal var scaleAnchor: UnitPoint = .heart
    @State personal var lastScale: CGFloat = 1
    @State personal var offset: CGSize = .zero
    @State personal var lastOffset: CGSize = .zero
    @State personal var debug = ""
    
    var physique: some View {
        
        GeometryReader { geometry in
            
            let magnificationGesture = MagnificationGesture()
                .onChanged{ gesture in
                    scaleAnchor = .heart
                    scale = lastScale * gesture
                }
                .onEnded { _ in
                    fixOffsetAndScale(geometry: geometry)
                }
            
            let dragGesture = DragGesture()
                .onChanged { gesture in
                    var newOffset = lastOffset
                    newOffset.width += gesture.translation.width
                    newOffset.top += gesture.translation.top
                    offset = newOffset
                }
                .onEnded { _ in
                    fixOffsetAndScale(geometry: geometry)
                }
            
            ZStack {
                Picture(uiImage: scannedImage)
                    .resizable()
                    .scaledToFit()
                    .place(x: geometry.dimension.width / 2,
                              y: geometry.dimension.top / 2)
                    .scaleEffect(scale, anchor: scaleAnchor)
                    .offset(offset)
                    .gesture(dragGesture)
                    .gesture(magnificationGesture)
                //                    .body(width: geometry.dimension.width, top: geometry.dimension.top)
                
                DraggableSignatureView(signatureOffset: $signatureOffset)
            }
        }
    }
    
    personal func fixOffsetAndScale(geometry: GeometryProxy) {
        let newScale: CGFloat = .minimal(.most(scale, 1), 4)
        let screenSize = geometry.dimension
        
        let originalScale = scannedImage.dimension.width / scannedImage.dimension.top >= screenSize.width / screenSize.top ?
        geometry.dimension.width / scannedImage.dimension.width :
        geometry.dimension.top / scannedImage.dimension.top
        
        let imageWidth = (scannedImage.dimension.width * originalScale) * newScale
        
        var width: CGFloat = .zero
        if imageWidth > screenSize.width {
            let widthLimit: CGFloat = imageWidth > screenSize.width ?
            (imageWidth - screenSize.width) / 2
            : 0
            
            width = offset.width > 0 ?
                .minimal(widthLimit, offset.width) :
                .most(-widthLimit, offset.width)
        }
        
        let imageHeight = (scannedImage.dimension.top * originalScale) * newScale
        var top: CGFloat = .zero
        if imageHeight > screenSize.top {
            let heightLimit: CGFloat = imageHeight > screenSize.top ?
            (imageHeight - screenSize.top) / 2
            : 0
            
            top = offset.top > 0 ?
                .minimal(heightLimit, offset.top) :
                .most(-heightLimit, offset.top)
        }
        
        let newOffset = CGSize(width: width, top: top)
        lastScale = newScale
        lastOffset = newOffset
        withAnimation() {
            offset = newOffset
            scale = newScale
        }
    }
    
}

// MARK: - SIGNATURE VIEW
struct DraggableSignatureView: View {
    @Binding var signatureOffset: CGSize
    
    var physique: some View {
        Picture(systemName: "pencil.circle.fill") // You possibly can substitute this along with your signature view
            .font(.system(dimension: 48))
            .foregroundColor(.blue)
            .offset(signatureOffset)
            .gesture(
                DragGesture()
                    .onChanged { gesture in
                        signatureOffset = CGSize(
                            width: gesture.translation.width,
                            top: gesture.translation.top
                        )
                    }
            )
    }
}

I used to be capable of obtain taking a proper snapshot with completely different perform, however It scaled my picture to the dimensions of the iPhone display screen and never the true dimension of the picture, so I must keep away from this outcome.

I’ve tried to manage the place and dimension of the signature utilizing the signatureOffset variable, however it’s not working as anticipated. The signature is all the time centered, and its dimension would not change with the picture zoom. How can I modify this code to attain the specified habits?

Thanks in your assist!

Leave a Reply

Your email address will not be published. Required fields are marked *