繁体   English   中英

如何检测用户何时在 UITextField 上使用密码自动填充

[英]How to detect when user used Password AutoFill on a UITextField

我已经实现了在 iOS 11 上支持密码自动填充所需的所有应用程序和服务器更改,并且运行良好。 我希望它工作得更好一点。

我的用户名和密码字段是 UITextFields。 我想确定用户何时“自动填充”了两个 UITextField 之一,因此我可以进行下一步。 目前用户自动填充一个项目,然后需要按屏幕键盘上的“下一步”按钮才能前进。 我想代表用户触发这个。

WWDC2017 密码自动填充会话说要使用 UITextFieldTextDidChange。 这可行,但当然,当用户在这些字段中手动输入时也会触发。

我的想法是将文本的先前版本与文本的新版本进行比较,并假设如果长度从零增加到大于某个最小长度(2 或更多),则用户使用了自动填充。 这应该在大多数情况下都有效,但有误触发的风险(可能在慢速设备上快速打字)。 所以对我来说,这可能是一个冒险的假设。

我很好奇是否有人找到了一种更可靠的方法来确定是否在 UITextField 上使用了密码自动填充,或者只是认为我对错误触发的担心是没有根据的。

不确定之前的答案是否在某个时候停止工作,但我无法让它工作 - 使用 AutoFill 时我只收到一个didBeginEditing调用。

但是,我确实找到了一种检测 AutoFill 的方法。 请记住,可以在输入某些字符后使用自动填充,例如,如果用户已经在电话号码中输入了一些数字,那么他们会自动填充完整的号码。

对于 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——在我的例子中,用户点击“登录”。

显然,您必须设置要监视的 UITextFields 的正确委派,但是一旦这样做:

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。

斯威夫特 5:

每次用户在文本字段中键入内容时,都会触发以下委托方法。 该字符串计数通常为 1,因此任何大于此的数字都是自动填充或用户将一些文本粘贴到字段中。 我只在这个密码字段上使用这个委托。

extension LoginView: UITextFieldDelegate {

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if string.count > 1 {
            DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) { [weak self] in
                self?.endEditing(true)
            }
        }
        return true
    }
}

我找到了另一种简单的方法..希望它会对正在寻找它的人有所帮助。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM