SwiftUI拖动变换位置

实现一下SwiftUI拖动变化位置的功能。
涉及到的是使用 onDrag 和 onDrop 属性。
大体的思路就是在 onDrag 中记录下移动中的元素, 在 onDrop 中根据移动到的位置改变移动元素插入的位置。
有两个版本效果。

1. 首先简单版本效果。

drop.gif

代码如下:

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 协议,能够获取移动中和碰撞的两个元素的事件,能够在运动中进行控制,方便实现更完美的动画。


update.gif
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()
    }
}

你可能感兴趣的:(SwiftUI拖动变换位置)