我已经实现了在 iOS 11 上支持密码自动填充所需的所有应用程序和服务器更改,并且效果良好。我希望它能更好一点。
我的用户名和密码字段是 UITextFields。我想确定用户何时“自动填充”两个 UITextField 之一,以便我可以继续下一步。目前,用户自动填充一个项目,然后需要按屏幕键盘上的
Next
按钮才能前进。我想代表用户触发此操作。
WWDC2017 密码自动填充会话要求使用 UITextFieldTextDidChange。这是可行的,但当然,当用户在这些字段中手动输入时也会触发。
我的想法是将文本的先前版本与新版本进行比较,并假设如果长度从零增加到大于某个最小长度(2 或更多),则用户使用自动填充。这应该在大多数情况下都有效,但存在错误触发的风险(可能在慢速设备上快速打字)。所以对我来说,这可能是一个冒险的假设。
我很好奇是否有人找到了一种更可靠的方法来确定是否在 UITextField 上使用了密码自动填充,或者只是认为我对错误触发的担心是没有根据的。
不确定之前的答案是否在某个时候停止工作,但我无法让它工作——使用自动填充时我只收到一个
didBeginEditing
调用。
但是,我确实找到了一种检测自动填充的方法。请记住,在输入某些字符后可以使用自动填充,例如,如果用户已经在电话号码中输入了一些数字,那么他们会自动填充完整的号码。
对于 Swift 4/5,将以下内容添加到 UITextField 的委托中:
private var fieldPossibleAutofillReplacementAt: Date?
private var fieldPossibleAutofillReplacementRange: NSRange?
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// To detect AutoFill, look for two quick replacements. The first replaces a range with a single space
// (or blank string starting with iOS 13.4).
// The next replaces the same range with the autofilled content.
if string == " " || string == "" {
self.fieldPossibleAutofillReplacementRange = range
self.fieldPossibleAutofillReplacementAt = Date()
} else {
if fieldPossibleAutofillReplacementRange == range, let replacedAt = self.fieldPossibleAutofillReplacementAt, Date().timeIntervalSince(replacedAt) < 0.1 {
DispatchQueue.main.async {
// Whatever you use to move forward.
self.moveForward()
}
}
self.fieldPossibleAutofillReplacementRange = nil
self.fieldPossibleAutofillReplacementAt = nil
}
return true
}
找到解决方案。
当密码管理器用于自动填充用户名+密码时,它将触发
didBeginEditing
两次,比人类更快。
因此,我计算了事件之间的时间。如果时间非常快,那么我假设自动填充(例如 FaceID 或 TouchID)用于输入凭据并自动触发接下来的任何 UI - 在我的例子中,用户点击“登录”。
显然,您必须设置要监视的 UITextField 的正确委托,但一旦这样做:
var biometricAutofillTime: Date!
func textFieldDidBeginEditing(_ textField: UITextField) {
if biometricAutofillTime != nil {
if Date().timeIntervalSince(biometricAutofillTime) < 0.1 {
// NOTE: Need to hesitate for a very short amount of time,
// because, otherwise, the second UITextField (password)
// won't yet be populated
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { self.didTapSignin() }
}
biometricAutofillTime = nil
}
biometricAutofillTime = Date()
}
这会检测用户何时通过密码自动填充。当用户从剪贴板粘贴文本时也可能触发它。 (如果文本字段为空)
您可以使用此链接处理删除用户粘贴案例的逻辑。 如何知道文本何时粘贴到 UITextView 中
private var didAutofillTextfield: Bool = false {
didSet {
if didAutofillTextfield {
// Fire analytics for user autofilling
}
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// If the range is {0,0} and the string count > 1, then user copy paste text or used password autofill.
didAutofillTextfield = range == NSRange(location: 0, length: 0) && string.count > 1
return true
}
我使用了这个委托方法:
func textFieldDidChangeSelection(_ textField: UITextField) {
// call when user select something
}
来自文档:
方法 textFieldDidChangeSelection(_:) 当指定文本字段中的文本选择发生变化时通知代理。te
我认为没有更好的解决方案。
我注意到的一件事是,仅当文本字段为空时才会启用自动填充。
因此,如果文本字段从空到长度大于最小密码/用户名,则很可能是自动填充/粘贴。
我正在使用
shouldChangeCharactersIn
来检测 UITextField 中的更改。我不确定是否存在在调用委托方法之前可以将键盘中的文本批量处理在一起的情况。
我想代表用户触发此操作。
如果这是您的主要目标,我在这里采取一些不同的方法。
显示登录表单后,我首先使用
SecRequestSharedWebCredential
检查 iCloud 钥匙串。如果关闭返回一个凭据,这意味着用户的意图是使用它登录,那么我会自动为他/她登录。否则,请将登录文本归档 becomeFirstResponder()
。
这种方法不支持第三方密码管理器,但我相信大多数人都使用iCloud Keychain。
只需添加以下扩展名:
import UIKit
extension UITextField {
var isAutoFilled: Bool {
!subviews.last!.isUserInteractionEnabled
}
}
我找到了另一种简单的方法..希望它能帮助正在寻找它的人。
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Usually string is coming as single character when user hit the keyboard ..
// but at AutoFill case it will come as whole string which recieved ..
// at this moment you can replace what you currently have by what you have recieved.
// In below case I'm expecting to recieve 4 digits as OTP code .. you can change it by what you are expecting to recieve.
if string.count == 4 {
doWhatEverYouByAutoFillString(text: string)
return true
}
}
}