I want to make a TextField in SwiftUI that adjusts its width dynamically based on the content, without adding extra spacing.
The TextField is part of an HStack with several Text views, and my goal is to have it appear seamlessly as part of the text. For example, the result should look something like this:
Text("Text(") + Text(""") + TextField + Text(""") + Text(")")
I tried using the .fixedSize() modifier, which works fine when the text becomes longer. However, it does not shrink the width when the text gets shorter, the TextField retains the initial size.
Here’s a simplified example of what I’ve tried:
import SwiftUI
struct ContentView: View {
@State private var text: String = ""
var body: some View {
HStack(spacing: 0) {
Text("Text(")
Text(""")
TextField("Placeholder", text: $text)
.fixedSize()
.textFieldStyle(PlainTextFieldStyle())
Text(""")
Text(")")
}
.font(.system(size: 15, weight: .medium, design: .monospaced))
}
}
The TextField works fine for longer text, but it doesn’t shrink dynamically when the content becomes shorter than initial one.
Is there a way to have the TextField behave like the Text views, adjusting its width dynamically without leaving extra spacing? Or is manual width calculation the only option?
Any help would be greatly appreciated!
2
Answers
You might experiment with the
fixedSize
modifier:Possibly combined with constraining the min width:
Note that the TextField’s width will not shrink smaller than its prompt/placeholder would take.
Update:
This simple solution does not always work correctly: when text has been entered, then this text has been selected, and then deleted, the text field won’t shrink again. It seems to work, when deleting single characters, though. So, we need to investigate this further.
You could show the same text in the background and measure its width using
.onGeometryChange
. Then apply this width as fixed width to theTextField
..onGeometryChange
also reports the initial size..fixedSize()
to the background version, to allow it to exceed the bounds of its frame..hidden()
, to prevent it from being seen.You might like to wrap the size-reading part as a
ViewModifier
, to make it more re-usable. The answer to How to get size of child? shows how this can be done (it was my answer).