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