Introduction
Setting isSecureTextEntry = true on a UITextField obscures the password by replacing characters with dots. This is the standard iOS approach and also disables text selection, auto-correction, and screenshot capture of the field content. For a password visibility toggle (show/hide button), combine isSecureTextEntry with a toggle button that switches the property at runtime. SwiftUI provides the built-in SecureField view for the same purpose.
Basic Password Field (UIKit)
1import UIKit
2
3class LoginViewController: UIViewController {
4 let passwordField = UITextField()
5
6 override func viewDidLoad() {
7 super.viewDidLoad()
8
9 passwordField.isSecureTextEntry = true
10 passwordField.placeholder = "Enter password"
11 passwordField.borderStyle = .roundedRect
12 passwordField.textContentType = .password // Enables Keychain autofill
13 passwordField.autocorrectionType = .no
14 passwordField.autocapitalizationType = .none
15
16 view.addSubview(passwordField)
17 // ... layout constraints
18 }
19}
Setting isSecureTextEntry = true does the following:
Replaces each character with a bullet dot after a brief moment
Disables text selection and copy/paste
Prevents the field content from appearing in screenshots
Disables predictive text and autocorrection
Interface Builder (Storyboard)
In Interface Builder, select the UITextField and in the Attributes Inspector:
Check "Secure Text Entry" under the Text Input Traits section
Set "Content Type" to "Password" for Keychain integration
Set "Autocorrection" to "No"
Set "Autocapitalization" to "None"
Password Visibility Toggle
1import UIKit
2
3class PasswordField: UITextField {
4 private let toggleButton = UIButton(type: .custom)
5
6 override init(frame: CGRect) {
7 super.init(frame: frame)
8 setup()
9 }
10
11 required init?(coder: NSCoder) {
12 super.init(coder: coder)
13 setup()
14 }
15
16 private func setup() {
17 isSecureTextEntry = true
18 textContentType = .password
19 autocorrectionType = .no
20
21 // Configure toggle button
22 toggleButton.setImage(UIImage(systemName: "eye.slash"), for: .normal)
23 toggleButton.setImage(UIImage(systemName: "eye"), for: .selected)
24 toggleButton.tintColor = .gray
25 toggleButton.addTarget(self, action: #selector(toggleVisibility), for: .touchUpInside)
26 toggleButton.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
27
28 rightView = toggleButton
29 rightViewMode = .always
30 }
31
32 @objc private func toggleVisibility() {
33 isSecureTextEntry.toggle()
34 toggleButton.isSelected = !isSecureTextEntry
35
36 // Fix cursor position after toggling
37 if let existingText = text, isFirstResponder {
38 deleteBackward() // Clear and re-set to fix cursor
39 insertText(existingText)
40 }
41 }
42}
43
44// Usage
45let passwordField = PasswordField()
46passwordField.placeholder = "Password"
Handling the Cursor Position Bug
When toggling isSecureTextEntry while the field is focused, the cursor may jump to the beginning. The fix:
1@objc func toggleSecureEntry() {
2 passwordField.isSecureTextEntry.toggle()
3
4 // Preserve cursor position
5 if let textRange = passwordField.textRange(
6 from: passwordField.endOfDocument,
7 to: passwordField.endOfDocument
8 ) {
9 passwordField.selectedTextRange = textRange
10 }
11}
SwiftUI SecureField
1import SwiftUI
2
3struct LoginView: View {
4 @State private var password = ""
5 @State private var showPassword = false
6
7 var body: some View {
8 VStack(spacing: 16) {
9 // Simple secure field
10 SecureField("Password", text: $password)
11 .textFieldStyle(.roundedBorder)
12 .textContentType(.password)
13
14 // Toggle between SecureField and TextField
15 HStack {
16 if showPassword {
17 TextField("Password", text: $password)
18 .textFieldStyle(.roundedBorder)
19 .textContentType(.password)
20 .autocorrectionDisabled()
21 .textInputAutocapitalization(.never)
22 } else {
23 SecureField("Password", text: $password)
24 .textFieldStyle(.roundedBorder)
25 .textContentType(.password)
26 }
27
28 Button {
29 showPassword.toggle()
30 } label: {
31 Image(systemName: showPassword ? "eye" : "eye.slash")
32 .foregroundColor(.gray)
33 }
34 }
35 }
36 .padding()
37 }
38}
Objective-C
1// Basic secure field
2UITextField *passwordField = [[UITextField alloc] init];
3passwordField.secureTextEntry = YES;
4passwordField.placeholder = @"Enter password";
5passwordField.textContentType = UITextContentTypePassword;
6passwordField.autocorrectionType = UITextAutocorrectionTypeNo;
7
8// Toggle visibility
9- (void)togglePasswordVisibility:(UIButton *)sender {
10 self.passwordField.secureTextEntry = !self.passwordField.secureTextEntry;
11
12 // Fix cursor position
13 NSString *temp = self.passwordField.text;
14 self.passwordField.text = @"";
15 self.passwordField.text = temp;
16}
Password Strength Indicator
1func passwordStrength(_ password: String) -> (String, UIColor) {
2 let length = password.count
3 let hasUpper = password.range(of: "[A-Z]", options: .regularExpression) != nil
4 let hasLower = password.range(of: "[a-z]", options: .regularExpression) != nil
5 let hasDigit = password.range(of: "[0-9]", options: .regularExpression) != nil
6 let hasSpecial = password.range(of: "[^A-Za-z0-9]", options: .regularExpression) != nil
7
8 let score = [hasUpper, hasLower, hasDigit, hasSpecial].filter { $0 }.count
9
10 if length < 8 { return ("Weak", .systemRed) }
11 if score < 3 { return ("Medium", .systemOrange) }
12 return ("Strong", .systemGreen)
13}
Common Pitfalls
Toggling isSecureTextEntry resets the cursor: When switching between secure and plain text while the field is focused, iOS may reset the cursor to the beginning or clear the text. Save and restore the text and cursor position after toggling to avoid user frustration.
Not setting textContentType to .password: Without textContentType = .password, iOS does not offer Keychain autofill for the field. Users expect the password autofill prompt, especially with Face ID/Touch ID integration. Set it to .password for login and .newPassword for registration.
Allowing autocorrection on password fields: Autocorrection caches typed text in the keyboard dictionary, potentially leaking password fragments. Always set autocorrectionType = .no and autocapitalizationType = .none on password fields.
Using custom font with secure entry: Setting a custom font on a secure text field may cause the bullet dots to render at the wrong size or position on some iOS versions. Test with your custom font, or use the system font for the secure state.
Screenshots still capturing the field in SwiftUI: SecureField in SwiftUI hides content from screenshots, but switching to a TextField for the "show password" state makes it capturable again. If screenshot protection is required, keep the field as SecureField and disable the show toggle.
Summary
Set isSecureTextEntry = true on UITextField to obscure password characters with dots
Use SecureField in SwiftUI for the same functionality with less code
Add a toggle button to switch between isSecureTextEntry = true/false for show/hide password
Set textContentType = .password to enable Keychain autofill and Face ID/Touch ID
Disable autocorrection and autocapitalization on all password fields
Fix the cursor position bug when toggling secure entry by resetting the text range