SwiftUI: Add ClearButton to TextField

Viewed 16117

I am trying to add a ClearButton to TextField in SwiftUI when the particular TextField is selected.

The closest I got was creating a ClearButton ViewModifier and adding it to the TextField using .modifer()

The only problem is ClearButton is permanent and does not disappear when TextField is deselected

TextField("Some Text" , text: $someBinding).modifier(ClearButton(text: $someBinding))

struct ClearButton: ViewModifier {
    @Binding var text: String

    public func body(content: Content) -> some View {
        HStack {
            content
            Button(action: {
                self.text = ""
            }) {
                Image(systemName: "multiply.circle.fill")
                    .foregroundColor(.secondary)
            }
        }
    }
}
6 Answers

Use ZStack to position the clear button appear inside the TextField.

TextField("Some Text" , text: $someBinding).modifier(ClearButton(text: $someBinding))

struct ClearButton: ViewModifier
{
    @Binding var text: String

    public func body(content: Content) -> some View
    {
        ZStack(alignment: .trailing)
        {
            content

            if !text.isEmpty
            {
                Button(action:
                {
                    self.text = ""
                })
                {
                    Image(systemName: "delete.left")
                        .foregroundColor(Color(UIColor.opaqueSeparator))
                }
                .padding(.trailing, 8)
            }
        }
    }
}

clear button inside TextView

Use .appearance() to activate the button

var body: some View {
    UITextField.appearance().clearButtonMode = .whileEditing
    return TextField(...)
}

For reuse try with this:

func TextFieldUIKit(text: Binding<String>) -> some View{
    UITextField.appearance().clearButtonMode = .whileEditing
    return TextField("Nombre", text: text)
}

=== solution 1(best): Introspect https://github.com/siteline/SwiftUI-Introspect

import Introspect
TextField("", text: $text)
    .introspectTextField(customize: {
        $0.clearButtonMode = .whileEditing
    })

=== solution 2: ViewModifier

public struct ClearButton: ViewModifier {
    @Binding var text: String

    public init(text: Binding<String>) {
        self._text = text
    }

    public func body(content: Content) -> some View {
        HStack {
            content
            Spacer()
            Image(systemName: "multiply.circle.fill")
                .foregroundColor(.secondary) 
                .opacity(text == "" ? 0 : 1)
                .onTapGesture { self.text = "" } // onTapGesture or plainStyle button
        }
    }
}

Usage:

@State private var name: String

...

Form {
    Section() {
        TextField("NAME", text: $name).modifier(ClearButton(text: $name))
    }
}

=== solution 3: global appearance

UITextField.appearance().clearButtonMode = .whileEditing

You can add another Binding in your modifier:

@Binding var visible: Bool

then bind it to opacity of the button:

.opacity(visible ? 1 : 0)

then add another State for checking textField:

@State var showClearButton = true

And lastly update the textfield:

TextField("Some Text", text: $someBinding, onEditingChanged: { editing in
    self.showClearButton = editing
}, onCommit: {
    self.showClearButton = false
})
.modifier( ClearButton(text: $someBinding, visible: $showClearButton))

Not exactly what you're looking for, but this will let you show/hide the button based on the text contents:

HStack {
    if !text.isEmpty {
        Button(action: {
            self.text = ""
        }) {
            Image(systemName: "multiply.circle")
        }
    }
}

After initializing a new project we need to create a simple view modifier which we will apply later to our text field. The view modifier has the tasks to check for content in the text field element and display a clear button inside of it, if content is available. It also handles taps on the button and clears the content. Let’s have a look at that view modifier:

import SwiftUI

struct TextFieldClearButton: ViewModifier {
    @Binding var text: String
    
    func body(content: Content) -> some View {
        HStack {
            content
            
            if !text.isEmpty {
                Button(
                    action: { self.text = "" },
                    label: {
                        Image(systemName: "delete.left")
                            .foregroundColor(Color(UIColor.opaqueSeparator))
                    }
                )
            }
        }
    }
}

The code itself should be self explanatory and easy to understand as there is no fancy logic included in our tasks. We just wrap the textfield inside a HStack and add the button, if the text field is not empty. The button itself has a single action of deleting the value of the text field.

For the clear icon we use the delete.left icon from the SF Symbols 2 library by Apple, but you could also use another one or even your own custom one.

The binding of the modifier is the same as the one we apply to the text field. Without it we would not be able to check for content or clear the field itself.

Inside the ContentView.swift we now simply add a TextField element and apply our modifier to it — that’s all!

import SwiftUI

struct ContentView: View {
    @State var exampleText: String = ""
    
    var body: some View {
        NavigationView {
            Form {
                Section {
                    TextField("Type in your Text here...", text: $exampleText)
                        .modifier(TextFieldClearButton(text: $exampleText))
                        .multilineTextAlignment(.leading)
                }
            }
            .navigationTitle("Clear button example")
        }
    }
}

The navigation view and form inside of the ContentView are not required. You could also just add the TextField inside the body, but with a form it’s much clearer and beautiful.

And so our final result looks like this:

enter image description here

Related