我有一个 SwiftUI 列表,它是 macOS 上的侧边栏。对于其项目,我添加了 dropDestination 修饰符,如下所示:
.dropDestination(for: URL.self) { urls, _ in
for url in urls {
//... adding urls to destination
}
}
return true
} isTargeted: { inDropArea in
if inDropArea {
highlightedItem = item
} else {
highlightedItem = nil
}
}
默认情况下,如果光标位于项目上方,我不会收到任何效果,但我想要与在 AppKit 中使用 NSOutlineView 相同的效果。以下是 Finder 中的示例:
如你所见,我在上面的代码中实现了
highlightedItem
。我可以用它来检查某个项目是否是目标并绘制背景:
.background {
if item == highlightedItem {
RoundedRectangle(cornerRadius: 5)
.fill(Color.blue)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
但这看起来不太一样:
有趣的是,如果您使用侧边栏列表的选择,我想要的效果是相同的:
List(selection: $selectedItem)
必须有一种本地方法来做到这一点,这样我就不必伪造它并得到看起来不太正确的东西。
让
List
元素可选择并暂时触发该选择似乎效果很好。
例如...
let Bands: Array<String> = [
"Beatles", "Rolling Stones", "Animals", "Beach Boys", "Doors",
]
struct ContentView: View {
@State var selection: String? = nil
@State var dropSelectionCache: String? = nil
var body: some View {
VStack {
Text("Drag text on onto band to trigger highlighting")
List(Bands, id: \.self, selection: $selection) { band in
Text(band)
.dropDestination(for: String.self) { _, _ in
true
} isTargeted: { (isTarget: Bool) in
if isTarget {
dropSelectionCache = selection
withAnimation {
selection = band
}
} else {
withAnimation {
selection = dropSelectionCache
}
}
}
}
}
}
}
这是一个 gif,展示了在 Ventura 上运行的代码(对屏幕录制的轻微抖动表示歉意 - 这是我的,而不是代码:-/)
shufflingb 的解决方法很好,但可能会对性能产生副作用,我找到了一个更简单的解决方案。
我为每个列表项创建了一个视图,并使用
.listRowBackground()
作为背景效果,而不仅仅是 .background
:
struct SidebarItemView: View {
@ObservedObject var item: Item
@State private var isTargeted = false
var body: some View {
NavigationLink(value: item) {
Label {
Text(item.title)
} icon: {
Image(systemName: "folder")
}
.onDrop(of: [.image], isTargeted: $isTargeted, perform: { itemProviders in
...
return true
})
}
.listRowBackground(
Color.secondary
.cornerRadius(5)
.opacity(isTargeted ? 1.0 : 0.0)
.padding(.horizontal, 10)
)
}
}