Created
December 27, 2023 10:36
-
-
Save ihamadfuad/d423497dfdeb8b1c716f0731f2c79a87 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct DraggableScaleableRotatableModifier: ViewModifier { | |
@State private var showingAlert = false | |
@Binding var lastOffset: CGSize | |
@Binding var lastRotation: Angle | |
@Binding var offset: CGSize | |
@Binding var scale: CGFloat | |
@Binding var angle: Angle | |
@State private var isHighlighted = false | |
let onDuplicate: (() -> Void)? | |
let onDelete: (() -> Void)? | |
@GestureState private var twistAngle: Angle = .zero | |
func body(content: Content) -> some View { | |
let rotationGesture = RotationGesture(minimumAngleDelta: .degrees(10)) | |
.updating($twistAngle, body: { (value, state, _) in | |
state = value | |
DispatchQueue.main.async { | |
isHighlighted = true | |
} | |
}) | |
.onEnded { value in | |
DispatchQueue.main.async { | |
self.angle += value | |
isHighlighted = false | |
} | |
} | |
let dragGesture = DragGesture() | |
.onChanged({ (value) in | |
DispatchQueue.main.async { | |
self.offset = value.translation | |
isHighlighted = true | |
} | |
}) | |
.onEnded({ (value) in | |
DispatchQueue.main.async { | |
self.lastOffset.width += value.translation.width | |
self.lastOffset.height += value.translation.height | |
self.offset = .zero | |
isHighlighted = false | |
} | |
}) | |
let scaleGesture = MagnificationGesture() | |
.onChanged { value in | |
DispatchQueue.main.async { | |
withAnimation(.easeInOut(duration: 0.2)) { | |
self.scale = value.magnitude | |
isHighlighted = true | |
} | |
} | |
} | |
.onEnded({ value in | |
DispatchQueue.main.async { | |
isHighlighted = false | |
} | |
}) | |
let gestures = rotationGesture | |
.simultaneously(with: dragGesture) | |
.simultaneously(with: scaleGesture) | |
return content | |
.rotationEffect(angle + twistAngle) | |
.offset(x: offset.width + lastOffset.width, y: offset.height + lastOffset.height) | |
.scaleEffect(scale) | |
.gesture(gestures, including: .gesture) | |
.background( | |
Rectangle() | |
.foregroundStyle(.clear) | |
.padding() | |
.padding() | |
.padding() | |
.background(Rectangle().foregroundStyle(Color.green.opacity(isHighlighted ? 0.10 : 0.0))) | |
.rotationEffect(angle + twistAngle) | |
.offset(x: offset.width + lastOffset.width, y: offset.height + lastOffset.height) | |
.scaleEffect(scale) | |
.gesture(gestures, including: .gesture) | |
) | |
.onTapGesture(count: 1, perform: { | |
if onDelete != nil { | |
showingAlert.toggle() | |
} | |
}) | |
.alert(isPresented: $showingAlert) { | |
Alert( | |
title: Text("Delete"), | |
message: Text("Would you like to delete this?"), | |
primaryButton: .destructive(Text("Delete")) { | |
onDelete?() | |
}, | |
secondaryButton: .cancel() | |
) | |
} | |
.confirmationDialog("", isPresented: $showingAlert) { | |
if onDuplicate != nil { | |
Button("Duplicate") { onDuplicate?() } | |
} | |
Button("Delete", role: .destructive) { onDelete?() } | |
Button("Cancel", role: .cancel) { } | |
} message: { | |
Text("Options") | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment