iOS 11 disable password autofill accessory view option?

后端 未结 20 2477
孤独总比滥情好
孤独总比滥情好 2020-11-28 04:20

As of now I would like to opt out of the new option iOS 11 gives, that is to suggest passwords in the app. When I run the app on iOS 11 I get the autofill option on top of t

20条回答
  •  抹茶落季
    2020-11-28 04:36

    I think set all UITextField textContentType in form to UITextContentType("") or .oneTimeCode is not a clean solution. Enable/Disable isSecureTextEntry still give you the same issue.

    @Gal Shahar 's Answer is nice but it is still not perfect. The masked char is not the same as the masked char that used in secure entry text from apple. It should use Unicode Character 'BLACK CIRCLE' (U+25CF) https://www.fileformat.info/info/unicode/char/25cf/index.htm

    Also, it is not handling cursor movement. It will change the cursor position to the end of the text when inserting text in the middle. It will give you the wrong value when selecting and replacing text.

    When you decide to use custom isSecureEntryText to avoid autofill password, here is the code:

    Swift 5 (simple version)

    @IBOutlet weak var passwordTextField: UITextField!
    
    var maskedPasswordChar: String = "●"
    var passwordText: String = ""
    var isSecureTextEntry: Bool = true {
        didSet {
            let selectedTextRange = passwordTextField.selectedTextRange
            passwordTextField.text = isSecureTextEntry ? String(repeating: maskedPasswordChar, count: passwordText.count) : passwordText
            passwordTextField.selectedTextRange = selectedTextRange
        }
    }
    
    //this is UITextFieldDelegate
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    
        if textField == passwordTextField {
            //update password string
            if let swiftRange = Range(range, in: passwordText) {
                passwordText = passwordText.replacingCharacters(in: swiftRange, with: string)
            } else {
                passwordText = string
            }
    
            //replace textField text with masked password char
            textField.text =  isSecureTextEntry ? String(repeating: maskedPasswordChar, count: passwordText.count) : passwordText
    
            //handle cursor movement
            if let newPosition = textField.position(from: textField.beginningOfDocument, offset: range.location + string.utf16.count) {
                textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
            }
            return false
        }
        return true
    }
    

    Swift 5 (COMPLETE version with securing last char animation)

    private struct Constants {
        static let SecuringLastCharPasswordDelay = 1.5
    }
    
    @IBOutlet weak var passwordTextField: UITextField!
    
    private var secureTextAnimationQueue: [String] = []
    
    var maskedPasswordChar: String = "●"
    var passwordText: String = ""
    var isSecureTextEntry: Bool = true {
        didSet {
            secureTextAnimationQueue.removeAll()
            let selectedTextRange = passwordTextField.selectedTextRange
            passwordTextField.text = isSecureTextEntry ? String(repeating: maskedPasswordChar, count: passwordText.count) : passwordText
            passwordTextField.selectedTextRange = selectedTextRange
        }
    }
    
    //this is UITextFieldDelegate
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    
        if textField == passwordTextField {
            //update password string
            if let swiftRange = Range(range, in: passwordText) {
                passwordText = passwordText.replacingCharacters(in: swiftRange, with: string)
            } else {
                passwordText = string
            }
    
            //replace textField text with masked password char
            updateTextFieldString(textField, shouldChangeCharactersIn: range, replacementString: string)
    
            //handle cursor movement
            if let newPosition = textField.position(from: textField.beginningOfDocument, offset: range.location + string.utf16.count) {
                textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
            }
            return false
        }
        return true
    }
    
    private func updateTextFieldString(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) {
        if isSecureTextEntry {
            if string.count == .one, let text = textField.text {
                let maskedText = String(repeating: maskedPasswordChar, count: text.count)
    
                var newMaskedText = String()
                if let swiftRange = Range(range, in: maskedText) {
                    newMaskedText = maskedText.replacingCharacters(in: swiftRange, with: string)
                } else {
                    newMaskedText = text + maskedText
                }
    
                textField.text = newMaskedText
                secureTextAnimationQueue.append(string)
                asyncWorker.asyncAfter(deadline: .now() + Constants.SecuringLastCharPasswordDelay) { [weak self] in
                    self?.securingLastPasswordChar()
                }
            } else {
                secureTextAnimationQueue.removeAll()
                textField.text = String(repeating: maskedPasswordChar, count: passwordText.count)
            }
        } else {
            textField.text = passwordText
        }
    }
    
    private func securingLastPasswordChar() {
        guard secureTextAnimationQueue.count > .zero, isSecureTextEntry else { return }
        secureTextAnimationQueue.removeFirst()
        if secureTextAnimationQueue.count == .zero {
            let selectedTextRange = passwordTextField.selectedTextRange
            passwordTextField.text = String(repeating: maskedPasswordChar, count: passwordText.count)
            passwordTextField.selectedTextRange = selectedTextRange
        }
    }
    

提交回复
热议问题