实现一下SwiftUI拖动变化位置的功能。
涉及到的是使用 onDrag 和 onDrop 属性。
大体的思路就是在 onDrag 中记录下移动中的元素, 在 onDrop 中根据移动到的位置改变移动元素插入的位置。
有两个版本效果。
1. 首先简单版本效果。
代码如下:
import SwiftUI
struct Test: View {
@State private var targeted = true
@State private var tags = [0,1,2,3,4,5,6,7,8,9,10]
var body: some View {
let adaColumns = [
GridItem(.adaptive(minimum: 60, maximum: 80))]
VStack {
LazyVGrid(columns: adaColumns) {
ForEach(tags, id: \.self) { tag in
Text("\(tag)")
.font(.subheadline)
.padding(.vertical, 8)
.frame(maxWidth: .infinity)
.background(
Color.init(hexString: "#dddddd")
)
.cornerRadius(4)
.onDrag {
let item = NSItemProvider(object: NSString(string: "\(tag)"))
item.suggestedName = "\(tag)"
return item
}
.onDrop(of: [.text], isTargeted: $targeted) { providers in
print("drop \(providers.first) \(tag)")
guard let provider = providers.first, let item = provider.suggestedName else {
return false
}
let a = Int(item)
let i = tags.firstIndex(of: a!)
withAnimation {
tags.remove(at: i!)
let j = tags.firstIndex(of: tag)
tags.insert(a!, at: j!)
}
return true
}
}
}
.frame(maxWidth: .infinity)
}
.padding()
}
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test()
}
}
改进版效果
使用到了 DropDelegate 协议,能够获取移动中和碰撞的两个元素的事件,能够在运动中进行控制,方便实现更完美的动画。
import SwiftUI
import UniformTypeIdentifiers
final class TModel: NSObject {
var id: UUID
var tag: String
internal init(id: UUID, tag: String) {
self.id = id
self.tag = tag
}
}
enum MoveEnum: Int {
case left
case right
}
struct DragRelocateDelegate: DropDelegate {
let item: TModel
var listData: [TModel]
@Binding var current: TModel?
var moveAction: (MoveEnum) -> Void
func dropEntered(info: DropInfo) {
}
func dropUpdated(info: DropInfo) -> DropProposal? {
print("update location.x is \(info.location.x)")
if item != current {
if info.location.x > 30 {
moveAction(.right)
} else {
moveAction(.left)
}
}
return DropProposal(operation: .move)
}
func performDrop(info: DropInfo) -> Bool {
return true
}
}
struct Test1: View {
@State private var targeted = true
@State private var tags:[TModel]
@State var dragTag: TModel?
init() {
var __tags:[TModel] = []
for i in 0...10 {
__tags.append(TModel.init(id: UUID(), tag: "\(i)"))
}
_tags = State.init(initialValue: __tags)
}
var body: some View {
let adaColumns = [
GridItem(.adaptive(minimum: 60, maximum: 80))]
VStack {
LazyVGrid(columns: adaColumns) {
ForEach(tags, id: \.id) { item in
Text(item.tag)
.font(.subheadline)
.padding(.vertical, 8)
.frame(maxWidth: .infinity)
.background(
Color.init(hexString: "#dddddd")
)
.cornerRadius(4)
.onDrag {
let provider = NSItemProvider(object: NSString(string: item.id.uuidString))
provider.suggestedName = "tags"
dragTag = item
return provider
}
.onDrop(of: [.text], delegate: DragRelocateDelegate(item: item, listData: tags, current: $dragTag, moveAction: { direction in
if item == dragTag {
return
}
let i = tags.firstIndex(of: dragTag!)
withAnimation {
tags.remove(at: i!)
let j = tags.firstIndex(of: item)
if direction == .left {
tags.insert(dragTag!, at: j!)
} else {
tags.insert(dragTag!, at: j! + 1)
}
}
}))
}
}
.frame(maxWidth: .infinity)
}
.padding()
}
}
struct Test1_Previews: PreviewProvider {
static var previews: some View {
Test1()
}
}