SwiftUI 拖放

拖放是一种直观的手势,可以改善应用程序的 UX(用户体验)。

国际象棋 表情符号艺术 纸牌游戏 待办事项列表
Chess Drag-And-Drop Demo Emoji Art Drag-And-Drop Demo Card Game Drag-And-Drop Demo To Do List Drag-And-Drop Demo
文档 文档 文档 文档

本库的用途。

此库支持 SwiftUI 代码中的拖放功能。它是原生 onDrag / onDrop 及其局限性的替代方案。

本库适合哪些用例?

如果您的用例符合以下一个或多个条件,则此库是一个不错的选择

开始使用。

以下步骤将使您的项目能够使用该库的基本实现进行编译。

  1. 选择一种类型以符合 DropReceiver 并使结构体符合它
protocol DropReceiver {
    var dropArea: CGRect? { get set }
}

例如

struct MyDropReceiver: DropReceiver {
    var dropArea: CGRect? = nil
}
  1. 让您的 ViewModel 符合 DropReceivableObservableObject 并在 ViewModel 中添加一个引用 DropReceiver 结构体的变量。
protocol DropReceivableObservableObject: ObservableObject {
    associatedtype DropReceivable: DropReceiver
    
    func setDropArea(_ dropArea: CGRect, on dropReceiver: DropReceivable)
}

例如

class DragAndDropViewModel: DropReceivableObservableObject {
    typealias DropReceivable = MyDropReceiver
    
    var dropReceiver = MyDropReceiver()
        
    func setDropArea(_ dropArea: CGRect, on dropReceiver: DropReceivable) {
        dropReceiver.updateDropArea(with: dropArea)
    }
}
  1. 将 ViewModifier .dropArea(for:model:) 添加到您的 View 中代表 DropReceiver 结构体的元素。 for: 应该是 DropReceiver 结构体类型的元素,而 model: 应该是对 DropReceivableObservableObject ViewModel 的引用。

例如

struct MyDragAndDropView: View {
    @State var model = DragAndDropViewModel()
    
    var body: some View {
        VStack {
            Rectangle()
                .dropReceiver(for: model.dropReceiver, model: model)
        }
    }
}
  1. 将 ViewModifier .dragable() 添加到 View 中的任何其他元素。现在可以运行代码,并且该 View 是可拖动的。拖动时它将显示蓝色阴影。

例如

    var body: some View {
        VStack {
            Rectangle()
                .frame(width: 150, height: 150)
                .dropReceiver(for: model.dropReceiver, model: model)
                
            Spacer()
            
            Circle()
                .frame(width: 50, height: 50)
                .dragable()
        }
        .padding()
    }
  1. 要查看拖动的对象和放置接收器交互,请将这两个函数定义添加到 View 并将它们分别分配给 .dragable(onDragged:onDropped:)
    func onDragged(position: CGPoint) -> DragState {
        if model.dropReceiver.getDropArea()!.contains(position) {
            return .accepted
        } else {
            return .rejected
        }
    }
    
    func onDropped(position: CGPoint) -> Bool {
        model.dropReceiver.getDropArea()!.contains(position)
    }

此代码将允许用户拖动标记为 dragable 的对象,如果放置接收器位于拖动手势下方,则阴影现在将为绿色,否则为红色。

深入的实现示例。

这些示例旨在展示拖放的各种实现,并非旨在成为完整的应用程序。由于拖放是一种表示用户意图的方式,因此这些示例显示了捕获该意图的各种方法。

表情符号艺术。

查看代码。 | 阅读文档。

此示例显示了单个放置接收器(画布)和多个拖放对象(调色板上的表情符号和画布上的表情符号)。

待办事项应用。

查看代码。 | 阅读文档。

一个待办事项列表,其中每个列表对象都可以拖到“完成”框或“垃圾箱”框的顶部。“添加新”按钮可以拖到列表的顶部以添加新对象。

国际象棋棋盘。

查看代码。 | 阅读文档。

此示例展示了如何在国际象棋棋盘上实现拖放棋子。唯一的移动规则是基本的方向规则。移动规则不强制检查、将死、回合顺序或棋盘环绕(也就是说,a3 上的主教可以移动到 h3 如此处所述)。

纸牌游戏。

查看代码。 | 阅读文档。

在这个游戏中,玩家可以在三个可玩区域之一玩牌:这里、那里或那边。每张牌都可以在这些区域中的 1 个或多个中玩。

使用非矩形放置区域。

阅读教程。

本教程介绍了如何在地图上或非矩形网格中使用非矩形放置区域。