SwiftUI - 在iPadOS和Catalyst上使用onTapGesture与鼠标/触摸板时出现意外行为

7
我在应用程序中有一个布局,显示一系列矩形卡片 – 每个卡片都可以点击(一次)以显示一组操作按钮和更多信息等。我使用了`.onTapGesture()`实现了这个功能,并使用了`.contentShape(Rectangle())`来强制可点击区域。然而,虽然我的实现在触摸界面上运行良好,但当我在iPadOS鼠标支持下使用它时,以及在Catalyst上使用它时,我看到了一些非常意外的行为。
我在下面提供了一个最小化的可重现示例,您可以复制该示例以重新创建此问题。
使用鼠标/轨迹板输入时存在的问题:
- 并不是每次鼠标点击都被记录为点按手势。这通常是任意发生的,除了在一些情况下: - 它似乎只在非常特定的区域单击,或者在同一位置多次单击时才会单击。 - 在很多情况下,似乎只有每两次点击才会被记录。所以我双击只得到一个点击手势。 - 尽管在此示例代码中并不明显,但在我的主要应用程序中,可点击区域似乎是任意的 - 通常可以在文本附近或与其对齐的位置单击以记录点按手势,但并非总是如此。
如果您运行示例代码,则可以通过多次移动鼠标并尝试单击来查看问题。除非您多次在同一位置单击,否则无法正常工作。
按预期工作的内容:
- 所有使用触摸而不是鼠标的输入;无论您点击何处,它都会记录一个点按手势。 - 在本机Mac目标下运行时的鼠标输入。上述问题仅适用于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()
        }
     }
}

我已经尝试了几种方法,包括移动修饰符,将eoFill设置为true在contentShape修饰符上(这没有解决问题,而是只产生了其他意外的行为)。

帮助找到一个预期和一致工作的解决方案,无论是鼠标还是触摸,将不胜感激。我不确定我是否做错了什么或者是否存在错误,因此请尝试使用提供的代码重新创建此示例,以查看是否能够重新现问题。

1个回答

13

所以我意识到有个更好的解决方案可以绕过所有关于鼠标输入的奇怪问题,那就是将整个视图封装在一个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))
    }
}

经过以下步骤: 首先,我有一个按钮样式,它只是简单地返回标签本身。这是必要的,因为默认的PlainButtonStyle()在点击时仍然具有可见效果。 然后,我创建了一个修饰符,将给定内容封装在一个使用此按钮样式的Button中,并将其作为扩展添加到View中。

用法示例

WidgetCompactTaskItemView(title: "Example", description: "Description")
                .tappable {
                    tapCounter += 1
                }

这解决了我使用鼠标时遇到的所有可点击区域问题。


非常有用,谢谢。我将tappable函数的第一个参数更改为“perform”,以使其与我的代码兼容,该代码有时使用带有显式“perform”参数名称的onTapGesture。 - Alex

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