使用SwiftUI时如何隐藏键盘?


88

在以下情况下如何隐藏keyboard使用SwiftUI

情况1

我有TextField,我需要keyboard在用户单击return按钮时隐藏。

情况二

我有TextFieldkeyboard当用户在外面轻按时,我需要隐藏它。

我该如何使用SwiftUI呢?

注意:

我还没有问有关的问题UITextField。我想用做SwifUI.TextField


29
@DannyBuonocore再次仔细阅读我的问题!
Hitesh Surani

9
@DannyBuonocore这不是所提到问题的重复项。这个问题是关于SwiftUI的,另外一个是普通的UIKit
Johnykutty19年

1
@DannyBuonocore,请查看developer.apple.com/documentation/swiftui,以了解UIKit和SwiftUI之间的区别。感谢
Hitesh Surani

在这里添加了解决方案希望对您有所帮助。
维克多·库什内罗夫

此处的大多数解决方案均无法按需工作,因为它们会禁用其他控制抽头上的所需反应。一个有效的解决方案可以在这里找到:forums.developer.apple.com/thread/127196
Hardy

Answers:


79

您可以通过向共享应用程序发送操作来强制第一响应者辞职:

extension UIApplication {
    func endEditing() {
        sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

现在,您可以根据需要使用此方法关闭键盘:

struct ContentView : View {
    @State private var name: String = ""

    var body: some View {
        VStack {
            Text("Hello \(name)")
            TextField("Name...", text: self.$name) {
                // Called when the user tap the return button
                // see `onCommit` on TextField initializer.
                UIApplication.shared.endEditing()
            }
        }
    }
}

如果您想通过点击来关闭键盘,则可以通过点击操作创建全屏白色视图,这将触发endEditing(_:)

struct Background<Content: View>: View {
    private var content: Content

    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content()
    }

    var body: some View {
        Color.white
        .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
        .overlay(content)
    }
}

struct ContentView : View {
    @State private var name: String = ""

    var body: some View {
        Background {
            VStack {
                Text("Hello \(self.name)")
                TextField("Name...", text: self.$name) {
                    self.endEditing()
                }
            }
        }.onTapGesture {
            self.endEditing()
        }
    }

    private func endEditing() {
        UIApplication.shared.endEditing()
    }
}

1
.keyWindow现在已弃用。请参阅Lorenzo Santini的答案
LinusGeffarth

3
此外,.tapAction已重命名为.onTapGesture
LinusGeffarth,

备用控件激活时,可以关闭键盘吗?stackoverflow.com/questions/58643512/…–
Yarm,

1
有没有一种方法可以在没有白色背景的情况下执行此操作,我正在使用垫片,并且我需要它来检测垫片上的轻击手势。同样,白色背景策略也会在较新的iPhone上造成问题,因为新iPhone上面有额外的屏幕空间。任何帮助表示赞赏!
约瑟夫·阿斯特拉罕

我发布了一个答案,可以改善您的设计。如果您希望我不在乎信用,请随时对您的答案进行修改。
约瑟夫·阿斯特拉罕

61

经过大量尝试,我发现了一种解决方案(当前)(不会)阻止任何控件-向中添加手势识别器UIWindow

  1. 如果您只想在“轻按外部”上关闭键盘(不处理拖拽),那么UITapGestureRecognizer仅需复制步骤3即可:
  2. 创建可与任何触摸配合使用的自定义手势识别器类:

    class AnyGestureRecognizer: UIGestureRecognizer {
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
            if let touchedView = touches.first?.view, touchedView is UIControl {
                state = .cancelled
    
            } else if let touchedView = touches.first?.view as? UITextView, touchedView.isEditable {
                state = .cancelled
    
            } else {
                state = .began
            }
        }
    
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
           state = .ended
        }
    
        override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
            state = .cancelled
        }
    }
    
  3. SceneDelegate.swiftfunc scene,添加下一个代码:

    let tapGesture = AnyGestureRecognizer(target: window, action:#selector(UIView.endEditing))
    tapGesture.requiresExclusiveTouchType = false
    tapGesture.cancelsTouchesInView = false
    tapGesture.delegate = self //I don't use window as delegate to minimize possible side effects
    window?.addGestureRecognizer(tapGesture)  
    
  4. 实施UIGestureRecognizerDelegate以允许同时触摸。

    extension SceneDelegate: UIGestureRecognizerDelegate {
        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            return true
        }
    }
    

现在,任何视图上的任何键盘都将在触摸或拖动到外部时关闭。

PS:如果您只想关闭特定的TextField,则每当调用TextField的回调时,在窗口中添加和删除手势识别器 onEditingChanged


3
这个答案应该在顶部。当视图中有其他控件时,其他答案将失败。
Imthath

1
@RolandLariotte更新了答案以解决此问题,请看AnyGestureRecognizer的新实现
Mikhail

1
很棒的答案。完美地工作。@Mikhail实际上很想知道如何删除特定于某些文本字段的手势识别器(我建立了带有标签的自动完成功能,因此每次点击列表中的元素时,我都不希望该特定文本字段失去焦点)
Pasta

1
这个解决方案实际上很棒,但是不幸的是,在使用了大约3个月之后,我发现了一个由此类黑客直接造成的错误。请注意发生在您
身上

1
很棒的答案!我不知道如何在没有SceneDelegate的情况下使用iOS 14来实现?

28

@RyanTCB的答案是好的;这里有一些改进,使其更易于使用并避免了潜在的崩溃:

struct DismissingKeyboard: ViewModifier {
    func body(content: Content) -> some View {
        content
            .onTapGesture {
                let keyWindow = UIApplication.shared.connectedScenes
                        .filter({$0.activationState == .foregroundActive})
                        .map({$0 as? UIWindowScene})
                        .compactMap({$0})
                        .first?.windows
                        .filter({$0.isKeyWindow}).first
                keyWindow?.endEditing(true)                    
        }
    }
}

“错误修复”只是应该keyWindow!.endEditing(true)正确地解决keyWindow?.endEditing(true)(是的,您可能会认为它不可能发生。)

更有趣的是如何使用它。例如,假设您有一个包含多个可编辑字段的表单。像这样包装它:

Form {
    .
    .
    .
}
.modifier(DismissingKeyboard())

现在,点击任何本身没有键盘的控件将执行适当的关闭操作。

(经过Beta 7测试)


6
嗯-点击其他控件不再注册。该事件被吞下。
Yarm

我无法复制它-使用11/1上的Apple最新发行版,它仍然对我有用。它有用吗,然后停止为您工作,或者?
费尔德

如果您在表单中有一个DatePicker,则将不再显示DatePicker
Albert

@Albert-是的;要使用此方法,您必须分解使用DismissingKeyboard()装饰项目的位置,使其达到适用于应关闭并避免使用DatePicker的元素的更细粒度级别。
费尔德

使用此代码将重现警告Can't find keyplane that supports type 4 for keyboard iPhone-PortraitChoco-NumberPad; using 25686_PortraitChoco_iPhone-Simple-Pad_Default
np2314

23

我在NavigationView中使用TextField时遇到了这种情况。这是我的解决方案。当您开始滚动时,它将关闭键盘。

NavigationView {
    Form {
        Section {
            TextField("Receipt amount", text: $receiptAmount)
            .keyboardType(.decimalPad)
           }
        }
     }
     .gesture(DragGesture().onChanged{_ in UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)})

这将导致onDelete(刷卡删除)行为异常。
塔雷克·哈拉克

很好,但是水龙头呢?
Danny182

20

我找到了另一种不需要访问该keyWindow属性的方法来关闭键盘。事实上,编译器会使用

UIApplication.shared.keyWindow?.endEditing(true)

iOS 13.0中已弃用“ keyWindow”:不应将其用于支持多个场景的应用程序,因为它会返回所有已连接场景的关键窗口

相反,我使用了以下代码:

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

15

只需在“ SceneDelegate.swift”文件中添加SwiftUI.onTapGesture {window.endEditing(true)}

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

        // Create the SwiftUI view that provides the window contents.
        let contentView = ContentView()

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(
                rootView: contentView.onTapGesture { window.endEditing(true)}
            )
            self.window = window
            window.makeKeyAndVisible()
        }
    }

这对于使用应用程序中的键盘的每个视图就足够了...


4
这带来了另一个问题-我在Form {}中有一个选择器,与文本字段一起,它没有响应。使用本主题中的所有答案,我都没有找到解决方案。但是,如果您不使用选择器,您的答案很适合解雇其他地方的敲击键盘。
Nalov

你好。我的代码```var body:some View {NavigationView {Form {Section {TextField(“ typesomething”,text:$ c)} Section {Picker(“ name”,selection:$ sel){ForEach(0 .. <200 ){Text(“(self.array [$ 0])%”)}}}`````轻按其他位置时键盘被关闭,但选择器无响应。我没有找到使它起作用的方法。
Nalov

2
再次嗨,目前我有两种解决方案:第一种-使用返回按钮上未显示的本机键盘,第二种-稍微更改敲击处理方式(aka'костыль')-window.rootViewController = UIHostingController(rootView :contentView.onTapGesture(count:2,perform:{window.endEditing(true)}))希望这对您有帮助...
Dim Novo

你好。谢谢。第二种方法解决了它。我使用的是数字键盘,因此用户只能输入数字,没有回车键。用攻丝解雇是我一直在寻找的东西。
Nalov

这将导致列表无法导航。
崔明达

13

SwiftUI 2

这里是更新的溶液SwiftUI 2/14的iOS(最初提出这里米哈伊尔)。

如果您使用SwiftUI生命周期,它不会使用AppDelegateSceneDelegate缺少的内容:

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onAppear(perform: UIApplication.shared.addTapGestureRecognizer)
        }
    }
}

extension UIApplication {
    func addTapGestureRecognizer() {
        guard let window = windows.first else { return }
        let tapGesture = UITapGestureRecognizer(target: window, action: #selector(UIView.endEditing))
        tapGesture.requiresExclusiveTouchType = false
        tapGesture.cancelsTouchesInView = false
        tapGesture.delegate = self
        window.addGestureRecognizer(tapGesture)
    }
}

extension UIApplication: UIGestureRecognizerDelegate {
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true // set to `false` if you don't want to detect tap during other gestures
    }
}

这是一个示例,如何检测除长按手势以外的同时手势:

extension UIApplication: UIGestureRecognizerDelegate {
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return !otherGestureRecognizer.isKind(of: UILongPressGestureRecognizer.self)
    }
}

2
这样完美!感谢您的解决方案
NotAPhoenix

2
这应该放在首位,因为牢记新的SwiftUI生命周期。
carlosobedgomez

这很好。但是,如果我在文本字段中双击,而不是选择文本,键盘现在会消失。知道如何允许双击选择吗?
加里

@Gary在底部扩展中,如果您不想在其他手势期间检测到轻敲,则可以看到注释设置为false的行。只需将其设置为即可return false
pawello2222

将其设置为false是可以的,但是如果有人在文本区域之外长按,拖动或滚动,键盘也不会消失。有什么方法可以将其设置为仅针对双击(最好是在文本字段内双击,但即使所有双击都可以)为false。
加里

11

我的解决方案是如何在用户点击外部时隐藏软件键盘。您需要使用contentShapewithonLongPressGesture来检测整个View容器。onTapGesture要求避免将注意力集中在TextField。您可以使用onTapGesture代替,onLongPressGesture但NavigationBar项将不起作用。

extension View {
    func endEditing() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

struct KeyboardAvoiderDemo: View {
    @State var text = ""
    var body: some View {
        VStack {
            TextField("Demo", text: self.$text)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .contentShape(Rectangle())
        .onTapGesture {}
        .onLongPressGesture(
            pressing: { isPressed in if isPressed { self.endEditing() } },
            perform: {})
    }
}

这很好用,我使用它的方式略有不同,并且必须确保在主线程上调用了它。
keegan3d

7

将此修饰符添加到要检测用户点击的视图中

.onTapGesture {
            let keyWindow = UIApplication.shared.connectedScenes
                               .filter({$0.activationState == .foregroundActive})
                               .map({$0 as? UIWindowScene})
                               .compactMap({$0})
                               .first?.windows
                               .filter({$0.isKeyWindow}).first
            keyWindow!.endEditing(true)

        }

7

我更喜欢使用.onLongPressGesture(minimumDuration: 0),它不会在另一个键盘TextView被激活时引起键盘闪烁(的副作用.onTapGesture)。隐藏键盘代码可以是可重用的功能。

.onTapGesture(count: 2){} // UI is unresponsive without this line. Why?
.onLongPressGesture(minimumDuration: 0, maximumDistance: 0, pressing: nil, perform: hide_keyboard)

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

使用此方法仍然闪烁。
丹尼尔·瑞安

这很好用,我使用它的方式略有不同,并且必须确保在主线程上调用了它。
keegan3d

6

因为keyWindow已弃用。

extension View {
    func endEditing(_ force: Bool) {
        UIApplication.shared.windows.forEach { $0.endEditing(force)}
    }
}

1
force不使用该参数。应该是{ $0.endEditing(force)}
Davide

5

好像endEditing解决方案是@rraphael指出的唯一解决方案。
到目前为止,我所看到的最干净的示例是:

extension View {
    func endEditing(_ force: Bool) {
        UIApplication.shared.keyWindow?.endEditing(force)
    }
}

然后在 onCommit:


2
.keyWindow现在已弃用。请参阅Lorenzo Santini的答案
LinusGeffarth

在iOS 13+上已贬值
Ahmadreza

4

通过@Feldur(基于@RyanTCB的答案)扩展答案,这是一个更具表达力和功能的解决方案,允许您关闭键盘上的其他手势onTapGesture,而无需在功能调用中指定键盘。

用法

// MARK: - View
extension RestoreAccountInputMnemonicScreen: View {
    var body: some View {
        List(viewModel.inputWords) { inputMnemonicWord in
            InputMnemonicCell(mnemonicInput: inputMnemonicWord)
        }
        .dismissKeyboard(on: [.tap, .drag])
    }
}

或使用All.gestures(只用糖作Gestures.allCases🍬)

.dismissKeyboard(on: All.gestures)

enum All {
    static let gestures = all(of: Gestures.self)

    private static func all<CI>(of _: CI.Type) -> CI.AllCases where CI: CaseIterable {
        return CI.allCases
    }
}

enum Gestures: Hashable, CaseIterable {
    case tap, longPress, drag, magnification, rotation
}

protocol ValueGesture: Gesture where Value: Equatable {
    func onChanged(_ action: @escaping (Value) -> Void) -> _ChangedGesture<Self>
}
extension LongPressGesture: ValueGesture {}
extension DragGesture: ValueGesture {}
extension MagnificationGesture: ValueGesture {}
extension RotationGesture: ValueGesture {}

extension Gestures {
    @discardableResult
    func apply<V>(to view: V, perform voidAction: @escaping () -> Void) -> AnyView where V: View {

        func highPrio<G>(
             gesture: G
        ) -> AnyView where G: ValueGesture {
            view.highPriorityGesture(
                gesture.onChanged { value in
                    _ = value
                    voidAction()
                }
            ).eraseToAny()
        }

        switch self {
        case .tap:
            // not `highPriorityGesture` since tapping is a common gesture, e.g. wanna allow users
            // to easily tap on a TextField in another cell in the case of a list of TextFields / Form
            return view.gesture(TapGesture().onEnded(voidAction)).eraseToAny()
        case .longPress: return highPrio(gesture: LongPressGesture())
        case .drag: return highPrio(gesture: DragGesture())
        case .magnification: return highPrio(gesture: MagnificationGesture())
        case .rotation: return highPrio(gesture: RotationGesture())
        }

    }
}

struct DismissingKeyboard: ViewModifier {

    var gestures: [Gestures] = Gestures.allCases

    dynamic func body(content: Content) -> some View {
        let action = {
            let forcing = true
            let keyWindow = UIApplication.shared.connectedScenes
                .filter({$0.activationState == .foregroundActive})
                .map({$0 as? UIWindowScene})
                .compactMap({$0})
                .first?.windows
                .filter({$0.isKeyWindow}).first
            keyWindow?.endEditing(forcing)
        }

        return gestures.reduce(content.eraseToAny()) { $1.apply(to: $0, perform: action) }
    }
}

extension View {
    dynamic func dismissKeyboard(on gestures: [Gestures] = Gestures.allCases) -> some View {
        return ModifiedContent(content: self, modifier: DismissingKeyboard(gestures: gestures))
    }
}

注意事项

请注意,如果您使用所有手势,它们可能会发生冲突,因此我没有提出任何巧妙的解决方案来解决该问题。


什么意思 eraseToAny()
Ravindra_Bhati

2

此方法使您可以将键盘隐藏垫片上!

首先添加此功能(来自SwiftUI的信用:Casper Zandbergen无法使用HStack的Spacer

extension Spacer {
    public func onTapGesture(count: Int = 1, perform action: @escaping () -> Void) -> some View {
        ZStack {
            Color.black.opacity(0.001).onTapGesture(count: count, perform: action)
            self
        }
    }
}

接下来添加以下2个功能(信贷给:rraphael,来自该问题)

extension UIApplication {
    func endEditing() {
        sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

下面的函数将添加到您的View类中,有关更多详细信息,请仅参考rraphael的此处最佳答案。

private func endEditing() {
   UIApplication.shared.endEditing()
}

最后,您现在可以简单地致电...

Spacer().onTapGesture {
    self.endEditing()
}

这将使任何间隔物区域现在关闭键盘。不再需要大的白色背景视图!

假设您可以将此技术extension应用于支持TapGestures所需的任何控件,而这些控件目前尚不支持,并onTapGesture结合使用该函数以在您需要的self.endEditing()任何情况下关闭键盘。


现在我的问题是,当键盘离开后,如何触发文本字段上的提交?当前,“ commit”仅在您按下iOS键盘上的回车键时触发。
约瑟夫·阿斯特拉罕


2

根据@Sajjon的答案,这是一个解决方案,可让您根据自己的选择在敲击,长按,拖动,放大和旋转手势时关闭键盘。

此解决方案在XCode 11.4中有效

用法获取@IMHiteshSurani要求的行为

struct MyView: View {
    @State var myText = ""

    var body: some View {
        VStack {
            DismissingKeyboardSpacer()

            HStack {
                TextField("My Text", text: $myText)

                Button("Return", action: {})
                    .dismissKeyboard(on: [.longPress])
            }

            DismissingKeyboardSpacer()
        }
    }
}

struct DismissingKeyboardSpacer: View {
    var body: some View {
        ZStack {
            Color.black.opacity(0.0001)

            Spacer()
        }
        .dismissKeyboard(on: Gestures.allCases)
    }
}

enum All {
    static let gestures = all(of: Gestures.self)

    private static func all<CI>(of _: CI.Type) -> CI.AllCases where CI: CaseIterable {
        return CI.allCases
    }
}

enum Gestures: Hashable, CaseIterable {
    case tap, longPress, drag, magnification, rotation
}

protocol ValueGesture: Gesture where Value: Equatable {
    func onChanged(_ action: @escaping (Value) -> Void) -> _ChangedGesture<Self>
}

extension LongPressGesture: ValueGesture {}
extension DragGesture: ValueGesture {}
extension MagnificationGesture: ValueGesture {}
extension RotationGesture: ValueGesture {}

extension Gestures {
    @discardableResult
    func apply<V>(to view: V, perform voidAction: @escaping () -> Void) -> AnyView where V: View {

        func highPrio<G>(gesture: G) -> AnyView where G: ValueGesture {
            AnyView(view.highPriorityGesture(
                gesture.onChanged { _ in
                    voidAction()
                }
            ))
        }

        switch self {
        case .tap:
            return AnyView(view.gesture(TapGesture().onEnded(voidAction)))
        case .longPress:
            return highPrio(gesture: LongPressGesture())
        case .drag:
            return highPrio(gesture: DragGesture())
        case .magnification:
            return highPrio(gesture: MagnificationGesture())
        case .rotation:
            return highPrio(gesture: RotationGesture())
        }
    }
}

struct DismissingKeyboard: ViewModifier {
    var gestures: [Gestures] = Gestures.allCases

    dynamic func body(content: Content) -> some View {
        let action = {
            let forcing = true
            let keyWindow = UIApplication.shared.connectedScenes
                .filter({$0.activationState == .foregroundActive})
                .map({$0 as? UIWindowScene})
                .compactMap({$0})
                .first?.windows
                .filter({$0.isKeyWindow}).first
            keyWindow?.endEditing(forcing)
        }

        return gestures.reduce(AnyView(content)) { $1.apply(to: $0, perform: action) }
    }
}

extension View {
    dynamic func dismissKeyboard(on gestures: [Gestures] = Gestures.allCases) -> some View {
        return ModifiedContent(content: self, modifier: DismissingKeyboard(gestures: gestures))
    }
}

2

您可以完全避免与UIKit交互,而可以在纯SwiftUI中实现它。只需在您要修改的键盘上添加.id(<your id>)修饰符TextField并更改其值即可(在键盘上滑动,查看点击,按钮动作等。)。

实施示例:

struct MyView: View {
    @State private var text: String = ""
    @State private var textFieldId: String = UUID().uuidString

    var body: some View {
        VStack {
            TextField("Type here", text: $text)
                .id(textFieldId)

            Spacer()

            Button("Dismiss", action: { textFieldId = UUID().uuidString })
        }
    }
}

请注意,我仅在最新的Xcode 12 beta中对其进行了测试,但是它应该可以与旧版本(甚至Xcode 11)一起使用,而不会出现任何问题。


0

键盘Return按键

除了有关在textField之外点击的所有答案之外,当用户点击键盘上的回车键时,您可能还希望关闭键盘:

定义此全局函数:

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

并在onCommit参数中添加use :

TextField("title", text: $text, onCommit:  {
    resignFirstResponder()
})

好处

  • 您可以从任何地方打电话
  • 它不依赖于UIKit或SwiftUI(可以在Mac应用程序中使用)
  • 即使在iOS 13中也可以使用

演示版

演示


0

到目前为止,以上选项对我而言不起作用,因为我具有窗体以及内部按钮,链接,选择器...

在上面的示例的帮助下,我创建了下面的有效代码。

import Combine
import SwiftUI

private class KeyboardListener: ObservableObject {
    @Published var keyabordIsShowing: Bool = false
    var cancellable = Set<AnyCancellable>()

    init() {
        NotificationCenter.default
            .publisher(for: UIResponder.keyboardWillShowNotification)
            .sink { [weak self ] _ in
                self?.keyabordIsShowing = true
            }
            .store(in: &cancellable)

       NotificationCenter.default
            .publisher(for: UIResponder.keyboardWillHideNotification)
            .sink { [weak self ] _ in
                self?.keyabordIsShowing = false
            }
            .store(in: &cancellable)
    }
}

private struct DismissingKeyboard: ViewModifier {
    @ObservedObject var keyboardListener = KeyboardListener()

    fileprivate func body(content: Content) -> some View {
        ZStack {
            content
            Rectangle()
                .background(Color.clear)
                .opacity(keyboardListener.keyabordIsShowing ? 0.01 : 0)
                .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
                .onTapGesture {
                    let keyWindow = UIApplication.shared.connectedScenes
                        .filter({ $0.activationState == .foregroundActive })
                        .map({ $0 as? UIWindowScene })
                        .compactMap({ $0 })
                        .first?.windows
                        .filter({ $0.isKeyWindow }).first
                    keyWindow?.endEditing(true)
                }
        }
    }
}

extension View {
    func dismissingKeyboard() -> some View {
        ModifiedContent(content: self, modifier: DismissingKeyboard())
    }
}

用法:

 var body: some View {
        NavigationView {
            Form {
                picker
                button
                textfield
                text
            }
            .dismissingKeyboard()

-2

SwiftUI已于2020年6月/ Xcode 12和iOS 14发布,其中添加了hideKeyboardOnTap()修饰符。这样可以解决您的案例编号2。针对您的案例编号1的解决方案随Xcode 12和iOS 14免费提供:当按下Return键时,TextField的默认键盘会自动隐藏。


1
iOS14中没有hideKeyboardOnTap修饰符
Teo Sartori

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.