Sho*_*ari 13 macos drag swift swiftui
在 MacO 上的 SwiftUI 中,实现时
onDrop(of supportedTypes: [String], isTargeted: Binding<Bool>?, perform action: @escaping ([NSItemProvider]) -> Bool) -> some View
我们会收到一个 NSItemProvider 数组,这使得可以在视图中删除多个项目。
实现时onDrag(_ data: @escaping () -> NSItemProvider) -> some View,如何提供多个item进行拖动?
我无法在网上找到任何多个项目拖动的示例,我想知道是否有另一种方法来实现拖动操作,允许我提供多个 NSItemProvider 或使用上述方法来实现它
我的目标是能够选择多个项目并准确地拖动它们,就像在 Finder 中发生的那样。为了做到这一点,我想提供一个 [URL] 作为 [NItemProvider],但目前我只能为每个拖动操作提供一个 URL。
实际上,您不需要[NSItemProvider]在 SwiftUI 中处理多个项目的拖放操作。由于无论如何您都必须在自己的选择管理器中跟踪多个选定的项目,因此在生成自定义拖动预览和处理放置时请使用该选择。
将新 MacOS 应用程序项目的替换ContentView为以下所有代码。这是如何使用 SwiftUI 拖放多个项目的完整工作示例。
要使用它,您必须选择一个或多个项目才能启动拖动,然后可以将其拖动到任何其他未选择的项目上。放置操作期间发生的结果会打印在控制台上。
我很快就把它们组合在一起,所以我的样本中可能存在一些效率低下的情况,但它似乎确实运作良好。
import SwiftUI
import Combine
struct ContentView: View {
private let items = ["Item 0", "Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7"]
@StateObject var selection = StringSelectionManager()
@State private var refreshID = UUID()
@State private var dropTargetIndex: Int? = nil
var body: some View {
VStack(alignment: .leading) {
ForEach(0 ..< items.count, id: \.self) { index in
HStack {
Image(systemName: "folder")
Text(items[index])
}
.opacity((dropTargetIndex != nil) && (dropTargetIndex == index) ? 0.5 : 1.0)
// This id must change whenever the selection changes, otherwise SwiftUI will use a cached preview
.id(refreshID)
.onDrag { itemProvider(index: index) } preview: {
DraggingPreview(selection: selection)
}
.onDrop(of: [.text], delegate: MyDropDelegate(items: items,
selection: selection,
dropTargetIndex: $dropTargetIndex,
index: index) )
.padding(2)
.onTapGesture { selection.toggle(items[index]) }
.background(selection.isSelected(items[index]) ?
Color(NSColor.selectedContentBackgroundColor) : Color(NSColor.windowBackgroundColor))
.cornerRadius(5.0)
}
}
.onReceive(selection.objectWillChange, perform: { refreshID = UUID() } )
.frame(width: 300, height: 300)
}
private func itemProvider(index: Int) -> NSItemProvider {
// Only allow Items that are part of a selection to be dragged
if selection.isSelected(items[index]) {
return NSItemProvider(object: items[index] as NSString)
} else {
return NSItemProvider()
}
}
}
struct DraggingPreview: View {
var selection: StringSelectionManager
var body: some View {
VStack(alignment: .leading, spacing: 1.0) {
ForEach(selection.items, id: \.self) { item in
HStack {
Image(systemName: "folder")
Text(item)
.padding(2.0)
.background(Color(NSColor.selectedContentBackgroundColor))
.cornerRadius(5.0)
Spacer()
}
}
}
.frame(width: 300, height: 300)
}
}
struct MyDropDelegate: DropDelegate {
var items: [String]
var selection: StringSelectionManager
@Binding var dropTargetIndex: Int?
var index: Int
func dropEntered(info: DropInfo) {
dropTargetIndex = index
}
func dropExited(info: DropInfo) {
dropTargetIndex = nil
}
func validateDrop(info: DropInfo) -> Bool {
// Only allow non-selected Items to be drop targets
if !selection.isSelected(items[index]) {
return info.hasItemsConforming(to: [.text])
} else {
return false
}
}
func dropUpdated(info: DropInfo) -> DropProposal? {
// Sets the proper DropOperation
if !selection.isSelected(items[index]) {
let dragOperation = NSEvent.modifierFlags.contains(NSEvent.ModifierFlags.option) ? DropOperation.copy : DropOperation.move
return DropProposal(operation: dragOperation)
} else {
return DropProposal(operation: .forbidden)
}
}
func performDrop(info: DropInfo) -> Bool {
// Only allows non-selected Items to be drop targets & gets the "operation"
let dropProposal = dropUpdated(info: info)
if dropProposal?.operation != .forbidden {
let dropOperation = dropProposal!.operation == .move ? "Move" : "Copy"
if selection.selection.count > 1 {
for item in selection.selection {
print("\(dropOperation): \(item) Onto: \(items[index])")
}
} else {
// /sf/answers/4852801971/
if let item = info.itemProviders(for: ["public.utf8-plain-text"]).first {
item.loadItem(forTypeIdentifier: "public.utf8-plain-text", options: nil) { (data, error) in
if let data = data as? Data {
let item = NSString(data: data, encoding: 4)
print("\(dropOperation): \(item ?? "") Onto: \(items[index])")
}
}
}
return true
}
}
return false
}
}
class StringSelectionManager: ObservableObject {
@Published var selection: Set<String> = Set<String>()
let objectWillChange = PassthroughSubject<Void, Never>()
// Helper for ForEach
var items: [String] {
return Array(selection)
}
func isSelected(_ value: String) -> Bool {
return selection.contains(value)
}
func toggle(_ value: String) {
if isSelected(value) {
deselect(value)
} else {
select(value)
}
}
func select(_ value: String?) {
if let value = value {
objectWillChange.send()
selection.insert(value)
}
}
func deselect(_ value: String) {
objectWillChange.send()
selection.remove(value)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1659 次 |
| 最近记录: |