可拖拽的修饰符会阻止选中项
我在研究一个带有文件夹和条目的树状数据结构。
我想在文件夹之间拖动条目和文件夹,使用 .draggable 与 dropDestination。
在我当前的代码中,拖拽可以工作,但对条目的选中不再起作用。如果我把 .draggable 注释掉,选中就会按预期工作。
如何确保同时实现选中和拖放?
我也尝试使用 Transferable 和 Codable,但结果一样。
编辑:我发现如果对 FolderRow(即子文件夹)禁用 .draggable,我就可以拖拽并选中所有条目。看起来,一旦在视图层级中存在一个 .draggable 修饰符位于 NavigationLink 之上,链接就不再工作。
下面是我的模型:
@Model
final class Folder {
@Attribute(.unique) var name: String
// Parent
var parentFolder: Folder?
// Child folders
@Relationship(deleteRule: .cascade, inverse: \Folder.parentFolder)
var subfolders: [Folder] = []
// Leaf entries
@Relationship(deleteRule: .cascade, inverse: \Entry.folder)
var entries: [Entry] = []
init(name: String, parentFolder: Folder? = nil) {
self.name = name
self.parentFolder = parentFolder
}
}
@Model
final class Entry {
@Attribute(.unique) var name: String
var detail: String
var folder: Folder? // recursive relationship
init(name: String, detail: String) {
self.name = name
self.detail = detail
}
}
以及其余代码:
@main
struct TestMacApp: App {
var body: some Scene {
WindowGroup {
SidebarView()
.modelContainer(for: Folder.self)
}
}
}
struct SidebarView: View {
@Environment(\.modelContext) private var context
@Query(filter: #Predicate<Folder> { $0.parentFolder == nil })
private var rootFolders: [Folder]
var body: some View {
NavigationSplitView {
List {
ForEach(rootFolders) { folder in
FolderRow(folder: folder)
}
}
.listStyle(.sidebar)
} detail: {
Text("detail")
}
.onAppear {
seed()
}
}
private func seed() {
do {
// delete current models
for folder: Folder in try context.fetchAllModels() {
context.delete(folder)
}
let rootFolder = Folder(name: "Root")
let entry1 = Entry(name: "One", detail: "Detail One")
let entry2 = Entry(name: "Two", detail: "Detail Two")
rootFolder.entries.append(contentsOf: [entry1, entry2])
let subFolder1 = Folder(name: "Sub1", parentFolder: rootFolder)
let entry3 = Entry(name: "Three", detail: "Detail Three")
let entry4 = Entry(name: "Four", detail: "Detail Four")
subFolder1.entries.append(contentsOf: [entry3, entry4])
context.insert(rootFolder)
}
catch {
debugPrint(error)
}
}
}
struct FolderRow: View {
// recursive
@Environment(\.modelContext) private var context
var folder: Folder
@State private var isExpanded: Bool = true
var body: some View {
DisclosureGroup(isExpanded: $isExpanded) {
// Subfolders
ForEach(folder.subfolders) { subfolder in
FolderRow(folder: subfolder)
.draggable(subfolder.name)
}
// Entries (leaf nodes)
ForEach(folder.entries) { entry in
NavigationLink {
VStack {
Text(entry.name)
Text(entry.detail)
}
} label: {
EntryRow(entry: entry)
}
.draggable(entry.name)
}
} label: {
Label(folder.name, systemImage: "folder")
}
.dropDestination(for: String.self) { names, _ in
return handleDrop(of: names)
}
}
private func handleDrop(of names: [String]) -> Bool {
do {
for name in names {
if let droppedEntry = try context.fetchFilteredModel(filter: #Predicate<Entry> { x in x.name == name }) {
droppedEntry.folder = folder
print("dropped \(droppedEntry.name) on \(folder.name)")
}
else if let droppedFolder = try context.fetchFilteredModel(filter: #Predicate<Folder> { x in x.name == name }) {
if droppedFolder.parentFolder != nil {
droppedFolder.parentFolder = folder
print("dropped \(droppedFolder.name) on \(folder.name)")
}
}
}
return true
}
catch {
debugPrint(error.localizedDescription)
return false
}
}
}
struct EntryRow: View {
var entry: Entry
var body: some View {
Text(entry.detail)
}
}
extension ModelContext { // convenience methods
func fetchAllModels<M>() throws -> [M] where M: PersistentModel {
let fetchDescriptor = FetchDescriptor<M>()
return try fetch(fetchDescriptor)
}
func fetchFilteredModels<M>(filter: Predicate<M>) throws -> [M] where M: PersistentModel {
let fetchDescriptor = FetchDescriptor<M>(predicate: filter)
return try fetch(fetchDescriptor)
}
func fetchFilteredModel<M>(filter: Predicate<M>) throws -> M? where M: PersistentModel {
return try fetchFilteredModels(filter: filter).first
}
}
解决方案
我找到了一个解决方案,我把用于 FolderRow 的 .draggable 修饰符移到了 DisclosureGroup 的 Label:
struct FolderRow: View {
@Environment(\.modelContext) private var context
var folder: Folder
@State private var isExpanded: Bool = true
var body: some View {
DisclosureGroup(isExpanded: $isExpanded) {
// Subfolders
ForEach(folder.subfolders) { subfolder in
FolderRow(folder: subfolder)
}
// Entries (leaf nodes)
ForEach(folder.entries) { entry in
NavigationLink(destination: EntryDetail(entry: entry)) {
EntryRow(entry: entry)
}
.draggable(entry.name)
}
} label: {
Label(folder.name, systemImage: "folder")
.draggable(folder.name) // <<<==== MOVE HERE
}
.dropDestination(for: String.self) { names, _ in
return handleDrop(of: names)
}
}
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。