SwiftUI - 在 iPadOS 和 Catalyst 上使用带有 mouse/trackpad 的 onTapGesture 的意外行为
SwiftUI - unexpected behaviour using onTapGesture with mouse/trackpad on iPadOS and Catalyst
我的应用程序中有一个显示矩形卡片列表的布局 - 每张卡片都应该可点击(一次)以显示一组操作按钮和更多信息等。
我已经使用 .onTapGesture()
实现了这一点,并且我还设置了 .contentShape(Rectangle()
来加强可点击区域。然而,虽然我的实现适用于触摸屏界面,但当我将它与 iPadOS 鼠标支持一起使用时,以及在 Catalyst 上,我看到了一些非常意外的行为。
我在下面做了一个最小的可重现示例,您可以复制它来重现问题。
使用mouse/trackpad输入时出现的问题:
- 并非每次点击鼠标都记录为点击手势。除了少数情况外,这主要是任意发生的:
- 它似乎要么只在非常特定的区域点击,要么在同一个地方点击多次。
- 似乎在很多情况下只记录每个 other 点击。所以我双击只得到一个点击手势。
- 在此示例代码中并不明显,但在我的主应用程序中,可点击区域似乎是任意的 - 您通常可以点击文本附近或与其对齐以记录点击手势,但 不总是.
如果您是 运行 示例代码,您应该能够通过反复移动鼠标并尝试单击来发现问题。除非你在同一个地方点击多次,否则它不起作用。
什么按预期工作:
- 所有输入都使用触摸而不是鼠标;无论您点击哪里,它都会记录一个点击手势。
- 当 运行 作为本机 Mac 目标时鼠标输入。以上问题仅针对mouse/trackpad when 运行 iPadOS和Mac Catalyst下的例子
我用来重现这个问题的代码(每次记录点击手势时都有一个计数器来计数):
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()
}
}
}
我尝试了几种方法,包括四处移动修饰符,将 contentShape
修饰符的 eoFill
设置为 true(这没有解决问题,只是造成了不同的意外行为)。
如能帮助找到 按预期工作并始终如一地工作的解决方案,无论是鼠标还是触摸,我们都将不胜感激。我不确定是我做错了什么还是这里有错误,所以请尝试使用代码自己重新创建此示例,看看是否可以重现问题。
所以我意识到有一个更好的解决方案可以绕过 .onTapGesture
对我来说鼠标输入的所有奇怪之处。而是将整个视图封装在 Button
中。
我把它做成类似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))
}
}
经历这个:
我首先有一个按钮样式,它只是按原样 returns 标签。这是必要的,因为默认的 PlainButtonStyle()
在点击时仍然有可见的效果。
然后我创建一个修饰符,用这个按钮样式封装 Button
中给定的内容,然后将其作为扩展添加到 View
.
用法示例
WidgetCompactTaskItemView(title: "Example", description: "Description")
.tappable {
tapCounter += 1
}
这解决了我在使用鼠标点击区域时遇到的所有问题。
我的应用程序中有一个显示矩形卡片列表的布局 - 每张卡片都应该可点击(一次)以显示一组操作按钮和更多信息等。
我已经使用 .onTapGesture()
实现了这一点,并且我还设置了 .contentShape(Rectangle()
来加强可点击区域。然而,虽然我的实现适用于触摸屏界面,但当我将它与 iPadOS 鼠标支持一起使用时,以及在 Catalyst 上,我看到了一些非常意外的行为。
我在下面做了一个最小的可重现示例,您可以复制它来重现问题。
使用mouse/trackpad输入时出现的问题:
- 并非每次点击鼠标都记录为点击手势。除了少数情况外,这主要是任意发生的:
- 它似乎要么只在非常特定的区域点击,要么在同一个地方点击多次。
- 似乎在很多情况下只记录每个 other 点击。所以我双击只得到一个点击手势。
- 在此示例代码中并不明显,但在我的主应用程序中,可点击区域似乎是任意的 - 您通常可以点击文本附近或与其对齐以记录点击手势,但 不总是.
如果您是 运行 示例代码,您应该能够通过反复移动鼠标并尝试单击来发现问题。除非你在同一个地方点击多次,否则它不起作用。
什么按预期工作:
- 所有输入都使用触摸而不是鼠标;无论您点击哪里,它都会记录一个点击手势。
- 当 运行 作为本机 Mac 目标时鼠标输入。以上问题仅针对mouse/trackpad when 运行 iPadOS和Mac Catalyst下的例子
我用来重现这个问题的代码(每次记录点击手势时都有一个计数器来计数):
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()
}
}
}
我尝试了几种方法,包括四处移动修饰符,将 contentShape
修饰符的 eoFill
设置为 true(这没有解决问题,只是造成了不同的意外行为)。
如能帮助找到 按预期工作并始终如一地工作的解决方案,无论是鼠标还是触摸,我们都将不胜感激。我不确定是我做错了什么还是这里有错误,所以请尝试使用代码自己重新创建此示例,看看是否可以重现问题。
所以我意识到有一个更好的解决方案可以绕过 .onTapGesture
对我来说鼠标输入的所有奇怪之处。而是将整个视图封装在 Button
中。
我把它做成类似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))
}
}
经历这个:
我首先有一个按钮样式,它只是按原样 returns 标签。这是必要的,因为默认的 PlainButtonStyle()
在点击时仍然有可见的效果。
然后我创建一个修饰符,用这个按钮样式封装 Button
中给定的内容,然后将其作为扩展添加到 View
.
用法示例
WidgetCompactTaskItemView(title: "Example", description: "Description")
.tappable {
tapCounter += 1
}
这解决了我在使用鼠标点击区域时遇到的所有问题。