首页 > 解决方案 > SwiftUI - 在 iPadOS 和 Catalyst 上使用带有鼠标/触控板的 onTapGesture 出现意外行为

问题描述

我在我的应用程序中有一个显示矩形卡片列表的布局 - 每个卡片都应该是可点击的(一次)以显示一组操作按钮和更多信息等。

我已经实现了这个使用.onTapGesture(),并且我还.contentShape(Rectangle()强制执行了可点击区域。然而,虽然我的实现适用于触摸屏界面,但当我将它与 iPadOS 鼠标支持一起使用时,以及在 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(这并没有解决问题,只是产生了不同的意外行为)。

任何帮助找到一个按预期工作并且无论鼠标还是触摸都能始终如一地工作的解决方案将不胜感激。我不确定我是否做错了什么或者这里是否有错误,所以请尝试使用代码自己重新创建此示例,看看是否可以重现问题。

标签: iosswiftswiftuimac-catalystipados

解决方案


所以我意识到有一个更好的解决方案可以绕过.onTapGesture鼠标输入给我带来的所有奇怪问题。而是将整个视图封装在 a 中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使用此按钮样式封装 a 中给出的内容,然后将其作为扩展添加到View.

使用示例

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

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


推荐阅读