
I'm building a simple login screen and I noticed that when I change between a TextField and a SecureField my screen moves a little and I think it's because their heights are changing? Or maybe I'm doing something wrong?
Here's the code:
struct LoginView: View {
@State var username: String
@State var password: String
@State var showingPassword: Bool = false
var body: some View {
VStack(spacing: 15) {
Text("Welcome")
.font(.largeTitle)
.bold()
.padding(.bottom)
TextField("Username", text: $username)
.customTextField()
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color("charcoal"), lineWidth: 0.6))
HStack {
if showingPassword {
TextField("Password", text: $password)
.customTextField()
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color("charcoal"), lineWidth: 0.6))
.border(.pink)
Image(systemName: "eye.slash")
.onTapGesture {
showingPassword.toggle()
}
.border(.pink)
} else {
SecureField("Password", text: $password)
.customTextField()
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color("charcoal"), lineWidth: 0.6))
.border(.green)
Image(systemName: "eye")
.onTapGesture {
showingPassword.toggle()
}
.foregroundColor(Color("charcoal"))
.border(.green)
}
}
}
.padding(.horizontal)
}
}
and the modifier:
struct TextFieldModifier: ViewModifier {
func body(content: Content) -> some View {
content
.padding(.horizontal)
.padding(.vertical, 12)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.gray, lineWidth: 0.6))
.lineLimit(5)
}
}
extension View {
func customTextField() -> some View {
modifier(TextFieldModifier())
}
}
Yes, there is indeed a height difference. You can inspect it by using Xcode's UI inspector on a view as simple as:
var body: some View {
VStack {
TextField("Username", text: $username)
SecureField("Password", text: $password)
}
}
On my simulator, the text field has height 22, and the secure field has height 21.


This difference in height is also visible in the picture in Apple's documentation page for SecureField. The picture is:

You can try opening the image up in Preview.app or similar and count the pixels (I did that!).
I'm not sure if this difference is intended or not.
This is indeed janky! I had the same problem and solved it by adding .frame(height: 22) to both fields, It's probably not needed on the TextField, but I decided to add it there, in the event that it get's rendered differently in some case.
Here's my full code snippet:
HStack {
Group {
if showPassword {
TextField("Password",
text: $password,
prompt: Text("Password"))
.frame(height: 22)
} else {
SecureField("Password",
text: $password,
prompt: Text("Password"))
.frame(height: 22)
}
}
.padding(10)
Button {
showPassword.toggle()
} label: {
Image(systemName: showPassword ? "eye.slash" : "eye")
.padding(.horizontal)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With