How can I detect when TextField is tapped? (SwiftUI on MacOS)
Asked Answered
U

4

6

How can I detect when a TextField is tapped? (SwiftUI on MacOS)

import SwiftUI

struct ContentView: View {
  @State var field: String = "TextField"
  
  var body: some View {
    TextField("Fill in Text", text: $field)
      .onTapGesture {
        print("Textfield pressed")
    }
  }
}

When the TextField is tapped it doesn't print "Textfield pressed". I'm guessing this is because the tap is picked up by the TextField before it can be detected by the Textfield gesture.

Unpack answered 22/11, 2019 at 4:40 Comment(0)
R
8

New Method (not backward compatible)

There is a new wrapper called @FocusState that controls the state of the keyboard and the focused keyboard ('aka' firstResponder).

Detect Focused State

you can observe on the changes of the @FocusState variable with .onChange modifier

Become First Responder ( Focused )

If you use a focused modifier on the text fields, you can make them become focused, for example, you can set the focusedField property in the code to make the binded textField become active:

enter image description here

Resign first responder ( Dismiss keyboard )

or dismiss the keyboard by setting the variable to nil:

enter image description here

Don't forget to watch the Direct and reflect focus in SwiftUI session from WWDC2021


Old Method (backward compatible and forward compatible)

Instead of Taping, You can detect when TextField is focused easily inside onEditingChanged:

TextField("Fill in Text", text: $field, onEditingChanged: { focused in
    print(focused ? "focused" : "unfocused")
}
Rickert answered 23/11, 2019 at 19:42 Comment(1)
This is wrong, with this approach you detect onEditingChanged and nothing else. If you only tap on the textfield this will not fire.Offcenter
A
6

The .simultaneousGesture modifier does what you need.

Here is an example:

struct TestTextTap: View {
    @State var field: String = "TextField"

    var body: some View {
      TextField("Fill in Text", text: $field)
        .simultaneousGesture(TapGesture().onEnded {
          print("Textfield pressed")
        })
    }
}
Agio answered 22/11, 2019 at 5:37 Comment(2)
Thanks Asperi for your response. On my machine this works on iOS simulator but does not work running as a macOS app which is what I need!Unpack
Is there any way to get part of text of textfield?Speer
A
2

I was able to get the intended results, but it feels slightly hacky until macOS can register a tap inside the text field.

I overlaid an almost transparent rectangle on top of the TextField. If the tap was sensed at that point, the TextField would not be selectable, but in SwiftUI you can make it the focus by using a @FocusState boolean.

So when the onTapGesture is sensed, it will change the @FocusState variable to true, which will let the user type into the TextField.

The little differences in macOS that make everything so much more complicated are maddening at times.

    @State var field: String = "TextField"
    @FocusState private var textFieldIsFocused: Bool

    var body: some View {
        TextField("Fill in Text", text: $field)
            .focused($textFieldIsFocused)
            .overlay((Color.white.opacity(0.0001)))
            .onTapGesture {
                self.textFieldIsFocused = true
                print("Textfield pressed")
            }
    }

Sorry, edited because I realized I did not include the .focused method!

Aprilette answered 19/10, 2021 at 21:2 Comment(0)
H
1

Unfortunately, .simultaneousGesture did not work for me on macOS, but what worked was to use Introspect to get the NSTextField and add a NSClickGestureRecognizer to it.

Then I added a @StateObject to handle the target/action.

Harneen answered 12/4, 2022 at 13:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.