'How can I remove Textfield focus when I press return or click outside Textfield? (SwiftUI, MacOS)

How can I remove Textfield focus when I press return or click outside Textfield? Note that this is SwiftUI on MacOS.

If I do this:

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

then click into the TextField and edit it then click on the Button the TextField does not lose focus. How can I make it exit editing mode and lose focus.

I would also like to lose focus from the TextField if I press Return. I used the Binding initialiser with get and set because I thought I could somehow intercept keypresses and detect the 'Return' character but this doesn't work.

Any help appreciated :-)



Solution 1:[1]

Here are possible variants

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

backup

Solution 2:[2]

Just add a onTapGesture to your VStack with the following line:

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

This code will close the keyboard.

Example:

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

Solution 3:[3]

Based on the previous answers I have created a .removeFocusOnTap extension that can be attached to any view that should remove the focus when tapped (I use it on the background view). It works for both iOS and 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:

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Tobias Hesselink
Solution 3