SwiftUI:添加.onDrag会阻止在macOS侧边栏中点击/选择项目

14

我在我的 macOS 应用程序边栏中有一个列表。当我添加 .onDrag 修改器时,拖动 NSItemProvider 可以正常工作,但点击/选择该项来触发 NavigationLink 会被阻止。

struct ContentView: View {
    var body: some View {
        NavigationView {
            List(Data.items) { item in
                NavigationLink(
                    destination: Text(item.text),
                    label: {
                        Text(item.text)
                          .lineLimit(5)
                           // Enabling dragging with .onDrag {} disables click and selection:
                          .onDrag { return NSItemProvider(object: item.url as NSURL) }
                    })
            }
            Text("Placeholder")
        }
    }
}

我如何在保持正常列表选择行为不变的同时添加拖动行为?

以下是最小可重现示例的其余部分:

import SwiftUI

@main
struct ListDragExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct Data {
    struct Item: Identifiable {
        let id = UUID()
        let text = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
        let url = URL(string: "http://example.com")!
    }
    static let items = [
        Item(),
        Item(),
        Item()
    ]
}

没有提供最小可复现示例,我们无法帮助您进行故障排除。 - lorem ipsum
谢谢@loremipsum!我已经添加了一个最小可复现示例。 - Frank R
我遇到了相同的问题,一个可拖动的视图在 NavigationLink 中会破坏正常的 NavigationLink 行为。有时可以点击链接,有时则不行。我也在寻找解决方案。箭头键可以正常工作,你可以上下移动列表,但使用鼠标似乎存在问题。 - cgiacomi
1
发生的情况是,当您单击文本时,单元格不会被选中。当您在文本外单击时,单元格会被选中。解决方法是使用一个列表,其中选择通过状态变量进行控制,并向您的文本添加TapGesture并更新状态变量。挑战在于区分命令单击和普通单击,因为选择行为应该有所不同。 - user1046037
1
这个 bug 两年后仍然很重要。如果你还没有的话,请提交一个 bug,让苹果尽快修复它。报告的 bug 越多,它被修复的机会就越大。顺便说一下,同样的代码在 iPadOS 上运行良好。 - Daniel
1个回答

6

我遇到同样的问题,根据我的了解,实际上是SwiftUI中的一个错误。我可能能够为讨论做出更多贡献:

  • 这与NavigationLink没有直接关联。我有一个带有自定义项目视图的普通List,并且它呈现相同的不稳定行为;
  • 在我的自定义项目视图中,有些区域没有实际的View,例如Text视图旁边的空白区域。当我点击那些区域(“背景”)时,仍然可以选择该项目,但拖动机制不起作用;
  • 当放置被拖动的项目时,接受放置的区域仅由目标项子视图的框架组成。如果我试图将其放置在List项目的“背景”区域上,它将无法接受。

由于我还没有足够的声望,请点击此链接查看图片。

总体而言,对我来说,无论接受拖动或放置的项目是否具有子视图(是一个简单的Text还是更复杂的布局,例如我在图片中展示的那个),只要鼠标光标命中其中一个子视图内部,拖动机制就会禁用选择,同时仅允许从它们的框架内开始或结束进行拖动和放置。

在下面的代码中,当单击“短”项右侧的区域时,选择机制将起作用,但是只有在拖动从Text视图的框架内部开始时才会触发。

struct ExampleList: View {
  private let data = ["Short", "Average", "Loooooooooooong"]
  @State private var selected = Set<String>()

  var body: some View {
    List(data, id: \.self, selection: $selected) { text in
      Text(text) // Clicking outside the frame selects it...
                 // ...but clicking inside starts the drag mechanism.
        .onDrag { NSItemProvider(object: text as NSString) }
    }
  }
}

希望这可以帮上忙。来自巴西的问候!


2
你的观察是正确的,点击文本不会选择单元格,而点击背景会选择单元格。解决方法是在文本上添加一个轻拍手势,并在该手势中更新你的 @State private var selected。挑战在于区分命令点击和普通点击,因为选择行为应该是不同的。 - user1046037
您可以使用 NSEvent.modifierFlags.contains(.command) 检查已按下的修改键。 .command 应该将一行添加到选择中。 .shift 应该选择从点击行到最后一行中已添加到选择中的所有内容。 - Daniel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接