iOS 11禁用密码自动填充附件查看选项?

问题描述 投票:61回答:15

截至目前,我想选择退出iOS 11提供的新选项,即在应用程序中建议密码。当我在iOS 11上运行应用程序时,我在键盘顶部获得了自动填充选项,我的用户名和密码文本字段甚至都没有出现。

所以,我的问题是,如何一起禁用新密码自动填充功能,以便键盘上的键完全不显示,整体行为与iOS 11之前的相同?

enter image description here

ios swift ios11
15个回答
54
投票

iOS 11和12 - Swift 4.2(更新):

        if #available(iOS 12, *) {
            // iOS 12: Not the best solution, but it works.
            passwordTextField.textContentType = .oneTimeCode
        } else {
            // iOS 11: Disables the autofill accessory view. 
            // For more information see the explanation below.
            emailTextField.textContentType = .init(rawValue: "")
            passwordTextField.textContentType = .init(rawValue: "")
        }

iOS 11说明:

确保你设置了所有这样的UITextField对象。

例如,如果您有UITextField对象,其中用户必须输入他的电子邮件地址,而另一个用户必须输入密码,请将UITextContentType("")分配给他们的textContentType属性。否则它将无法工作,仍会显示自动填充附件视图。


1
投票

你可以在这里尝试不同的答案,总结它可能删除accessoryview。但这留下了一些错误。

How to hide inputAccessoryView without dismissing keyboard

您可以尝试实现自定义键盘,仅用于密码字段。还尝试禁用您的文本字段的建议,我认为这也隐藏了accessoryView。

编辑:苹果论坛在同一问题上没有答案。另外,我在官方uitextfield文档中找不到任何相关内容。

https://forums.developer.apple.com/thread/82442


1
投票

您可以通过在密码文本字段中指定虚拟textContentType来“关闭”用户名/密码组合检测。

passwordFormField.textContentType = UITextContentType("dummy")

这会关闭密码字段和其前面的电子邮件字段的键符号,这样您就不会使用其中一个预定义值,并且可以避免在键盘附件视图中显示不相关的建议。


1
投票
self.passwordTextField.autocorrectionType = NO;

它似乎不起作用,钥匙链标志仍然存在,

self.passwordTextField.textContentType = UITextContentTypeName;

上面的代码确实有效,但是如果用户设置了他们的Apple ID帐户,那么,通过将autocorrectionType设置为No,键盘上将显示苹果ID的名称无法禁用它,不确定Apple是否仍会优化此自动填充功能,现在它很麻烦。


1
投票

据我所知,Bem的答案在iOS 12中不起作用,Gal Shahar的答案没有解释一些边缘情况(例如,如果用户一次删除多个字符)。我使用IBAction解决了这个问题,因此无需检查iOS版本。我只是一个初学者,所以这可能不是“最好的”答案或最有效的,但它对我来说最有意义:

首先,取消选中Storyboard中的“Secure Text Entry”或通过密码UITextField的代码将其设置为“false”/“NO”。这将阻止iOS尝试自动填充。

然后,将您的密码UITextField链接到IBAction。我的号召是:

  • 编辑确实开始了
  • 编辑确实改变了
  • 编辑结束了

我编写的IBAction函数确定用户的起始密码与输入密码UITextField的内容之间的差异,并根据以下信息创建新密码:

class Login: UIViewController {
    var password = ""

    override func viewDidLoad() { super.viewDidLoad() }

    @IBAction func editPasswordField(_ sender: UITextField) {
        var input = Array(sender.text ?? "")
        var oldPassword = Array(password)
        var newPassword = Array("")

        //if character(s) are simply deleted from "passwordField" (not replaced or added to), "cursorPosition" is used to determine which corresponding character(s) need to also be removed from "oldPassword"
        //this is indicated by "input" comprising of only "•" (bullets) and being shorter in length than "oldPassword"
        var onlyBullets = true
        for char in input { if char != "•" { onlyBullets = false } }
        if onlyBullets && input.count < oldPassword.count {
            if let selectedRange = sender.selectedTextRange {
                let cursorPosition = sender.offset(from: sender.beginningOfDocument, to: selectedRange.start)
                let prefix = String(oldPassword.prefix(cursorPosition))
                let suffix = String(oldPassword.suffix(input.count - cursorPosition))
                input = Array(prefix + suffix)
            } else { input = Array("") }
        }

        //if no changes were made via input, input would comprise solely of a number of bullets equal to the length of "oldPassword"
        //therefore, the number of changes made to "oldPassword" via "input" can be measured with "bulletDifference" by calculating the number of characters in "input" that are NOT bullets
        var bulletDifference = oldPassword.count
        for char in input { if char == "•" { bulletDifference -= 1 } }

        //the only way "bulletDifference" can be less than 0 is if a user copy-pasted a bullet into "input", which cannot be allowed because it breaks this function
        //if a user pastes bullet(s) into "input", "input" is deleted
        //an edge case not accounted for is pasting a mix of characters and bullets (i.e. "ex•mple") when "oldPassword.count" exceeds the number of bullets in the mixed input, but this does not cause crashes and therefore is not worth preventing
        if bulletDifference < 0 {
            bulletDifference = oldPassword.count
            input = Array("")
        }

        //"bulletDifference" is used to remove every character from "oldPassword" that corresponds with a character in "input" that has been changed
        //a changed character in "input" is indicated by the fact that it is not a bullet
        //once "bulletDifference" equals the number of bullets deleted, this loop ends
        var bulletsDeleted = 0
        for i in 0..<input.count {
            if bulletsDeleted == bulletDifference { break }
            if input[i] != "•" {
                oldPassword.remove(at: i - bulletsDeleted)
                bulletsDeleted += 1
            }
        }

        //what remains of "oldPassword" is used to substitute bullets in "input" for appropriate characters to create "newPassword"
        //for example, if "oldPassword" is "AcbDE" and "input" is "•bc••", then "oldPassword" will get truncated to "ADE" and "newPassword" will equal "A" + "bc" + "DE", or "AbcDE"
        var i = 0
        for char in input {
            if char == "•" {
                newPassword.append(oldPassword[i])
                i += 1
            } else { newPassword.append(char) }
        }
        password = String(newPassword)

        //"passwordField.text" is then converted into a string of bullets equal to the length of the new password to ensure password security in the UI
        sender.text = String(repeating: "•", count: password.count)
    }
}

建设性的批评是值得赞赏的!


0
投票

回应@Gal Shahar答案。

iOS 12通过isSecureTextEntry属性识别密码textFields,而不仅仅是textContentType属性。

绕过自动填充建议的方式。

  1. isSecureTextEntry属性设置为false。

self.passwordTextField.secureTextEntry = NO;

  1. 添加UITextField委托方法并启用isSecureTextEntry属性。
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    if (textField == self.passwordTextField && !self.passwordTextField.secureTextEntry) {
        self.passwordTextField.secureTextEntry = YES;
    }

    return YES;
}

注意: - 不要使用shouldBeginEditing UITextField委托方法,它仍将显示自动填充建议。不要使用textFieldDidChange UITextField委托方法它将自动删除第一个charachter,因为它将在显示第一个charachter后发生。并且'secureTextEntry'将清空该字段。


0
投票

我认为将表单中的所有UITextField textContentType设置为UITextContentType("").oneTimeCode不是一个干净的解决方案。启用/禁用isSecureTextEntry仍然会给你同样的问题。

@Gal Shahar的答案很好,但仍然不完美。屏蔽的字符与Apple中安全输入文本中使用的屏蔽字符不同。它应该使用Unicode Character'BLACK CIRCLE'(U + 25CF)https://www.fileformat.info/info/unicode/char/25cf/index.htm

此外,它不处理光标移动。在中间插入文本时,它会将光标位置更改为文本的末尾。选择和替换文本时,它会给您错误的值。

当您决定使用自定义isSecureEntryText来避免自动填充密码时,这里是代码:

Swift 5(简易版)

@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(具有最后一个char动画的完整版本)

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
    }
}

-1
投票

这对我有用:

注意:尝试将此代码放在密码,密码确认(如果适用)和电子邮件文本字段上。我没有将它放在电子邮件文本字段中,它仍然弹出两个密码字段。

if #available(iOS 12, *) {
     // iOS 12: Not the best solution, but it works.
     cell.textField.textContentType = .oneTimeCode
} else {
     // iOS 11: Disables the autofill accessory view.
     cell.textField.textContentType = .init(rawValue: "")
}

10
投票

iOS 12似乎也通过isSecureTextEntry属性识别密码textFields,而不仅仅是textContentType属性,因此除非你将textContentType设置为空,否则删除secureEntry功能并导致应用程序出现安全漏洞,因此无法实现此配件视图的消失)然后阻止iOS 12将textField识别为密码textField并显示这个烦人的配件视图。

在我的情况下,配件导致了一个错误,当我点击时我的应用程序没有响应(这也使我的应用程序在应用程序审核过程中被拒绝)。所以我不得不删除此功能。我不想放弃这个安全功能所以我必须自己解决问题。

我们的想法是删除secureEntry功能,但手动添加它。它确实有效:

enter image description here


它可以这样做:

Swift 4路:

首先,在这里回答,将textContentType设置为空:

if #available(iOS 10.0, *) {
    passwordText.textContentType = UITextContentType("")
    emailText.textContentType = UITextContentType("")
}

然后,声明一个String变量,该变量稍后将包含我们的textField真实内容:

var passwordValue = ""

将目标添加到passwordTextField,每次textField内容更改时都会调用该目标:

passwordText.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)

现在,这将是魔术,声明将处理文本替换的函数:

@objc func textFieldDidChange(_ textField: UITextField) {
    if textField.text!.count > 1 {
        // User did copy & paste
        if passwordValue.count == 0 { // Pasted into an empty textField
            passwordValue = String(textField.text!)
        } else { // Pasted to a non empty textField
            passwordValue += textField.text!.substring(from: passwordValue.count)
        }
    } else {
        // User did input by keypad
        if textField.text!.count > passwordValue.count { // Added chars
            passwordValue += String(textField.text!.last!)
        } else if textField.text!.count < passwordValue.count { // Removed chars
            passwordValue = String(passwordValue.dropLast())
        }
    }
    self.passwordText.text = String(repeating: "•", count: self.passwordText.text!.count)
}

最后,将textField的autocorrectionType设置为.no以删除预测文本:

passwordText.autocorrectionType = .no

就是这样,使用passwordValue来执行您的登录。

希望它能帮助别人。

UPDATE

它也捕获粘贴的值,忘了之前添加它。


8
投票

可以通过指定既不是用户名也不是密码的内容类型来禁用该功能。例如,如果用户应输入电子邮件地址,则可以使用

usernameTextField?.textContentType = .emailAddress

8
投票

您可以像这样为UITextContentType添加扩展名

extension UITextContentType {
    public static let unspecified = UITextContentType("unspecified")
}

之后,你可以使用它

if #available(iOS 10.0, *) {
    passwordField.textContentType = .unspecified
}

6
投票

ios11中一个非常简单的方法为我工作。假设您的iboutlet是usernametextfield和passwordtextfield。在viewcontroller的viewDidLoad()函数中,保存两者都使用以下代码

usernametextfield.textContentType = UITextContentType("")
passwordtextfield.textContentType = UITextContentType("")

在此之后,当您点击文本字段时,您将看不到自动填充附件选项。


5
投票

Objective-C的

if (@available(iOS 10, *)){
    self.tfEmail.textContentType = @"";
    self.tfPassword.textContentType = @"";
}

这对我有用。


3
投票

默认情况下,为用户启用自动填充功能。 iOS会将所有密码保存在钥匙串中,并使其在应用程序的键盘中可用。 UITextViewUITextField自动考虑使用自动填充密码。您可以通过指定既不是用户名也不是密码的内容类型来禁用,但如果内容类型信息已经存储在钥匙串中,它将显示在快速栏中。所以最好分配空的UITextContentType类型,它不会显示快速栏。

例:

  if #available(iOS 10.0, *) {
  self.textField.textContentType = UITextContentType("")
  } else {
  // Fallback on earlier versions
 }

2
投票

Objective C版本:

if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10.0")) {
    self.passwordTextField.textContentType = @"";
    self.confirmPasswordTextField.textContentType = @"";
}

哪里

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

2
投票

这适用于ios 12和10:

if (@available(iOS 10, *)) {
    passwordTextField.textContentType = UITextContentTypeStreetAddressLine2;
}

if (@available(iOS 12, *)) {
    passwordTextField.textContentType = UITextContentTypeOneTimeCode;
}
© www.soinside.com 2019 - 2024. All rights reserved.