I have used the .gesture for dragging the piece of text.
What I want to happen is the remaining letters to move to the right such that 'g' takes the first position and the remaining letters move towards the right. But I am unable to figure out how I should do that.
struct TryingDrag: View {
let letters = Array("Begin Saving")
@State private var dragAmount = CGSize.zero
var body: some View {
HStack (spacing: 0) {
ForEach(0..<letters.count) { index in
let letter = String(letters[index])
LettersDrag(letter: letter)
}
}
}
}
struct LettersDrag: View {
let letter: String
@State private var dragAmount = CGSize.zero
var body: some View {
Text(letter).foregroundColor(.white)
.padding(5)
.font(.title)
.background(Color.red)
.offset(dragAmount)
.animation(.spring())
.gesture(
DragGesture()
.onChanged {
dragAmount = $0.translation
}
.onEnded { _ in
dragAmount = .zero
}
)
}
}
I want it to have similar behaviour to the following image (but without using list/forms):
You could use onDrag
and onDrop
to implement this. I had to change your structure a bit and added a model to handle your letters data.
How the dragging and dropping part works is described well here by Asperi. (Its also his idea + code, I have simply adapted it for your case)
import SwiftUI
import UniformTypeIdentifiers
struct LettersData: Identifiable, Equatable {
let id: Int
let letter: String
}
class Model: ObservableObject {
@Published var letters: [LettersData] = []
init(input: String) {
let inputArray = Array(input).map(String.init)
for i in 0..<inputArray.count {
letters.append(LettersData(id: i, letter: inputArray[i]))
}
}
}
struct TryingDrag: View {
@StateObject private var model = Model(input: "Begin Saving")
@State private var dragging: LettersData?
var body: some View {
HStack (spacing: 0) {
ForEach(model.letters) { letter in
LettersDrag(letter: letter.letter)
.onDrag {
self.dragging = letter
return NSItemProvider(object: String(letter.id) as NSString)
}
.onDrop(of: [UTType.text], delegate: DragRelocateDelegate(item: letter, listData: $model.letters, current: $dragging))
}
}
.animation(.default, value: model.letters)
}
}
struct LettersDrag: View {
let letter: String
var body: some View {
Text(letter).foregroundColor(.white)
.padding(5)
.font(.title)
.background(Color.red)
.animation(.spring())
}
}
struct DragRelocateDelegate: DropDelegate {
let item: LettersData
@Binding var listData: [LettersData]
@Binding var current: LettersData?
func dropEntered(info: DropInfo) {
if item != current {
let from = listData.firstIndex(of: current!)!
let to = listData.firstIndex(of: item)!
if listData[to].id != current!.id {
listData.move(fromOffsets: IndexSet(integer: from),
toOffset: to > from ? to + 1 : to)
}
}
}
func dropUpdated(info: DropInfo) -> DropProposal? {
return DropProposal(operation: .move)
}
func performDrop(info: DropInfo) -> Bool {
self.current = nil
return true
}
}
I hope that's what you are looking for.