【问题标题】:How to add a tap gesture recognizer to a SecureField such that the hit area of the recognizer is the same as the field's default tap recognizer?如何将点击手势识别器添加到 SecureField 以使识别器的点击区域与字段的默认点击识别器相同?
【发布时间】:2020-12-03 17:31:59
【问题描述】:

在 Xcode 11.6 / iOS 13.6 中使用以下代码在 SwiftUI 中遇到一个奇怪的错误。

它将呈现两个带有白色边框的文本字段(如果您不使用深色模式,您可以更改为黑色边框)。 BUG是点击手势识别器的点击区域与SecureField的点击区域不一样。因此,您可以点击字段的边缘,虽然识别器会触发,但实际的文本字段不会成为第一响应者。

为了说明这一点,我将两个 UITextField(SwiftUI 在后台使用)的背景颜色设置为绿色。要使给定的 UITextField “成为第一响应者”,您必须在此绿色区域内点击,否则您无法在该字段中输入内容。

问题是因为我们的应用程序是跨平台的,即它将运行在 AppKit 或 UIKit 上,我们不想使用任何基于 UIKit 的方法来处理 UI 的工作方式。我们需要知道用户当前正在编辑哪个字段,以便我们可以将该字段的边框颜色设置为绿色(根据我们 UIX 团队的设计要求)。因此,由于我们无法设置委托来监听普通的 UITextField 委托方法,因此我们决定使用点击手势识别器来确定用户何时编辑给定字段。

但是现在我们遇到了这个问题,即填充区域包含在我们在 SwiftUI 中添加的 tapGestureRecognizer 中,但基础文本字段本身不会收到这样的手势,除非它位于填充内的较小区域中。

什么是可以纯粹在 SwiftUI 中完成的解决方案?

import PlaygroundSupport
import SwiftUI
import UIKit

public extension View {
    var any: AnyView { AnyView(self) }
}
struct Value: Identifiable {
    typealias ID = Int
    let id: ID
    var text: String = ""
}
struct MyView: View {
    @State var fields: [Value] = [Value(id: 0), Value(id: 1)]
    init() { 
        UITextField.appearance().backgroundColor = .green
    }
    
    var body: some View {
        ForEach(fields) { field  in
            SecureField(
                " ",
                text: self.$fields[field.id].text,
                onCommit: {
                    print("committed")
            })
                .onTapGesture {
                    print("tapped")
            }
            .padding(12)
            .overlay(
                RoundedRectangle(
                    cornerRadius: 5
                ).stroke(
                    Color.white,
                    lineWidth: 2
                )
            )
        }
    }
}

【问题讨论】:

  • 我不会说这是一个错误...点击手势不是鼠标指针,它是通过点检测到的,而不是通过一个热像素检测到的。而且它不是通过填充 - 如果您删除填充并在安全字段之外点击一点,行为将是相同的 - 手势会触发。
  • @Asperi 如果这不是一个错误,那么请告诉我我们应该如何只向 SecureField 添加一个轻击手势识别器(这在 UIKit 中很容易做到)——或者如果这不可能,那为什么不可能呢?如果这是不可能的,那么他们还指望我们做一些简单的事情,比如在它是第一响应者(输入文本的地方)的任何时候改变字段的边框颜色?我已经阅读了有关 Gesture 和 TextField 的所有文档和 SwiftUI 标头,并尝试了很多可能性,无法在此处列出,但尚未提出解决方案。

标签: swiftui ios13 xcode11 ios13.6


【解决方案1】:

如果你愿意,请大笑,但这该死的作品(将它放在我的环境对象中):

class MrEnvironment: ObservableObject {
       /// Set this to id in .onTapGesture of the View
       @Published public var tappedId: Int? = nil
        
       /// Call this later from wherever to resign first responder
       public var resignFirstResponder: (() -> Bool)?

       private static var textFieldDidStartEditingPublisher = 
           NotificationCenter.default.publisher(
               for: UITextField.textDidBeginEditingNotification) 

       /// Only publishes when user tapped on the field AND started editing it
       private var whichTextFieldIsEditingPublisher =
           Publishers.Zip(Self.textFieldDidStartEditingPublisher, $tappedId)
       
       let win: AnyCancellable = 
           whichTextFieldIsEditingPublisher
               .sink { [weak self] notification, _ in   
                   let view = notification.object as? UIView
                   self?.resignFirstResponder = view?.resignFirstResponder
                   // now since we know id of the field we can do anything to it
                   // like set the border color etc.
               }




    // etc. other stuff
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-27
    • 1970-01-01
    • 1970-01-01
    • 2016-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多