Eric
Eric

Reputation: 793

Select all text in TextField upon click SwiftUI

How do i select all text when clicking inside the textfield? Just like how a web browser like chrome would when you click inside the address bar.

import SwiftUI
import AppKit

   struct ContentView: View {

    var body: some View {
        TextField("Enter a URL", text: $site)
    
  }
}

Upvotes: 18

Views: 11695

Answers (7)

Konstantin Cherkasov
Konstantin Cherkasov

Reputation: 3343

macOS 12.0+

struct ContentView: View {
    @State var text: String = ""
    @FocusState var focused: Bool
    
    var body: some View {
        TextField("", text: $text)
            .focused($focused)
            .overlay {
                if !focused {
                    Color.clear
                        .contentShape(Rectangle())
                        .onTapGesture {
                            focused = true
                        }
                }
            }
    }
}

Upvotes: 0

Mojtaba Hosseini
Mojtaba Hosseini

Reputation: 119177

The native way

With Xcode 16 and from iOS 18 / macOS 15

You can use the pass a TextSelection to text editing controls like:

@State private var selection: TextSelection?

var body: some View {
    TextField("Enter a URL", text: $site, selection: $selection)
}

Upvotes: 7

budiDino
budiDino

Reputation: 13527

I had issues when using code from some of the answers previously posted here, so I'm just sharing what worked for me.

First of all, you will have to import Combine:

import Combine

Then you can add this to your TextField:

.onReceive(NotificationCenter.default.publisher(
    for: UITextField.textDidBeginEditingNotification)) { _ in
        DispatchQueue.main.async {
            UIApplication.shared.sendAction(
                #selector(UIResponder.selectAll(_:)), to: nil, from: nil, for: nil
            )
        }
    }

Alternatively, you could use a ViewModifier for a more reusable approach:

public struct SelectAllTextOnBeginEditingModifier: ViewModifier {
    public func body(content: Content) -> some View {
        content
            .onReceive(NotificationCenter.default.publisher(
                for: UITextField.textDidBeginEditingNotification)) { _ in
                    DispatchQueue.main.async {
                        UIApplication.shared.sendAction(
                            #selector(UIResponder.selectAll(_:)), to: nil, from: nil, for: nil
                        )
                    }
                }
        }
}

extension View {
    public func selectAllTextOnBeginEditing() -> some View {
        modifier(SelectAllTextOnBeginEditingModifier())
    }
}

And then just add this to your TextField:

.selectAllTextOnBeginEditing()

Upvotes: 5

LeonardoXUI
LeonardoXUI

Reputation: 580

Here’s my solution:

    struct ContentView: View {
    
    @FocusState var isFocused
    @State var text = "Text"
    
    var body: some View {
        TextField("Some Text", text: $text)
            .focused($isFocused)
            .onChange(of: isFocused) { focus in
                if focus {
                    DispatchQueue.main.async {
                        UIApplication.shared.sendAction(#selector(UIResponder.selectAll(_:)), to: nil, from: nil, for: nil)
                    }
                }
            }
    }
}

If .focused doesn’t work for some weird reason, you can add a .onTapGesture to the TextField and use a Bool variable.

Like this:

struct ContentView: View {
    
    @State var isFocused = false
    @State var text = "Text"
    
    var body: some View {
        TextField("Some Text", text: $text)
            .onTapGesture {
                isFocused = true
            }
            .onSubmit {
                isFocused = false
            }
            .onChange(of: isFocused) { focus in
                if focus {
                    DispatchQueue.main.async {
                        UIApplication.shared.sendAction(#selector(UIResponder.selectAll(_:)), to: nil, from: nil, for: nil)
                    }
                }
            }
    }
}

Upvotes: 11

Rick Pasveer
Rick Pasveer

Reputation: 720

I've created a ViewModifier to select all the text in a TextField. Only downside is, it won't work with multiple TextFields.

public struct SelectTextOnEditingModifier: ViewModifier {
    public func body(content: Content) -> some View {
        content
            .onReceive(NotificationCenter.default.publisher(for: UITextField.textDidBeginEditingNotification)) { obj in
                if let textField = obj.object as? UITextField {
                    textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument)
                }
            }
    }
}

extension View {

    /// Select all the text in a TextField when starting to edit.
    /// This will not work with multiple TextField's in a single view due to not able to match the selected TextField with underlying UITextField
    public func selectAllTextOnEditing() -> some View {
        modifier(SelectTextOnEditingModifier())
    }
}

usage:

TextField("Placeholder", text: .constant("This is text data"))
    .selectAllTextOnEditing()

Upvotes: 6

Reinchold
Reinchold

Reputation: 66

Here is my solution

import SwiftUI
import PlaygroundSupport

struct ContentView: View {

    @State private var renameTmpText: String = ""
    @FocusState var isFocused: Bool
    @State private var textSelected = false

    var body: some View {
        TextEditor(text: $renameTmpText)
            .padding(3)
            .border(Color.accentColor, width: 1)
            .frame(width: 120, height: 40)
            .onExitCommand(perform: {
                renameTmpText = ""
            })
            .onAppear {
                renameTmpText = "Test"
                isFocused = true
            }
            .focused($isFocused)
            .onReceive(NotificationCenter.default.publisher(for: NSTextView.didChangeSelectionNotification)) { obj in
                if let textView = obj.object as? NSTextView {
                    guard !textSelected else { return }
                    let range = NSRange(location: 0, length:     textView.string.count)
                    textView.setSelectedRange(range)
                    textSelected = true
                }
            }
            .onDisappear { textSelected = false }
    }
}

let view = ContentView()
PlaygroundPage.current.setLiveView(view)

Upvotes: 1

Raja Kishan
Raja Kishan

Reputation: 18904

SwiftUI Solution:

struct ContentView: View {
    var body: some View {
        TextField("Placeholder", text: .constant("This is text data"))
            .onReceive(NotificationCenter.default.publisher(for: UITextField.textDidBeginEditingNotification)) { obj in
                if let textField = obj.object as? UITextField {
                    textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument)
                }
            }
    }
}

Note : import Combine


Use UIViewRepresentable and wrap UITextField and use textField.selectedTextRange property with delegate.

Here is the sample demo

struct HighlightTextField: UIViewRepresentable {
    
    @Binding var text: String
    
    func makeUIView(context: Context) -> UITextField {
        let textField = UITextField()
        textField.delegate = context.coordinator
        return textField
    }
    
    func updateUIView(_ textField: UITextField, context: Context) {
        textField.text = text
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }
    
    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: HighlightTextField
        
        init(parent: HighlightTextField) {
            self.parent = parent
        }
        
        func textFieldDidBeginEditing(_ textField: UITextField) {
            textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument)
        }
    }
}



For macOS

struct HighlightTextField: NSViewRepresentable {
    
    @Binding var text: String
    
    func makeNSView(context: Context) -> CustomTextField {
        CustomTextField()
    }
    
    func updateNSView(_ textField: CustomTextField, context: Context) {
        textField.stringValue = text
    }
}

class CustomTextField: NSTextField {
    override func mouseDown(with event: NSEvent) {
        if let textEditor = currentEditor() {
            textEditor.selectAll(self)
        }
    }
}

Upvotes: 31

Related Questions