当我按下回车键或点击文本框外部时,如何取消文本框的焦点?(SwiftUI,MacOS)

21

当我按回车键或点击文本框外部时,如何取消文本框的焦点? 请注意,这是在MacOS上使用SwiftUI。

如果我执行以下操作:

import SwiftUI

struct ContentView: View {
  @State var field1: String = "This is the Text Field View"

  var body: some View {
    VStack{
      Button("Press") {
        print("Button Pressed")
      }

      TextField("Fill in Text", text: Binding(
        get: { print("get") ; return self.field1 },
        set: { print("set") ; self.field1 = $0 }
        )
      )
    }
  }
}

当您点击进入TextField并编辑它,然后点击按钮时,TextField不会失去焦点。如何使其退出编辑模式并失去焦点。

如果我按Return键,我也想从TextField失去焦点。 我使用了绑定初始化程序与get和set,因为我认为我可以拦截按键并检测'Return'字符,但这不起作用。

非常感谢您的帮助 :-)

4个回答

18

这里是可能的变体

import SwiftUI
import AppKit

struct ContentView: View {
  @State var field1: String = "This is the Text Field View"

  var body: some View {
    VStack{
      Button("Press") {
        print("Button Pressed")
          NSApp.keyWindow?.makeFirstResponder(nil)
      }

      TextField("Fill in Text", text: Binding(
        get: { print("get") ; return self.field1 },
        set: { print("set") ; self.field1 = $0 }
        ), onCommit: {
            DispatchQueue.main.async {
                NSApp.keyWindow?.makeFirstResponder(nil)
            }
      }
      )
    }
  }
}

感谢Asperi。这正是我所要求的。奇怪的是,它会调用两次onCommit - 不确定为什么。但这并不会给我带来问题。我现在意识到需要额外的东西来回答问题,但我会提出另一个问题。 - Cortado-J
我决定在这里提问,因为原始问题的主题行中有这个额外的部分:是否有一种方法可以在点击/轻敲TextField之外发生时失去焦点?否则,我将不得不将makeFirstResponder位添加到所有其他控件中,其中有许多! - Cortado-J
2
实际上,将TextField默认放在焦点位置是macOS的默认(也称为正常)行为。因此,如果UI中没有其他可聚焦元素,则它将始终默认处于焦点状态。(请注意,单击按钮不会使按钮可聚焦)。因此,将焦点移出文本字段是相当不像Mac的行为。 - Asperi
1
非常有用的评论 - 我刚刚查看了一些与我面临的情况类似的应用程序,它们的行为与您所描述的一样。 - Cortado-J
我觉得有趣的是,很容易只使用UI而不真正看到发生的逻辑。如果你问我点击一个按钮是否会使焦点离开编辑区域,我本以为肯定是的,但实际上我错了。 - Cortado-J

13

只需向您的 VStack 添加以下行的 onTapGesture:

UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil)

此代码将关闭键盘。

示例:

VStack {
    // ...
}.onTapGesture {
    UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil)
}

1
谢谢您的回答 - 不过我认为UIApplication只适用于iOS,而我使用的是macOS。 - Cortado-J
@Adahus 对不起,我刚刚做了一些研究,但是找不到有关在 macOS 上移除焦点的任何信息。如果我很快找到什么,我会告诉你的。 - Tobias Hesselink
4
对于那些在这里寻找iOS解决方案的人来说,这仍然是有用的。 :) - Nick

7

根据之前的答案,我创建了一个.removeFocusOnTap扩展,可以附加到任何应在点击时移除焦点的视图上(我将其用于背景视图)。它适用于iOS和macOS。

public struct RemoveFocusOnTapModifier: ViewModifier {
    public func body(content: Content) -> some View {
        content
#if os (iOS)
            .onTapGesture {
                UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil)
            }
#elseif os(macOS)
            .onTapGesture {
                DispatchQueue.main.async {
                    NSApp.keyWindow?.makeFirstResponder(nil)
                }
            }
#endif
    }
}

扩展:

extension View {
    public func removeFocusOnTap() -> some View {
        modifier(RemoveFocusOnTapModifier())
    }
}

1

对于 macOS,一个好的解决方案是使用另一个不可见的文本字段。这应该修复两个文本字段的问题:

  1. 出现时不要聚焦主文本字段。
  2. 编辑完成后取消聚焦。

示例代码:

struct YourView: View {
    @FocusState private var focus: Focus?

    var body: some View {
        TextField("", text: .constant(""))
            .focused($focus, equals: .some(.none))

        TextField("Some placeholder", text: .constant("Editable field"), onCommit: {
            focus = .some(.none)
        })
        .focused($focus, equals: .some(.main))
    }
}

private extension YourView {
    enum Focus: Hashable {
        case none
        case main
    }
}

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