[英]SwiftUI - unexpected behaviour using onTapGesture with mouse/trackpad on iPadOS and Catalyst
I have in my app a layout showing a list of rectangular cards - each one should be tappable (once) to reveal a set of action buttons and more information, etc.我在我的应用程序中有一个显示矩形卡片列表的布局 - 每个卡片都应该是可点击的(一次)以显示一组操作按钮和更多信息等。
I have implemented this using .onTapGesture()
and I have also put .contentShape(Rectangle()
to enforce the tappable area. However, while my implementation works fine for touchscreen interface, when I'm using it with the iPadOS mouse support, and on Catalyst for that matter, I see some very unexpected behaviour.我已经使用
.onTapGesture()
实现了这一点,并且我还使用了.contentShape(Rectangle()
来强制执行可点击区域。但是,虽然我的实现适用于触摸屏界面,但当我将它与 iPadOS 鼠标支持一起使用时,并且在 Catalyst 上,我看到了一些非常出乎意料的行为。
I've made a minimal reproducible example below that you can copy to recreate the problem.我在下面做了一个最小的可重现示例,您可以复制它来重新创建问题。
Where the problems are when using mouse/trackpad input:使用鼠标/触控板输入时问题出在哪里:
If you are running the example code you should be able to see the problem by repeatedly moving the mouse and attempting one click.如果您正在运行示例代码,您应该能够通过反复移动鼠标并尝试单击来查看问题。 It doesn't work unless you click multiple times in the same spot.
除非您在同一位置多次单击,否则它不起作用。
What does work as expected :什么按预期工作:
Code I used to recreate this problem (has a counter to count every time a tap gesture is recorded):我用来重新创建此问题的代码(每次记录点击手势时都有一个计数器来计数):
struct WidgetCompactTaskItemView: View {
let title: String
let description: String
var body: some View {
HStack {
Rectangle()
.fill(Color.purple)
.frame(maxWidth: 14, maxHeight: .infinity)
VStack(alignment: .leading) {
Text(title).font(.system(size: 14, weight: .bold, design: .rounded))
Text(description).font(.system(.footnote, design: .rounded))
.frame(maxHeight: .infinity)
.fixedSize(horizontal: false, vertical: true)
.lineLimit(1)
.padding(.vertical, 0.1)
Spacer()
}
.padding(.horizontal, 6)
.padding(.top, 12)
}
.frame(maxWidth: .infinity, maxHeight: 100, alignment: .leading)
.background(Color.black)
.cornerRadius(16)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(Color.green, lineWidth: 0.5)
)
}
}
struct ContentView: View {
@State var tapCounter = 0
var body: some View {
VStack {
Text("Button tapped \(tapCounter) times.")
WidgetCompactTaskItemView(title: "Example", description: "Description")
.contentShape(Rectangle())
.onTapGesture(count: 1) {
tapCounter += 1
}
Spacer()
}
}
}
I have tried several things including moving modifiers around, setting eoFill
to true on the contentShape
modifier (which didn't fix the problem but simply made different unexpected behaviour).我尝试了几件事,包括移动修饰符,在
contentShape
修饰符eoFill
设置为 true(这并没有解决问题,只是产生了不同的意外行为)。
Any help to find a solution that works as expected and works consistently whether mouse or touch would be much appreciated.任何帮助找到一个按预期工作并且无论鼠标还是触摸都能始终如一地工作的解决方案将不胜感激。 I am not sure if I am doing something wrong or if there is a bug here, so please try and recreate this example yourself using the code to see if you can reproduce the problem.
我不确定我是否做错了什么或者这里是否有错误,所以请尝试使用代码自己重新创建此示例,看看是否可以重现问题。
So I realised that there was a much better solution that could bypass all the oddities that .onTapGesture
had for me with mouse input.所以我意识到有一个更好的解决方案可以通过鼠标输入绕过
.onTapGesture
对我的所有奇怪之处。 It was to encapsulate the whole view in a Button
instead.而是将整个视图封装在一个
Button
中。
I made this into a modifier similar to onTapGesture so that it's much more practical.我把它做成了一个类似于 onTapGesture 的修饰符,这样它就更实用了。
import Foundation
import SwiftUI
public struct UltraPlainButtonStyle: ButtonStyle {
public func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
}
}
struct Tappable: ViewModifier {
let action: () -> ()
func body(content: Content) -> some View {
Button(action: self.action) {
content
}
.buttonStyle(UltraPlainButtonStyle())
}
}
extension View {
func tappable(do action: @escaping () -> ()) -> some View {
self.modifier(Tappable(action: action))
}
}
Going through this: I first have a button style which simply returns the label as is.经历这个:我首先有一个按钮样式,它只是按原样返回 label。 This is necessary because the default
PlainButtonStyle()
still has a visible effect when clicked.这是必要的,因为默认的
PlainButtonStyle()
在单击时仍然具有可见效果。 I then create a modifier that encapsulates the content given in a Button
with this button style, then add that as an extension to View
.然后,我创建一个修饰符,用这种按钮样式封装
Button
中给出的内容,然后将其作为扩展添加到View
。
Usage example使用示例
WidgetCompactTaskItemView(title: "Example", description: "Description")
.tappable {
tapCounter += 1
}
This has solved all problems I've been having with clickable area using a mouse.这解决了我在使用鼠标可点击区域时遇到的所有问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.