Reputation: 393
Am trying to figure out how to dismiss the iOS decimal keyboard if my TextFields do not have focus. Obviously when clicking a button I can call a dismiss keyboard function.
But what I wanted to learn was how to dismiss the keyboard of if the user hits return OR clicks off of the TextFields.
Before I could figure out the "user hit return part" I got stuck at the user being able to click away from the TextFields.
While I can add a onTapGesture to the text fields, I wonder if I can do something like detecting a tapGesture to the entire screen, so that if the user taps on any blank space I could call the dismiss keyboard function.
import SwiftUI
struct ContentView: View {
@State var textField1 = ""
@State var textField2 = ""
@State var hasFocus = "No text field has focus"
@FocusState var leftTyping : Bool
@FocusState var rightTyping : Bool
var body: some View {
VStack {
Text(hasFocus)
.font(.largeTitle)
HStack {
TextField("left" , text: $textField1)
.focused($leftTyping)
.keyboardType(.decimalPad)
.onChange(of: leftTyping) {
if leftTyping == false, rightTyping == false {
hideKeyboard()
hasFocus = "No text field has focus"
}
else if leftTyping {
hasFocus = "focus on left field"
}
}
TextField("right", text: $textField2)
.focused($rightTyping)
.keyboardType(.decimalPad)
.onChange(of: rightTyping) {
if leftTyping == false, rightTyping == false {
hideKeyboard()
hasFocus = "No text field has focus"
}
else if rightTyping {
hasFocus = "focus on right field"
}
}
}
Button ("steal focus"){
hideKeyboard()
hasFocus = "No text field has focus"
}
.buttonStyle(.borderedProminent)
.tint(.brown)
.font(.largeTitle)
.padding(10)
.foregroundStyle(.white)
}
.padding()
}
func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
#Preview {
ContentView()
}
Upvotes: 0
Views: 2078
Reputation: 13996
There are many ways to accomplish this - from simple to sophisticated. Here's the simplest way: it uses the fact that your background is also a view, which can accept clicks:
var body: some View {
VStack { // Nothing changes in your VStack
Text(hasFocus)
// ...
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity) // <-- Change size of the frame to take the entire screen
.background( // <-- add background
Color.white // <-- this is also a view
.onTapGesture { // <-- add tap gesture to it
hideKeyboard() // <-- and hide keyboard from it
}
)
This resolves your goal of dismissing keyboard "if the user taps on any blank space". It won't, however, dismiss keyboard when you tap on label (or any other element that is shown on top of background), which is one of the limitations of this solution. How to solve that limitation very much depends on context. For instance in case of this simple form, I would either leave it as is (since label doesn't take much space, and the probability that user taps it is low), or add a tapGesture to a label as well - it's just one element after all.
Upvotes: 2