【问题标题】:Accessibility identifier for UI subviews based on parent view id基于父视图 ID 的 UI 子视图的可访问性标识符
【发布时间】:2022-01-04 08:26:48
【问题描述】:

我有一个自定义视图,其中包含一些子视图,例如标签和文本字段。当我在一个控制器中使用多个自定义视图时,我想知道子视图可访问性标识符。我想要实现的是设置父标识符(parent_accessibility_identifier),而子视图标识符可以是它的扩展(例如parent_accessibility_identifier_labelparent_accessibility_identifier_text_field) .我们可以通过将父标识符可访问地设置为 false 并将标签和文本添加到子视图中来做到这一点,但是有没有更好的方法来做到这一点?此代码在子视图类中不起作用。

public override var accessibilityIdentifier: String? {
    didSet {
        if let accessibilityIdentifier = self.accessibilityIdentifier {
            self.accessibilityIdentifier = accessibilityIdentifier + "_label"
        }
    }
}

自定义视图类中的这项工作

public override var accessibilityIdentifier: String? {
    didSet {
        guard let accessibilityIdentifier = accessibilityIdentifier else { return }
        
        textLabel.accessibilityIdentifier = accessibilityIdentifier + "_text_label"
    }
}

【问题讨论】:

    标签: ios swift uiaccessibility


    【解决方案1】:

    我建议为此使用 swizzling。它使您能够覆盖系统级框架的打开属性和函数的行为。

    1. 首先,将 swizzling 代码放在某处:

       ///
       extension UIView {
      
           /**
            */
           class func swizzle() {
               let orginalSelectors = [
                   #selector(getter: accessibilityIdentifier),
               ]
               let swizzledSelectors = [
                   #selector(getter: swizzledAccessibilityIdentifier)
               ]
      
               let orginalMethods = orginalSelectors.map { class_getInstanceMethod(UIView.self, $0) }
               let swizzledMethods = swizzledSelectors.map { class_getInstanceMethod(UIView.self, $0) }
      
               orginalSelectors.enumerated().forEach { item in
                   let didAddMethod = class_addMethod(
                       UIView.self,
                       item.element,
                       method_getImplementation(swizzledMethods[item.offset]!),
                       method_getTypeEncoding(swizzledMethods[item.offset]!))
      
                   if didAddMethod {
                       class_replaceMethod(
                           UIView.self,
                           swizzledSelectors[item.offset],
                           method_getImplementation(orginalMethods[item.offset]!),
                           method_getTypeEncoding(orginalMethods[item.offset]!))
                   } else {
                       method_exchangeImplementations(orginalMethods[item.offset]!, swizzledMethods[item.offset]!)
                   }
               }
           }
      
           /// that's where you override `accessibilityIdentifier` getter for 
           @objc var swizzledAccessibilityIdentifier: String {
               if let parentIdentifier = superview?.accessibilityIdentifier {
                   return "\(parentIdentifier)_\(self.swizzledAccessibilityIdentifier)"
               } else {
                   return self.swizzledAccessibilityIdentifier
               }
           }
       }
      
    2. 致电UIView.swizzle()。通常,您在应用程序启动时执行此操作。在AppDelegate 或类似的地方。

    3. 设置视图层次结构、分配标识符和测试:

       class ParentView: UIView {}
       class SubclassedChildView: UIView {}
      
       let parentView = ParentView()
       let child1 = SubclassedChildView()
       let child2 = UIView()
      
       parentView.accessibilityIdentifier = "parent"
       child1.accessibilityIdentifier = "child1"
       child2.accessibilityIdentifier = "child2"
      
       parentView.addSubview(child1)
       child1.addSubview(child2)
      
       print(parentView.accessibilityIdentifier) // "parent"
       print(child1.accessibilityIdentifier) // "parent_child1"
       print(child2.accessibilityIdentifier) // "parent_child1_child2"
      

    【讨论】:

    • 感谢您的回答,我的印象是使用公共 API 可以做一些事情,而不是调整框架。