iOS 11禁用密码自动填充附件视图选项?


81

到目前为止,我想退出iOS 11提供的新选项,即在应用程序中建议输入密码。当我在iOS 11上运行该应用程序时,我在键盘上方获得了自动填充选项,并且我的用户名和密码文本字段甚至都没有显示。

因此,我的问题是,如何才能一起禁用新的密码自动填充功能,以使键盘上的键完全不显示并且总体行为与iOS 11之前的版本相同?

在此处输入图片说明


尝试将用户名和密码字段的 textContentType属性设置为.textContentType--这应该告诉iOS 11您的字段不是用户名/密码字段(即使它们是),并阻止显示附件视图;诸如“ self.passwordField.textContentType = .textContentType`之类的东西
Paulw11,2008年

@zumzum你有没有解决的办法。请分享
Nazmul Hasan

2
这对我来说也是一个问题。自动填写功能会显示在注册字段中,这是不希望的。我希望他们只是登录领域。奇怪的东西。
布拉德·根

2
看起来在iOS12上再次出现。您是如何解决的?
dollar2048 '18

在iOS12中,它仍然出现。你有什么主意吗 ?
EmrahAkgül'18年

Answers:


78

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

if #available(iOS 12, *) {
    // iOS 12 & 13: 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
它的问世形式的iOS 10,而不是iOS的11
MATM

@GalShahar我现在无法对其进行测试,但这是我的建议:iOS 12引入了一个新UITextContentType的名称newPassword (请参阅:developer.apple.com/documentation/uikit/uitextcontenttype/…)。尝试将密码文本字段的内容类型设置为此类型。我猜想iOS不会再显示自动填充附件视图,因为用户被迫设置一个新密码,而不能使用/访问之前通过使用自动填充附件视图设置的密码。现在这仅仅是理论。我不确定这是否行得通,但请让我知道是否行得通!
Baran Emre

好的,忘记了我写的关于iOS 12的内容。设置UITextContentType newPassword不起作用。我尝试了@GalShahar的解决方案,但我不喜欢它。相反,我将设置UITextContentTypeoneTimeCode 。现在就可以了。
巴兰·埃姆雷

对于iOS 12,isSecureTextEntry除了设置UITextContentType为之外,我还必须先设置为false oneTimeCodeisSecureTextEntry一旦他们开始输入文本字段,我们就可以设置为我们想要的任何内容。
da1

@ DA1我们设定.isSecureTextEntrytrue设定前textContentType.oneTimeCode和它的作品如预期。我不知道您的代码看起来如何,但是在初始化文本字段时,设置其属性的顺序并不重要。
巴兰·埃姆雷

13

iOS 12似乎也可以通过isSecureTextEntry属性而不是仅通过textContentType属性来识别密码textFields ,因此,除非您都将textContentType设置为nothing,并删除secureEntry功能(并在应用程序中造成安全漏洞),否则实际上不可能使此附件视图消失。然后阻止iOS 12将textField识别为密码textField并显示此烦人的附件视图。

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

这个想法是删除secureEntry功能,但您自己手动添加它。它确实起作用了:

在此处输入图片说明


可以这样做:

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执行登录。

希望对别人有帮助。

更新

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


2
超!不错的黑客哥们!iOS让我们骇客,而不是提供更干净的方法来做最基本的事情,这是可耻的。这应该是公认的答案。
sandpat

9

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

usernameTextField?.textContentType = .emailAddress

1
有趣的是,我在密码字段上方有一个电子邮件地址字段,并且该电子邮件地址字段提供了自动完成功能。
布拉德·根

1
@BradRoot也更改您的密码字段textContentype!如果您的电子邮件已保存在钥匙串中,则它将在快捷栏中显示信息,因此最好使用.nickname或尝试使用以下代码,如果#available(iOS 10.0,*){self.passwordTextField.textContentType = UITextContentType.init(rawValue:“ “)} else {//早期版本的回退}
Sahil

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("")

此后,当您点击文本框时,您将不会看到自动填充附件选项。


6

回应@Gal Shahar答案。

iOS 12isSecureTextEntry不仅可以按textContentType属性识别密码textField ,还可以按属性识别。

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

  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”将清空该字段。


1
这不仅解决了所讨论的问题,而且还解决了最初点击密码字段时从UIKeyboardWillShowNotification读取键盘高度错误的问题。好答案。
ekashking


3

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

例:

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

3

这适用于ios 12和10:

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

if (@available(iOS 12, *)) {
    passwordTextField.textContentType = UITextContentTypeOneTimeCode;
}

2

您可以通过textContentType为密码文本字段分配虚拟对象来“关闭”用户名/密码组合检测。

passwordFormField.textContentType = UITextContentType("dummy")

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


这仍然适用于您最新的iOS 12 beta吗?
pfandrade

不,不是的。在iOS 12上,它将此字段检测为新密码字段,并建议一个新的强密码(iOS 12的新功能)。我实际上需要这种行为,因此我用新的iOS 12 newPasswordUITextContentType标记了此密码字段 。我不知道如何避免这种新行为。
米格尔Cabeça

对于iOS 12 Beta,如果使用:textContentType = UITextContentType.oneTimeCode。然后,它的键盘上没有钥匙串。它对我来说似乎很好,没有任何副作用。如果使用.newPassword,它也可以使用,但是一旦输入密码,它会要求您保存密码。
丹尼尔·琼斯

2

目标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)

1

据我所知,Bem的答案在iOS 12中不起作用,Gal Shahar的答案无法解决某些极端情况(例如,如果用户一次删除多个字符)。我使用IBAction解决了此问题,因此完全不必检查iOS版本。我只是一个初学者,所以这可能不是“最佳”答案或最有效的答案,但对我而言最有意义:

首先,在情节提要中取消选中“安全文本输入”,或通过密码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)
    }
}

赞赏建设性的批评!


1

您可以在此处尝试不同的答案,得出可以删除附件视图的结论。但这留下了一些错误。

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

编辑:在苹果论坛上对同一问题仍然没有答案。我在官方UITextField文件中也找不到任何关于这个的信息


1

在这个主题上发生的疯狂工作人员。我在没有iOS的情况下向我提出密码,但只给我自动填充了电子邮件。就算有人需要它。经过不同的组合和不同的类型后,textContentType我便按需完成了。

  1. 建议使用iOS自动填充功能的“电子邮件文本”字段。
  2. 没有iOS系统的密码文本字段向我建议一个新密码,但是如果我从电子邮件文本字段中选择密码,则用密码填充。
  3. 密码文本字段包含安全的输入文本。

并使用此代码可以正常工作。不管您有没有emailusername它都会使您根据自己的需求提出建议。因此,我禁用了附件自动填充视图,只在键盘的工具栏中保留了自动填充功能。

self.passwordField.isSecureTextEntry = true

if #available(iOS 11.0, *) {
    self.emailField.textContentType = .username
    self.emailField.keyboardType = .emailAddress
}

if #available(iOS 12.0, *) {
    self.passwordField.textContentType = .password
    self.passwordField.keyboardType = .default
}

0
self.passwordTextField.autocorrectionType = NO;

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

self.passwordTextField.textContentType = UITextContentTypeName;

上面的代码确实有效,但是如果用户设置了他们的Apple ID帐户,则将在键盘上显示Apple ID的名称,您无法通过将autocorrectionType设置为No来禁用它,不确定苹果是否仍在完善此自动填充功能,此刻的越野车相当多。


这种类型的“答案”应保留给注释部分,因为它没有给出实际的答案,也没有链接到Apple等任何参考文档的链接。欢迎来到SO!
stktrc

尚无解决方案。这里没有列出对我有用的东西。
zumzum

0

这对我有用:

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

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: "")
}

1
这与Bem的答案相同。
Eric Aya

0

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

@Gal Shahar的答案很好,但仍然不够完美。掩码的字符与来自Apple的安全输入文本中使用的掩码的字符不同。它应该使用Unicode字符'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
    }
}

0

在此处输入图片说明

我已经附上了屏幕截图。您可以在情节提要中将内容类型更改为用户名,也可以通过编程方式进行。您可以为UITextfield创建扩展。

func diableAutofill() {
        self.autocorrectionType = .no
        if #available(iOS 11.0, *) {
            self.textContentType = .username
        } else {
            self.textContentType = .init("")
        }
    }

0

感谢Apple,当isSecureTextEntry设置为YES时,本机方法找不到任何方法。Gal Shahar的方法是禁用密码自动填充附件视图选项的唯一解决方案。但这更容易

目标c

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

迅速

textField(_:shouldChangeCharactersIn:replacementString:)

代表。并使用这样的简单代码。声明一个String变量,该变量以后将包含我们的textField实际内容,我的是pswd。

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
    {
        //Thanks iOS13!!!
        if(!_pswd)
        {
            _pswd = @"";
        }
        _pswd = [_pswd stringByReplacingCharactersInRange:range withString:string];

        if (!buttonShowPassword.selected)
        {
            textField.text = [@"" stringByPaddingToLength:_pswd.length withString: @"•" startingAtIndex:0];
        }
        else
        {
            textField.text = _pswd;
        }
        return NO;
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.