到目前为止,我想退出iOS 11提供的新选项,即在应用程序中建议输入密码。当我在iOS 11上运行该应用程序时,我在键盘上方获得了自动填充选项,并且我的用户名和密码文本字段甚至都没有显示。
因此,我的问题是,如何才能一起禁用新的密码自动填充功能,以使键盘上的键完全不显示并且总体行为与iOS 11之前的版本相同?
Answers:
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
属性。否则它将不起作用,并且仍将显示自动填充附件视图。
UITextContentType
的名称newPassword
(请参阅:developer.apple.com/documentation/uikit/uitextcontenttype/…)。尝试将密码文本字段的内容类型设置为此类型。我猜想iOS不会再显示自动填充附件视图,因为用户被迫设置一个新密码,而不能使用/访问之前通过使用自动填充附件视图设置的密码。现在这仅仅是理论。我不确定这是否行得通,但请让我知道是否行得通!
UITextContentType
为newPassword
不起作用。我尝试了@GalShahar的解决方案,但我不喜欢它。相反,我将设置UITextContentType
为oneTimeCode
。现在就可以了。
isSecureTextEntry
除了设置UITextContentType
为之外,我还必须先设置为false oneTimeCode
。isSecureTextEntry
一旦他们开始输入文本字段,我们就可以设置为我们想要的任何内容。
.isSecureTextEntry
到true
设定前textContentType
向.oneTimeCode
和它的作品如预期。我不知道您的代码看起来如何,但是在初始化文本字段时,设置其属性的顺序并不重要。
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
执行登录。
希望对别人有帮助。
更新
它也捕获粘贴的值,忘记了之前添加它。
可以通过指定既不是用户名也不是密码的内容类型来禁用该功能。例如,如果用户应输入电子邮件地址,则可以使用
usernameTextField?.textContentType = .emailAddress
您可以像这样为UITextContentType添加扩展名
extension UITextContentType {
public static let unspecified = UITextContentType("unspecified")
}
之后,您可以使用它
if #available(iOS 10.0, *) {
passwordField.textContentType = .unspecified
}
回应@Gal Shahar答案。
iOS 12isSecureTextEntry
不仅可以按textContentType
属性识别密码textField ,还可以按属性识别。
绕过自动填充建议的方法。
isSecureTextEntry
属性设置为false。self.passwordTextField.secureTextEntry = NO;
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”将清空该字段。
目标C
if (@available(iOS 10, *)){
self.tfEmail.textContentType = @"";
self.tfPassword.textContentType = @"";
}
这对我有用。
默认情况下,为用户启用了自动填充功能。iOS将所有密码保存在钥匙串中,并使其在应用程序的键盘中可用。UITextView
并UITextField
自动考虑使用自动填充密码。您可以通过指定既不是用户名也不是密码的内容类型来禁用,但是如果该内容类型信息已存储在钥匙串中,它将显示在快速栏中。因此最好分配空UITextContentType
类型,它不会显示快捷栏。
例:
if #available(iOS 10.0, *) {
self.textField.textContentType = UITextContentType("")
} else {
// Fallback on earlier versions
}
您可以通过textContentType
为密码文本字段分配虚拟对象来“关闭”用户名/密码组合检测。
passwordFormField.textContentType = UITextContentType("dummy")
这将关闭密码字段和其前面的电子邮件字段的键符号,因此您无需使用预定义的值之一,并且可以避免在键盘附件视图中显示不相关的建议。
newPassword
UITextContentType标记了此密码字段 。我不知道如何避免这种新行为。
目标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)
据我所知,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)
}
}
赞赏建设性的批评!
在这个主题上发生的疯狂工作人员。我在没有iOS的情况下向我提出密码,但只给我自动填充了电子邮件。就算有人需要它。经过不同的组合和不同的类型后,textContentType
我便按需完成了。
并使用此代码可以正常工作。不管您有没有email
,username
它都会使您根据自己的需求提出建议。因此,我禁用了附件自动填充视图,只在键盘的工具栏中保留了自动填充功能。
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
}
self.passwordTextField.autocorrectionType = NO;
它似乎不起作用,钥匙串标志仍然存在,
self.passwordTextField.textContentType = UITextContentTypeName;
上面的代码确实有效,但是如果用户设置了他们的Apple ID帐户,则将在键盘上显示Apple ID的名称,您无法通过将autocorrectionType设置为No来禁用它,不确定苹果是否仍在完善此自动填充功能,此刻的越野车相当多。
这对我有用:
注意:尝试将此代码放在密码,密码确认(如果适用)和电子邮件文本字段上。我没有将其放在电子邮件文本字段中,并且仍在弹出两个密码字段。
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: "")
}
我认为将所有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
}
}
感谢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;
}
textContentType
属性设置为.textContentType
--这应该告诉iOS 11您的字段不是用户名/密码字段(即使它们是),并阻止显示附件视图;诸如“ self.passwordField.textContentType = .textContentType`之类的东西