SwiftUI NavigationLink 双击列表 MacOS
SwiftUI NavigationLink double click on List MacOS
谁能想到在 MacOS 中双击列表中的 NavigationLink 时如何调用操作?我试过添加 onTapGesture(count:2) 但它没有达到预期的效果,并且覆盖了 link 的可靠选择能力。
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink(destination: Item(itemDetail: item)) {
ItemRow(itemRow: item) //<-my row view
}.buttonStyle(PlainButtonStyle())
.simultaneousGesture(TapGesture(count:2)
.onEnded {
print("double tap")
})
}
}
}
}
编辑:
我在 NavigationLink 中设置了 tag/selection,现在可以双击或单击该行的内容。唯一的麻烦是,虽然显示了 itemDetail 视图,但 link 上没有出现带有重音符号的“活动”状态。有没有办法设置活动状态(突出显示状态)或扩展 NavigationLink 功能以接受双击和单击?
@State var selection:String?
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink(destination: Item(itemDetail: item), tag: item.id, selection: self.$selection) {
ItemRow(itemRow: item) //<-my row view
}.onTapGesture(count:2) { //<- Needed to be first!
print("doubletap")
}.onTapGesture(count:1) {
self.selection = item.id
}
}
}
}
}
使用同步手势,如下所示(使用 Xcode 11.4 / macOS 10.15.5 测试)
NavigationLink(destination: Text("View One")) {
Text("ONE")
}
.buttonStyle(PlainButtonStyle()) // << required !!
.simultaneousGesture(TapGesture(count: 2)
.onEnded { print(">> double tap")})
或.highPriorityGesture(...
如果你需要双击有更高的优先级
寻找类似的解决方案,我尝试了@asperi 的回答,但在可点击区域方面遇到了与原始海报相同的问题。在尝试了许多变体之后,以下对我有用:
@State var selection: String?
...
NavigationLink(destination: HistoryListView(branch: string), tag: string, selection: self.$selection) {
Text(string)
.gesture(
TapGesture(count:1)
.onEnded({
print("Tap Single")
selection = string
})
)
.highPriorityGesture(
TapGesture(count:2)
.onEnded({
print("Tap Double")
})
)
}
我已经尝试了所有这些解决方案,但主要问题是使用 gesture
或 simultaneousGesture
覆盖列表视图上的默认单击手势,select列表。因此,我想到了一个简单的方法来保留默认的单击手势(select 行)并单独处理双击。
struct ContentView: View {
@State private var monitor: Any? = nil
@State private var hovering = false
@State private var selection = Set<String>()
let fruits = ["apple", "banana", "plum", "grape"]
var body: some View {
List(fruits, id: \.self, selection: $selection) { fruit in
VStack {
Text(fruit)
.frame(maxWidth: .infinity, alignment: .leading)
.clipShape(Rectangle()) // Allows the hitbox to be the entire word not the if you perfectly press the text
}
.onHover {
hovering = [=10=]
}
}
.onAppear {
monitor = NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown) {
if [=10=].clickCount == 2 && hovering { // Checks if mouse is actually hovering over the button or element
print("Double Tap!") // Run action
}
return [=10=]
}
}
.onDisappear {
if let monitor = monitor {
NSEvent.removeMonitor(monitor)
}
}
}
}
如果您只需要单击 select 和项目,则此方法有效,但仅当用户双击时才执行某些操作。如果你想处理单击和双击,当它是双击时,仍然存在单击 运行 的问题。一个潜在的解决方法是捕获单击动作并将其延迟几百毫秒,如果是双击动作则取消它
这是另一个似乎最适合我的解决方案。它是一个修饰符,添加了一个 NSView 来进行实际处理。即使有选择也适用于列表:
extension View {
/// Adds a double click handler this view (macOS only)
///
/// Example
/// ```
/// Text("Hello")
/// .onDoubleClick { print("Double click detected") }
/// ```
/// - Parameters:
/// - handler: Block invoked when a double click is detected
func onDoubleClick(handler: @escaping () -> Void) -> some View {
modifier(DoubleClickHandler(handler: handler))
}
}
struct DoubleClickHandler: ViewModifier {
let handler: () -> Void
func body(content: Content) -> some View {
content.overlay {
DoubleClickListeningViewRepresentable(handler: handler)
}
}
}
struct DoubleClickListeningViewRepresentable: NSViewRepresentable {
let handler: () -> Void
func makeNSView(context: Context) -> DoubleClickListeningView {
DoubleClickListeningView(handler: handler)
}
func updateNSView(_ nsView: DoubleClickListeningView, context: Context) {}
}
class DoubleClickListeningView: NSView {
let handler: () -> Void
init(handler: @escaping () -> Void) {
self.handler = handler
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func mouseDown(with event: NSEvent) {
super.mouseDown(with: event)
if event.clickCount == 2 {
handler()
}
}
}
https://gist.github.com/joelekstrom/91dad79ebdba409556dce663d28e8297
谁能想到在 MacOS 中双击列表中的 NavigationLink 时如何调用操作?我试过添加 onTapGesture(count:2) 但它没有达到预期的效果,并且覆盖了 link 的可靠选择能力。
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink(destination: Item(itemDetail: item)) {
ItemRow(itemRow: item) //<-my row view
}.buttonStyle(PlainButtonStyle())
.simultaneousGesture(TapGesture(count:2)
.onEnded {
print("double tap")
})
}
}
}
}
编辑:
我在 NavigationLink 中设置了 tag/selection,现在可以双击或单击该行的内容。唯一的麻烦是,虽然显示了 itemDetail 视图,但 link 上没有出现带有重音符号的“活动”状态。有没有办法设置活动状态(突出显示状态)或扩展 NavigationLink 功能以接受双击和单击?
@State var selection:String?
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink(destination: Item(itemDetail: item), tag: item.id, selection: self.$selection) {
ItemRow(itemRow: item) //<-my row view
}.onTapGesture(count:2) { //<- Needed to be first!
print("doubletap")
}.onTapGesture(count:1) {
self.selection = item.id
}
}
}
}
}
使用同步手势,如下所示(使用 Xcode 11.4 / macOS 10.15.5 测试)
NavigationLink(destination: Text("View One")) {
Text("ONE")
}
.buttonStyle(PlainButtonStyle()) // << required !!
.simultaneousGesture(TapGesture(count: 2)
.onEnded { print(">> double tap")})
或.highPriorityGesture(...
如果你需要双击有更高的优先级
寻找类似的解决方案,我尝试了@asperi 的回答,但在可点击区域方面遇到了与原始海报相同的问题。在尝试了许多变体之后,以下对我有用:
@State var selection: String?
...
NavigationLink(destination: HistoryListView(branch: string), tag: string, selection: self.$selection) {
Text(string)
.gesture(
TapGesture(count:1)
.onEnded({
print("Tap Single")
selection = string
})
)
.highPriorityGesture(
TapGesture(count:2)
.onEnded({
print("Tap Double")
})
)
}
我已经尝试了所有这些解决方案,但主要问题是使用 gesture
或 simultaneousGesture
覆盖列表视图上的默认单击手势,select列表。因此,我想到了一个简单的方法来保留默认的单击手势(select 行)并单独处理双击。
struct ContentView: View {
@State private var monitor: Any? = nil
@State private var hovering = false
@State private var selection = Set<String>()
let fruits = ["apple", "banana", "plum", "grape"]
var body: some View {
List(fruits, id: \.self, selection: $selection) { fruit in
VStack {
Text(fruit)
.frame(maxWidth: .infinity, alignment: .leading)
.clipShape(Rectangle()) // Allows the hitbox to be the entire word not the if you perfectly press the text
}
.onHover {
hovering = [=10=]
}
}
.onAppear {
monitor = NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown) {
if [=10=].clickCount == 2 && hovering { // Checks if mouse is actually hovering over the button or element
print("Double Tap!") // Run action
}
return [=10=]
}
}
.onDisappear {
if let monitor = monitor {
NSEvent.removeMonitor(monitor)
}
}
}
}
如果您只需要单击 select 和项目,则此方法有效,但仅当用户双击时才执行某些操作。如果你想处理单击和双击,当它是双击时,仍然存在单击 运行 的问题。一个潜在的解决方法是捕获单击动作并将其延迟几百毫秒,如果是双击动作则取消它
这是另一个似乎最适合我的解决方案。它是一个修饰符,添加了一个 NSView 来进行实际处理。即使有选择也适用于列表:
extension View {
/// Adds a double click handler this view (macOS only)
///
/// Example
/// ```
/// Text("Hello")
/// .onDoubleClick { print("Double click detected") }
/// ```
/// - Parameters:
/// - handler: Block invoked when a double click is detected
func onDoubleClick(handler: @escaping () -> Void) -> some View {
modifier(DoubleClickHandler(handler: handler))
}
}
struct DoubleClickHandler: ViewModifier {
let handler: () -> Void
func body(content: Content) -> some View {
content.overlay {
DoubleClickListeningViewRepresentable(handler: handler)
}
}
}
struct DoubleClickListeningViewRepresentable: NSViewRepresentable {
let handler: () -> Void
func makeNSView(context: Context) -> DoubleClickListeningView {
DoubleClickListeningView(handler: handler)
}
func updateNSView(_ nsView: DoubleClickListeningView, context: Context) {}
}
class DoubleClickListeningView: NSView {
let handler: () -> Void
init(handler: @escaping () -> Void) {
self.handler = handler
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func mouseDown(with event: NSEvent) {
super.mouseDown(with: event)
if event.clickCount == 2 {
handler()
}
}
}
https://gist.github.com/joelekstrom/91dad79ebdba409556dce663d28e8297