ios – ImagePreview zoom problem SwiftUI

ios – ImagePreview zoom problem SwiftUI


I’m new to swiftUI so apologies if this can be a very primary query. At present, I’m constructing a view that enables customers to see pictures they’ve beforehand added to the appliance in full display screen.

I’m having many points with making an attempt so as to add zoom performance to the view (final 2 weeks I’ve struggled).
I managed to get pinch to zoom to work however it’s not very easy, in addition to this I’m unable to work out the best way to zoom in a specific location somewhat than simply centrally. Customers anticipate the place they pinch to mirror the place it zooms however this doesn’t work for me. I additionally need the person to have the power to navigate this zoomed in picture however the utility struggles to distinguish between drag to dismiss the view and the person desirous to pan the picture.
General the view is simply very laggy, not responsive and never superb UI. Most purposes have this potential so certainly I am lacking one thing, it can’t be this tough.

The opposite options I’ve seen have an identical problem to mine, the appliance can not have each drag to dismiss and pinch to zoom / pan to navigate because it can not differentiate between the 2.

struct FullScreenImageView: View {
    let picture: UIImage
    @Binding var selectedImage: UIImage?
    @Surroundings(.presentationMode) var presentationMode
    @State personal var showControls = false
    @State personal var dragOffset = CGSize.zero
    @State personal var scale: CGFloat = 1.0
    @State personal var contentOffset = CGSize.zero // Tracks the picture place when zoomed in

    var physique: some View {
        ZStack {
            // Background
            Shade.grey.ignoresSafeArea()
            Shade.black
                .opacity(1 - Double(abs(dragOffset.top) / 300))
                .ignoresSafeArea()

            // Picture
            Picture(uiImage: picture)
                .resizable()
                .scaledToFit()
                .ignoresSafeArea()
                .offset(x: contentOffset.width + dragOffset.width, y: contentOffset.top + dragOffset.top)
                .scaleEffect(scale)
                .gesture(
                    DragGesture()
                        .onChanged { worth in
                            if scale > 1.0 { // Solely enable dragging when zoomed in
                                dragOffset = worth.translation
                            } else {
                                dragOffset = CGSize(width: 0, top: worth.translation.top)
                            }
                        }
                        .onEnded { worth in
                            if scale > 1.0 {
                                // Replace content material offset and clamp inside bounds
                                let newOffset = CGSize(
                                    width: contentOffset.width + worth.translation.width,
                                    top: contentOffset.top + worth.translation.top
                                )
                                contentOffset = clampOffset(newOffset, scale: scale)
                                dragOffset = .zero
                            } else {
                                // Deal with drag-to-dismiss when not zoomed in
                                if abs(dragOffset.top) > 150 {
                                    presentationMode.wrappedValue.dismiss()
                                } else {
                                    withAnimation {
                                        dragOffset = .zero
                                    }
                                }
                            }
                        }
                )
                .gesture(
                    MagnificationGesture()
                        .onChanged { worth in
                            scale = max(1.0, worth) // Stop scale beneath 1.0
                        }
                        .onEnded { _ in
                            withAnimation {
                                scale = max(1.0, scale) // Reset scale to 1.0 if too small
                                contentOffset = clampOffset(contentOffset, scale: scale) // Clamp offset after zooming
                            }
                        }
                )
                .onTapGesture {
                    withAnimation {
                        showControls.toggle()
                    }
                }

            // Management Bar
            if showControls {
                VStack {
                    HStack {
                        // Shut Button
                        Button(motion: {
                            presentationMode.wrappedValue.dismiss()
                        }) {
                            Picture(systemName: "xmark")
                                .font(.title2)
                                .padding()
                        }
                        Spacer()
                        // Share Button
                        ShareLink(merchandise: Picture(uiImage: picture), preview: SharePreview("Receipt")) {
                            Picture(systemName: "sq..and.arrow.up")
                                .font(.title2)
                        }
                        .padding()
                        // Trash Button
                        Button(motion: {
                            selectedImage = nil
                            presentationMode.wrappedValue.dismiss()
                        }) {
                            Picture(systemName: "trash")
                                .foregroundColor(.purple)
                                .font(.title2)
                                .padding()
                        }
                    }
                    .body(maxWidth: .infinity)
                    .body(top: 45)
                    .background(
                        Shade(purple: 0.1, inexperienced: 0.1, blue: 0.1)
                            .opacity(1 - Double(abs(dragOffset.top) / 300))
                    )
                    .foregroundColor(.white)
                    .padding(.high, 10)
                    Spacer()
                }
                .transition(.transfer(edge: .high))
            }
        }
        .statusBarHidden(true)
    }

    // Perform to Clamp Offset to Stop Picture from Transferring Out of Bounds
    personal func clampOffset(_ offset: CGSize, scale: CGFloat) -> CGSize {
        // Calculate picture dimensions based mostly on the present scale
        let imageWidth = UIScreen.primary.bounds.width * scale
        let imageHeight = UIScreen.primary.bounds.top * scale

        // Calculate horizontal and vertical bounds
        let horizontalBound = (imageWidth - UIScreen.primary.bounds.width) / 2
        let verticalBound = (imageHeight - UIScreen.primary.bounds.top) / 2

        // Clamp the offset throughout the bounds
        let clampedWidth = min(max(offset.width, -horizontalBound), horizontalBound)
        let clampedHeight = min(max(offset.top, -verticalBound), verticalBound)

        return CGSize(width: clampedWidth, top: clampedHeight)
    }
}

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 
rooshohttps://www.roosho.com
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here


Latest Articles

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog.