23

我们使用 KIF 进行功能测试,它使用元素的可访问性标签来确定将事件发送到何处。我目前正在尝试测试 UISegmentedControl 的行为,但为此我需要为控件的不同段设置不同的可访问性标签。如何为特定细分设置可访问性标签?

4

13 回答 13

19

正如顶点所说,

对象-c

[[[self.segmentOutlet subviews] objectAtIndex:3] setAccessibilityLabel:@"GENERAL_SEGMENT"];

迅速

self.segmentOutlet.subviews[3].accessibilityLabel = "GENERAL_SEGMENT"

一些建议,这样你就不会像我一样发疯:

  1. 要在辅助功能模式中滚动,请滑动三个手指
  2. 段的索引比您预期的要向后,即最右边的段是第 0 个索引,最左边的是第 n 个索引,其中 n 是 UISegmentControl 中的元素数
于 2013-03-29T21:07:30.030 回答
15

我自己刚刚开始使用 KIF,所以我还没有测试过这个,但它可能值得一试。我相信我很快就会遇到同样的问题,所以我很想知道它是否有效。

首先,UIAccessibility Protocol Reference在accessibilityLabel 下有一条说明:

“如果您提供 UIImage 对象以在 UISegmentedControl 中显示,则可以在每个图像上设置此属性以确保可以正确访问这些段。”

所以,我想知道您是否也可以在每个 NSString 对象上设置accessibilityLabel,并能够使用它来访问带有KIF 的每个段。首先,您可以尝试创建几个字符串,设置它们的可访问性标签,并使用 [[UISegmentedControl alloc] initWithItems:myStringArray]; 填充它。

请向我们更新您的进度。我想听听这是怎么回事

于 2012-01-12T21:38:09.383 回答
9

的每个段UISegmentedControl都是UISegment类实例,它是 的子类UIImageViewsubviews您可以通过属性访问这些实例,UISegmentedControl并尝试以编程方式为它们添加可访问性。

于 2012-01-12T21:43:48.017 回答
9

您不能依赖 subviewsarray 中的索引来获取位置。对于单个子视图的自定义,我在设置任何属性之前对子视图的 X 位置进行排序。对于 accesibilityLbel 也有效。

let sortedViews = self.subviews.sorted( by: { $0.frame.origin.x < $1.frame.origin.x } )

sortedViews[0].accessibilityLabel = "segment_full"
sortedViews[1].accessibilityLabel = "segment_not_full"
于 2018-09-20T08:59:31.623 回答
8

这是一个老问题,但以防万一其他人遇到这个问题,我发现这些段自动有一个可访问性标签指定为其文本。因此,如果添加了选项 1 和选项 2 的两个选项。调用

[tester tapViewWithAccessibilityLabel:@"Option 2"];

成功选择了细分。

于 2014-06-26T13:02:40.800 回答
6

使用索引子视图的解决方案不起作用,因为您不能依赖正确的顺序,并且很难更改段数。并且按原点排序不起作用,因为框架(至少对于当前版本)似乎总是在 x: 0。

我的解决方案:

(segmentedControl.accessibilityElement(at: 0) as? UIView)?.accessibilityLabel = "Custom VoiceOver Label 1"
(segmentedControl.accessibilityElement(at: 1) as? UIView)?.accessibilityLabel = "Custom VoiceOver Label 2"
(segmentedControl.accessibilityElement(at: 2) as? UIView)?.accessibilityLabel = "Custom VoiceOver Label 3"

似乎对我有用并且有正确的顺序。你也不依赖图像。也不是那么漂亮,但可能比其他解决方案更可靠。

于 2020-01-24T14:29:46.137 回答
4

这是一个老问题,但以防万一其他人遇到这个问题,我发现这些段自动有一个可访问性标签指定为其文本。

除了 Stuart 的回答,我发现在编写测试用例以在模拟器上打开“辅助功能检查器”(设置 - > 常规 - > 辅助功能 - > 辅助功能检查器)时非常有用。您会惊讶地发现有多少元素已经包含了可访问性标签,例如标准 iOS UI 元素甚至第三方框架。

带有可访问性标签的 Facebook 登录按钮

注意:手势现在会有所不同 - 点击查看辅助功能信息,双击选择。最小化辅助功能检查器窗口(通过点击 X 按钮)将使手势恢复正常。

于 2014-09-23T03:52:16.707 回答
2

你们想看看Apple建议如何完成吗?

太丑了。

这是来自这个例子

func configureCustomSegmentsSegmentedControl() {
    let imageToAccessibilityLabelMappings = [
        "checkmark_icon": NSLocalizedString("Done", comment: ""),
        "search_icon": NSLocalizedString("Search", comment: ""),
        "tools_icon": NSLocalizedString("Settings", comment: "")
    ]

    // Guarantee that the segments show up in the same order.
    var sortedSegmentImageNames = Array(imageToAccessibilityLabelMappings.keys)
    sortedSegmentImageNames.sort { lhs, rhs in
        return lhs.localizedStandardCompare(rhs) == ComparisonResult.orderedAscending
    }

    for (idx, segmentImageName) in sortedSegmentImageNames.enumerated() {
        let image = UIImage(named: segmentImageName)!

        image.accessibilityLabel = imageToAccessibilityLabelMappings[segmentImageName]

        customSegmentsSegmentedControl.setImage(image, forSegmentAt: idx)
    }

    customSegmentsSegmentedControl.selectedSegmentIndex = 0

    customSegmentsSegmentedControl.addTarget(self,
                                             action: #selector(SegmentedControlViewController.selectedSegmentDidChange(_:)),
                                             for: .valueChanged)
}

他们将可访问性标签应用于图像,然后附加图像。和上面的答案差别不大。

于 2018-12-19T01:15:41.123 回答
1

如果不愿意设置可访问性标签,另一种选择可能是计算每个段部分的位置并使用

[tester tapScreenAtPoint:segementPosition];

触发动作

于 2014-07-23T04:46:15.447 回答
1

如果您通过可访问性检查器查看分段控件,您会发现这些段是 UISegment 对象。此外,它们是 UISegmentedControl 的直接子视图。这一事实表明以下疯狂但非常安全的 Swift 4 代码可以设置 UISegmentedControl 段的可访问性标签:

    let seg = // the UISegmentedControl
    if let segclass = NSClassFromString("UISegment") {
        let segments = seg.subviews.filter {type(of:$0) == segclass}
            .sorted {$0.frame.minX < $1.frame.minX}
        let labels = ["Previous article", "Next article"] // or whatever
        for pair in zip(segments,labels) {
            pair.0.accessibilityLabel = pair.1
        }
    }
于 2018-10-04T15:09:59.897 回答
1

如已接受的答案中所述,添加accessibilityLabel到文本中应该可以解决问题:

let title0 = "Button1" as NSString
title0.accessibilityLabel = "MyButtonIdentifier1"
segmentedControl.setTitle("\(title0)", forSegmentAt: 0)
            
let title1 ="Button2" as NSString
title1.accessibilityLabel = "MyButtonIdentifier2"
segmentedControl.setTitle("\(title1)", forSegmentAt: 1)
于 2021-02-02T11:48:20.933 回答
0

XCode 12 / iOS 14.3 / 斯威夫特 5

这是一篇旧文章,但我在尝试为 UISegmentedControl 中的各个段设置可访问性提示时遇到了同样的问题。我对一些较旧的解决方案也有问题。目前为我的应用程序工作的代码借鉴了来自 matt 和 Ilker Baltaci 的回复,然后使用 UIView.description 混合到我自己的 hack 中。

首先,一些评论:

  • 对于我的具有 3 个段的 UISegmentedControl,父 UIVIewController 的 viewDidLoad() 和 viewWillAppear() 中的子视图计数为 3。但 viewDidAppear() 中的子视图计数为 7。
  • 在 viewDidLoad() 或 viewWillAppear() 中未设置子视图框架,因此对子视图进行排序对我不起作用。显然 Benjamin B 在帧起源方面遇到了同样的问题。
  • 在 viewDidAppear() 中,7 个子视图包括 4 个UIImageView类型的视图和 3 个UISegment类型的视图。
  • UISegment 是私有类型。直接使用私有 API可能会将您的应用标记为被拒绝。(见下面的评论)
  • type(of:) 没有产生任何对 UISegment 子视图有用的东西
  • (HACK!)UIView.description可用于在不访问私有 API 的情况下检查类型。
  • 根据 X 顺序设置可访问性提示将 UI 段标题和提示与其当前位置紧密结合。如果用户测试建议更改段顺序,则必须在 UI 和代码中进行更改以设置可访问性提示。很容易错过这一点。

使用枚举设置段标题是依赖于在 UI 中手动设置的 X 排序的替代方法。如果您的枚举继承自 String 并采用 CaseIterable 和 RawRepresentable 协议,则可以直接从枚举案例创建标题,并根据段标题确定枚举案例。

鉴于对description.contains("UISegment")的依赖,不能保证以下内容将在框架的未来版本中工作,但它对我有用。得继续前进。

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

// get only the UISegment items; ignore UIImageView
let segments = mySegmentedControl.subviews.compactMap(
    { $0.description.contains("UISegment") ? $0 : nil }
)
let sortedSegments = segments.sorted(
    by: { $0.frame.origin.x < $1.frame.origin.x }
)

for i in 0 ..< sortedSegments.count {
    let segment = sortedSegments[i]
    // set .accessibilityHint or .accessibilityLabel by index
    // or check for a segment title matching an enum case
    // ...
}
}

关于私有 API 和拒绝

我指的是 2016 年 4 月 @dan 在Test if object is an instance of class UISegment 中的评论:

是私人课。您可以使用 [... isKindOfClass:NSClassFromString(@"UISegment")] 进行检查,但这可能会导致您的应用程序因使用私有 api 而被拒绝,或者如果苹果更改内部类名称或结构,将来可能会停止工作。

另外: 私有 API 到底是什么,如果使用了一个 iOS 应用程序,为什么 Apple 会拒绝它?

“应用因非公开 api 而被拒绝”:https ://discussions.apple.com/thread/3838251

于 2021-02-24T15:41:57.287 回答
-1

正如 Vortex 所说,数组从右到左,[0] 从右边开始。您可以通过访问子视图来设置每个可访问性选项。由于子视图是可选的,因此最好先拉出子视图,然后分配您想要的可访问性特征。一个简单的两个选项段控制的 Swift 4 示例:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    guard let rightSegment = segmentControl.subviews.first, let leftSegment = segmentControl.subviews.last else { return }
    rightSegment.accessibilityLabel = "A label for the right segment."
    rightSegment.accessibilityHint = "A hint for the right segment."
    leftSegment.accessibilityLabel = "A label for the left segment."
    leftSegment.accessibilityHint = "A hint for the left segment."
}
于 2018-08-23T12:54:42.657 回答