For those still looking for a simple solution to this issue (requires iOS 15 for swiftUI 3):
With the new @FocusState introduced in swiftUI 3, it's possible to keep focus and keyboard open while changing State.
By using the opacity modifier instead of conditionally changing between SecureField and TextField, the focus can jump between the two without issues with the keyboard.
This allows you to toggle between revealing and hiding the password with the the eye button included in the ZStack.
import SwiftUI
struct SecureTextFieldWithReveal: View {
@FocusState var focus1: Bool
@FocusState var focus2: Bool
@State var showPassword: Bool = false
@State var text: String = ""
var body: some View {
HStack {
ZStack(alignment: .trailing) {
TextField("Password", text: $text)
.modifier(LoginModifier())
.focused($focus1)
.opacity(showPassword ? 1 : 0)
SecureField("Password", text: $text)
.modifier(LoginModifier())
.focused($focus2)
.opacity(showPassword ? 0 : 1)
Button(action: {
showPassword.toggle()
if showPassword { focus1 = true } else { focus2 = true }
}, label: {
Image(systemName: self.showPassword ? "eye.slash.fill" : "eye.fill").font(.system(size: 16, weight: .regular))
.padding()
})
}
}
}
}
Password field hidden
Password field revealed
This is the code in LoginModifier:
import SwiftUI
struct LoginModifier: ViewModifier {
var borderColor: Color = Color.gray
func body(content: Content) -> some View {
content
.disableAutocorrection(true)
.autocapitalization(.none)
.textContentType(.password)
.padding()
.overlay(RoundedRectangle(cornerRadius: 10).stroke(borderColor, lineWidth: 1))
}
}
The only issue I've had with this method is that on regaining focus SecureField will automatically clear any text already entered if you start typing. This seems to be a design choice by Apple.