I'm trying to drag a group of elements and I want the element to be on top of all other elements in the group when I drag it. I'm trying to change the zIndex of the dragged element but for some reason it doesn't work... Maybe someone who knows this stuff can point out my mistake
struct DragShapesView: View {
var body: some View {
let layout = [GridItem(), GridItem()]
LazyHGrid(rows: layout, spacing: 15) {
ForEach(1..<5) { i in
Circle()
.frame(width: 50, height: 50)
.overlay {
Text(String(i))
.font(.evolventaBold(25))
.foregroundColor(.white)
}
.draggable(
onChanged: { location in
},
onEnded: {
}
)
}
}
}
}
modifier draggable
is:
struct Draggable: ViewModifier {
@State private var location: CGPoint = .zero
@State private var offset: CGSize = .zero
@State private var zIndex: Double = 0
var onChanged: ((CGPoint) -> Void)?
var onEnded: (() -> Void)?
func body(content: Content) -> some View {
content
.zIndex(zIndex)
.offset(offset)
.gesture(
DragGesture(coordinateSpace: .global)
.onChanged { value in
zIndex = Double.infinity
offset = value.translation
onChanged?(value.location)
}
.onEnded { _ in
zIndex = .zero
offset = .zero
onEnded?()
}
)
}
}
extension View {
func draggable(onChanged: ((CGPoint) -> Void)?, onEnded: (() -> Void)?) -> some View {
self.modifier(Draggable(onChanged: onChanged, onEnded: onEnded))
}
}
Unfortunately, changing the zIndex
of items in a lazy grid does not work well.
LazyHGrid
based on the item being dragged. This is demonstrated in the answers to LazyVGrid is not redrawn on .id(). This would probably mean, passing a binding to Draggable
so that it can be updated with the id of the item being dragged..matchedGeometryEffect
can be used. This is demonstrated in the answer to Cant change zIndex of LazyVGrid element to bring it to the front with animation (it was my answer).To use .matchedGeometryEffect
in your case, the following changes are needed:
Here is an updated version of the example to show it working:
struct DragShapesView: View {
private let colors: [Color] = [.red, .orange, .yellow, .green]
private let layout = [GridItem(), GridItem()]
@Namespace private var ns
var body: some View {
LazyHGrid(rows: layout, spacing: 15) {
ForEach(Array(colors.enumerated()), id: \.offset) { i, _ in
Color.clear
.frame(width: 100, height: 100)
.matchedGeometryEffect(id: i, in: ns)
}
}
.overlay {
ForEach(Array(colors.enumerated()), id: \.offset) { i, color in
Circle()
.fill(color)
.overlay {
Text(String(i))
.font(.title) // .evolventaBold(25)
.foregroundStyle(.white)
}
.draggable(
onChanged: { location in },
onEnded: {}
)
.matchedGeometryEffect(id: i, in: ns, isSource: false)
}
}
}
}