有没有办法找到 XCUIElement 是否有焦点?

Is there a way to find if the XCUIElement has focus or not?

我的一个屏幕有多个文本字段,我可以从不同的其他屏幕登陆到这个屏幕。在每种情况下,我都会将一个或另一个文本字段作为第一响应者。我无法编写测试来确定所需的文本字段是否具有焦点。

当我在控制台中打印文本字段时 -

Output: { TextField 0x131171d40: traits: 146031247360, Focused, {{6.0, 108.3}, {402.0, 35.0}}, value: ​ }

但是我在 XCUIElement 上找不到任何 focus/isFocus 属性。

有办法实现吗?

我不知道,但也许 XCUIElement 上的 selected 属性 就是您要找的。试试吧。

NSObject 上有一个通过 extension 的方法(来自 UIAccessibility.h),它在 XCUIElement 上可用,名为 accessibilityElementIsFocused(),但似乎不 return true 即使该元素的 debugDescription 明确表示 Focused。还有一些其他相关方法 accessibilityElementDidLoseFocus()accessibilityElementDidBecomeFocused(),它们似乎是旨在被 NSObject 的子类覆盖的方法,以获取 Focused 状态更改的通知.

经过这次挖掘,我倾向于说我们正在讨论的 Focused 的概念与 firstResponder 状态不同,这可能是您希望知道的.我相信在这一点上,根据对这些方法的一些评论,焦点表明该元素是某些辅助技术的焦点,例如 VoiceOver

如果您希望能够直接判断一个项目是否 firstResponder,我认为此时是提交错误报告的时候了。

如果有一种方法可以直接与软件键盘交互,一个棘手的解决方法可能是为您的 UITextField 获取一个 XCUIElement 并比较您的 [=] 的 value 27=] 元素在使用键盘输入之前和之后(您可能想先输入一个字符,然后输入一个退格键)。话虽如此,我草草想不出如何直接获取键盘。

我观察到一些情况

XCUIApplication().textFields[{index/identifier}].selected

不 return 正确,即使我将光标聚焦在文本字段上但我能够使用 .typeText() 输入文本。但是,.enabled 正确地 return 是 true ,看起来 'enabled' 值表示 UI 元素启用了辅助功能,您可以与之交互。

不是一个干净的解决方案,但您可以尝试,

XCUIApplication().textFields.elementMatchingPredicate("predicate")

.elementMatchingType() 获取 XCUIElement 并编写测试块,同时使用 guard 语句处理异常情况,如果未找到 textField 则抛出适当的错误。

我参加聚会有点晚了 :) 但是据我转储变量 XCUIElement 所见,它有一个有趣的 属性:

property name: hasKeyboardFocus

property type: TB,R

因此您可以通过以下方式检查您的元素是否具有焦点:

let hasFocus = (yourTextField.value(forKey: "hasKeyboardFocus") as? Bool) ?? false

注意:您可以转储具有以下扩展名的任何 NSObject 子类的 属性 变量:

extension NSObject {
    func dumpProperties() {
        var outCount: UInt32 = 0

        let properties = class_copyPropertyList(self.dynamicType, &outCount)
        for index in 0...outCount {
            let property = properties[Int(index)]
            if nil == property {
                continue
            }
            if let propertyName = String.fromCString(property_getName(property)) {
                print("property name: \(propertyName)")
            }
            if let propertyType = String.fromCString(property_getAttributes(property)) {
                print("property type: \(propertyType)")
            }
        }
    }
}

更新:属性转储,Swift 4:*

extension NSObject {
    func dumpProperties() {
        var outCount: UInt32 = 0

        let properties = class_copyPropertyList(type(of: self), &outCount)
        for index in 0...outCount {
            guard let property = properties?[Int(index)] else {
                continue
            }
            let propertyName = String(cString: property_getName(property))
            print("property name: \(propertyName)")
            guard let propertyAttributes = property_getAttributes(property) else {
                continue
            }
            let propertyType = String(cString: propertyAttributes)
            print("property type: \(propertyType)")
        }
    }
}

基于 ,我整理了这个小扩展(也适用于 Swift 4)...

extension XCUIElement
{
    func hasFocus() -> Bool {
        let hasKeyboardFocus = (self.value(forKey: "hasKeyboardFocus") as? Bool) ?? false
        return hasKeyboardFocus
    }
}