如何为UISegmentedControl的特定段设置辅助功能标签?

时间:2012-01-12 11:01:29

标签: ios accessibility uisegmentedcontrol kif-framework

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

13 个答案:

答案 0 :(得分:19)

正如Vertex所说,

<强> OBJ-C

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

<强>迅速

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

一些建议,所以你不要像我一样疯狂:

  1. 要在辅助功能模式下滚动,请滑动三根手指
  2. 段的索引比你预期的要靠后,即右边最远的段是第0个索引,左边最远的是第n个索引,其中n是UISegmentControl中元素的数量

答案 1 :(得分:10)

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

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

  

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

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

请向我们介绍您的进度。我想知道这是怎么回事

答案 2 :(得分:9)

UISegmentedControl的每个段都是UISegment类实例,它是UIImageView的子类。您可以通过subviews的{​​{1}}属性访问这些实例,并尝试以编程方式为其添加辅助功能。

答案 3 :(得分:8)

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

[tester tapViewWithAccessibilityLabel:@"Option 2"];

成功选择了细分受众群。

答案 4 :(得分:4)

  

这是一个老问题,但是为了防止其他人遇到这种情况,我发现这些网段会自动将可访问性标签指定为其文字。

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

Facebook log in button with accessibility label

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

答案 5 :(得分:4)

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

country_index = input('Country Code: ')

engine = create_engine('mysql+pymysql://user:pass@localhost:3306/{}'.format(country_index))
connection = engine.connect()


for lines in countryDict[country_index]:
    lines.splitlines()
    fredder = fred.get_series_all_releases(lines)


def Database_Table_Creator():

      for table in countryDict[country_index]:
          table.splitlines()

          queryCreateTable = """CREATE TABLE `{}`(
                                DATE int,
                                REALTIME_START int,
                                VALUE int
                                )""".format(table)

          connection.execute(queryCreateTable)

          fredder.to_sql(
                  name = '{}'.format(table),
                  con = connection,
                  index = False,
                  if_exists='append',
                  chunksize = 500)


      connection.close()


Database_Table_Creator()

答案 6 :(得分:4)

使用索引子视图的解决方案不起作用,因为您不能依赖正确的顺序,并且将很难更改段数。而且按原点排序不起作用,因为框架(至少对于当前版本而言)似乎始终为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"

似乎为我工作,并且命令正确。您也不依赖图像。也不是很漂亮,但可能比其他解决方案更可靠。

答案 7 :(得分:1)

如果不愿意设置辅助功能标签,则另一个选项可能是计算每个分段部分的位置并使用

[tester tapScreenAtPoint:segementPosition];

触发行动

答案 8 :(得分: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
        }
    }

答案 9 :(得分:1)

你们想看看苹果推荐如何做?

这很丑。

这来自this example

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

他们将辅助功能标签应用于图像,然后附加图像。与above answer没什么不同。

答案 10 :(得分:0)

如已接受的答案中所述,在文本中添加 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)

答案 11 :(得分:0)

XCode 12 / iOS 14.3 / Swift 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 和拒绝

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

<块引用>

这是一个私人课程。你可以用 [... isKindOfClass:NSClassFromString(@"UISegment")] 但这可能会让你 应用程序因使用私有 API 被拒绝或在将来停止工作,如果 苹果改变内部类名或结构。

还有: What exactly is a Private API, and why will Apple reject an iOS App if one is used?

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

答案 12 :(得分:-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."
}