使用SwiftUI时如何隐藏键盘?

时间:2019-06-07 09:13:16

标签: ios swift swiftui

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

案例1

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

案例2

我有TextField,当用户在外面轻按时,我需要隐藏keyboard

如何使用SwiftUI来做到这一点?

注意:

我没有问关于UITextField的问题。我想通过使用SwifUITextField)来做到这一点。

26 个答案:

答案 0 :(得分:62)

经过大量尝试,我发现了一种解决方案(当前)不阻止任何控件-向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.swift的{​​{1}}中,添加下一个代码:

    func scene
  4. 实施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) 以允许同时触摸。

    UIGestureRecognizerDelegate

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

P.S。如果您只想关闭特定的TextField,则每当调用TextField extension SceneDelegate: UIGestureRecognizerDelegate { func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } }

的回调时,在窗口中添加和删除手势识别器

答案 1 :(得分:24)

我在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)})

答案 2 :(得分:18)

SwiftUI 2

这是 SwiftUI 2 / iOS 14 (最初由Mikhail提出的here)的更新解决方案。

如果您使用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)
    }
}

答案 3 :(得分:11)

我发现了另一种不需要访问keyWindow属性的键盘解雇方法。实际上,编译器会使用

发出警告
UIApplication.shared.keyWindow?.endEditing(true)
  

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

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

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

答案 4 :(得分:11)

@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测试)

答案 5 :(得分:11)

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

onLongPressGesture

答案 6 :(得分:10)

SwiftUI 在“ SceneDelegate.swift”文件中,只需添加: .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()
        }
    }

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

答案 7 :(得分:8)

到目前为止,我发现唯一关闭键盘的方法是在endEditing(_:)上调用keyWindow

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

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

如果您想通过点击来关闭键盘,则可以通过点击操作创建全屏白色视图,这将触发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(self.$name, placeholder: Text("Name...")) {
                    self.endEditing(true)
                }
            }
        }.tapAction {
            self.endEditing(true)
        }
    }

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

答案 8 :(得分:5)

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

.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)

        }

答案 9 :(得分:3)

因为<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="video"> <video class="video__video" autoplay loop muted playsinline poster="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg"> <source src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", type="video/mp4" /> </video> <button class="video__toggle-sound">Toggle video sound</button> </div>已过时。

keyWindow

答案 10 :(得分:3)

通过@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.gesturesGestures.allCases sugar只是糖)

.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))
    }
}

警告语

请注意,如果使用 all 手势,它们可能会发生冲突,而我并没有提出任何巧妙的解决方案。

答案 11 :(得分:2)

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

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

,然后在onCommit:

中使用它

答案 12 :(得分:2)

展开 answer by josefdolezal above,您可以在用户点击文本字段外的任意位置时隐藏键盘,如下所示:

struct SwiftUIView: View {
        @State private var textFieldId: String = UUID().uuidString // To hidekeyboard when tapped outside textFields
        @State var fieldValue = ""
        var body: some View {
            VStack {
                TextField("placeholder", text: $fieldValue)
                    .id(textFieldId)
                    .onTapGesture {} // So that outer tap gesture has no effect on field

                // any more views

            }
            .onTapGesture { // whenever tapped within VStack
                textFieldId = UUID().uuidString 
               //^ this will remake the textfields hence loosing keyboard focus!
            }
        }
    }

答案 13 :(得分: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)上正常工作。

答案 14 :(得分: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))
    }
}

答案 15 :(得分:2)

请检查https://github.com/michaelhenry/KeyboardAvoider

只需在主视图的顶部加上KeyboardAvoider {}即可。

KeyboardAvoider {
    VStack { 
        TextField()
        TextField()
        TextField()
        TextField()
    }

}

答案 16 :(得分:1)

我更喜欢使用 .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)
}

答案 17 :(得分:0)

此方法可让您隐藏键盘垫片上!

首先添加此功能(信贷来源:SwiftUI can't tap in Spacer of HStack的Casper Zandbergen)

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类中,有关更多详细信息,请参考raraelel的此处顶部答案。

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

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

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

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

假设您可以将extension的这种技术应用于您需要支持当前不支持TapGestures的任何控件,然后将onTapGestureself.endEditing()结合使用以关闭键盘在任何情况下都可以。

答案 18 :(得分:0)

键盘的Return

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

定义此全局函数:

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

并在onCommit参数中添加用法:

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

好处

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

演示

demo

答案 19 :(得分: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()

答案 20 :(得分:0)

我发现非常有用的东西是

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

然后添加到视图结构中:

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

然后

struct YourView: View {
    var body: some View {
       ParentView {
           //...
       }.contentShape(Rectangle()) //<---- This is key!
        .onTapGesture {endEditing()} 
     }
 }
    

答案 21 :(得分:0)

点击“外部”的简单解决方案对我有用:

首先在所有视图之前提供一个 ZStack。在其中放置一个背景(使用您选择的颜色)并提供一个点击手势。在手势调用中,调用我们在上面看到的“sendAction”:

import SwiftUI

struct MyView: View {
    private var myBackgroundColor = Color.red
    @State var text = "text..."

var body: some View {
    ZStack {
        self.myBackgroundColor.edgesIgnoringSafeArea(.all)
            .onTapGesture(count: 1) {
                UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
        }
        TextField("", text: $text)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .padding()
    }
  }
}
extension UIApplication {
   func endEditing() {
       sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
  }
}

sample

答案 22 :(得分:0)

一种更简洁的 SwiftUI 原生方式,可以通过点击关闭键盘,而不会阻止任何复杂的表单或诸如此类的东西......感谢 @user3441734 将 GestureMask 标记为一种干净的方法。

  1. 监控 UIWindow.keyboardWillShowNotification / willHide

  2. 通过在/a 根视图中设置的 EnvironmentKey 传递当前键盘状态

针对 iOS 14.5 进行测试。

在表单上附加关闭手势

Form { }
    .dismissKeyboardOnTap()

在根视图中设置监视器

// Root view
    .environment(\.keyboardIsShown, keyboardIsShown)
    .onDisappear { dismantleKeyboarMonitors() }
    .onAppear { setupKeyboardMonitors() }

// Monitors

    @State private var keyboardIsShown = false
    @State private var keyboardHideMonitor: AnyCancellable? = nil
    @State private var keyboardShownMonitor: AnyCancellable? = nil
    
    func setupKeyboardMonitors() {
        keyboardShownMonitor = NotificationCenter.default
            .publisher(for: UIWindow.keyboardWillShowNotification)
            .sink { _ in if !keyboardIsShown { keyboardIsShown = true } }
        
        keyboardHideMonitor = NotificationCenter.default
            .publisher(for: UIWindow.keyboardWillHideNotification)
            .sink { _ in if keyboardIsShown { keyboardIsShown = false } }
    }
    
    func dismantleKeyboarMonitors() {
        keyboardHideMonitor?.cancel()
        keyboardShownMonitor?.cancel()
    }

SwiftUI 手势 + 糖


struct HideKeyboardGestureModifier: ViewModifier {
    @Environment(\.keyboardIsShown) var keyboardIsShown
    
    func body(content: Content) -> some View {
        content
            .gesture(TapGesture().onEnded {
                UIApplication.shared.resignCurrentResponder()
            }, including: keyboardIsShown ? .all : .none)
    }
}

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

extension View {

    /// Assigns a tap gesture that dismisses the first responder only when the keyboard is visible to the KeyboardIsShown EnvironmentKey
    func dismissKeyboardOnTap() -> some View {
        modifier(HideKeyboardGestureModifier())
    }
    
    /// Shortcut to close in a function call
    func resignCurrentResponder() {
        UIApplication.shared.resignCurrentResponder()
    }
}

环境密钥

extension EnvironmentValues {
    var keyboardIsShown: Bool {
        get { return self[KeyboardIsShownEVK] }
        set { self[KeyboardIsShownEVK] = newValue }
    }
}

private struct KeyboardIsShownEVK: EnvironmentKey {
    static let defaultValue: Bool = false
}

答案 23 :(得分:0)

真正的 SwiftUI 解决方案

@State var dismissKeyboardToggle = false
var body: some View {
    if dismissKeyboardToggle {
        textfield
    } else {
        textfield
    }
    
    Button("Hide Keyboard") {
        dismissKeyboardToggle.toggle()
    }                   
}

这将完美运行

答案 24 :(得分:0)

嗯,对我来说最简单的解决方案就是使用库 here

SwiftUI 支持有些有限,我通过将此代码放置在@main 结构中来使用它:

import IQKeyboardManagerSwift

@main
struct MyApp: App {
            
    init(){
        IQKeyboardManager.shared.enable = true
        IQKeyboardManager.shared.shouldResignOnTouchOutside = true
        
    }

    ...
}

答案 25 :(得分:-2)

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