1

I have a SwiftUI Form that contains a Picker, a TextField, and a Text:

struct ContentView: View {

    var body: some View {
        Form {
            Section {
                Picker(selection: $selection, label: label) {
                    // Code to populate picker
                }.pickerStyle(SegmentedPickerStyle())
                HStack {
                    TextField(title, text: $text)
                    Text(text)
                }
            }
        }
    }

}

The code above results in the following UI:

Screenshot of iOS application that displays a picker and a text field with a label

I am able to easily select the second item in the picker, as shown below:

Screenshot of iOS application that displays an updated selection in the picker

Below, you can see that I am able to initiate text entry by tapping on the TextField:

Screenshot of iOS application that displays keyboard for text entry in text field

In order to dismiss the keyboard when the Picker value is updated, a Binding was added, which can be seen in the following code block:

Picker(selection: Binding(get: {
        // Code to get selected segment
    }, set: { (index) in
        // Code to set selected segment
        self.endEditing()
    }), label: label) {
        // Code to populate picker
    }

The call to self.endEditing() is provided in the following method:

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

The following screenshot displays that selecting a different segment of the Picker dismisses the keyboard:

Screenshot of iOS application that displays dismissed keyboard when selecting new segment of picker

Up to this point, everything works as expected. However, I would like to dismiss the keyboard when tapping anywhere outside of the TextField since I am unable to figure out how to dismiss the keyboard when dragging the Form's containing scroll view.

I attempted to add the following implementation to dismiss the keyboard when tapping on the Form:

Form {
    Section {
        // Picker
        HStack {
            // TextField
            // Text
        }
    }
}.onTapGesture {
    self.endEditing()
}

Below, the following two screenshot displays that the TextField is able to become the first responder and display the keyboard. The keyboard is then successfully dismissed when tapping outside of the TextField:

Screenshot of iOS application that displays displayed keyboard after tapping text field

However, the keyboard will not dismiss when attempting to select a different segment of the `Picker. In fact, I cannot select a different segment, even after the keyboard has been dismissed. I presume that a different segment cannot be selected because the tap gesture attached to the form is preventing the selection.

The following screenshot shows the result of attempting to select the second value in the Picker while the keyboard is shown and the tap gesture is implemented:

Screenshot of iOS application that displays displayed keyboard and previously-selected picker segment

What can I do to allow selections of the Picker's segments while allowing the keyboard to be dismissed when tapping outside of the TextField?

2 Answers 2

2
import SwiftUI

struct ContentView: View {
    @State private var tipPercentage = 2
    let tipPercentages = [10, 15, 20, 25, 0]
    @State var text = ""

    @State var isEdited = false
    var body: some View {
        Form {
            Section {
                Picker("Tip percentage", selection: $tipPercentage) {
                    ForEach(0 ..< tipPercentages.count) {
                        Text("\(self.tipPercentages[$0])%")
                    }
                }
                .pickerStyle(SegmentedPickerStyle())
                HStack {
                    TextField("Amount", text: $text, onEditingChanged: { isEdited in
                        self.isEdited = isEdited
                    }).keyboardType(.numberPad)
                }
            }
        }.gesture(TapGesture().onEnded({
            UIApplication.shared.windows.first{$0.isKeyWindow }?.endEditing(true)
        }), including: isEdited ? .all : .none)
    }

}

Form's tap gesture (to finish editing by tap anywhere) is enabled only if text field isEdited == true

Once isEdited == false, your picker works as before.

Sign up to request clarification or add additional context in comments.

Comments

0

You could place all of your code in an VStack{ code }, add a Spacer() to it and add the onTap to this VStack. This will allow you to dismiss the keyboard by clicking anywhere on the screen.

See code below:

import SwiftUI

struct ContentView: View {

        @State private var text: String = "Test"

        var body: some View {
            VStack {
                HStack {
                    TextField("Hello World", text: $text)
                    Spacer()
                }
                Spacer()
            }
            .background(Color.red)
            .onTapGesture {
                self.endEditing()
            }
        }

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

Changing the background color of an HStack or VStack to red simplifies figuring out where the user may click to dismiss.

Copy and paste code for a ready to run example.

2 Comments

Unfortunately, the result of this solution is the same as the result of my solution.
This is because your .onTapGesture is attached to the Form{ code }. Try to use a VStack{} and HStack{} with Spacers() to "color the HStack { TextField() Text() } and empty space below red". Currently your whole view is "red".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.