拖放以在不使用 List/Form 的情况下对文本重新排序
Drag & drop to reorder for Text without using List/Form
我已经使用了.gesture 来拖动文本。
我想要发生的是剩余的字母向右移动,这样 'g' 占据第一个位置,剩余的字母向右移动。但是我不知道该怎么做。
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 = [=11=].translation
}
.onEnded { _ in
dragAmount = .zero
}
)
}
}
我希望它具有与下图类似的行为(但不使用 list/forms):
您可以使用 onDrag
和 onDrop
来实现它。我不得不稍微更改一下您的结构并添加一个模型来处理您的字母数据。
Asperi 很好地描述了拖放部分的工作原理。
(也是他的思路+代码,我针对你的情况简单做了改编)
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
}
}
希望这就是您要找的。
我已经使用了.gesture 来拖动文本。
我想要发生的是剩余的字母向右移动,这样 'g' 占据第一个位置,剩余的字母向右移动。但是我不知道该怎么做。
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 = [=11=].translation
}
.onEnded { _ in
dragAmount = .zero
}
)
}
}
我希望它具有与下图类似的行为(但不使用 list/forms):
您可以使用 onDrag
和 onDrop
来实现它。我不得不稍微更改一下您的结构并添加一个模型来处理您的字母数据。
Asperi 很好地描述了拖放部分的工作原理
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
}
}
希望这就是您要找的。