如下图所示,如果您键入日期"Jan 11 2023",则会显示日期 Select 器.我想实现的是在其他地方有一个按钮,当那个按钮被点击时,自动显示这个日期 Select 器.

有没有人知道是否有办法实现这一点?

DatePicker("Jump to", selection: $date, in: dateRange, displayedComponents: [.date]) 

enter image description here

以下是对@rob mayoff答案的测试.我还是不能 弄清楚为什么它还不起作用.

我在带有iOS 16.2模拟器的iPhone 14和Xcode14.2上进行了测试,也在设备上进行了测试.我注意到的是,虽然叫了triggerDatePickerPopover(),但永远也打不到button.accessibilityActivate().

import SwiftUI

struct ContentView: View {
  @State var date: Date = .now
  let dateRange: ClosedRange<Date> = Date(timeIntervalSinceNow: -864000) ... Date(timeIntervalSinceNow: 864000)

  var pickerId: String { "picker" }

  var body: some View {
    VStack {
      DatePicker(
        "Jump to",
        selection: $date,
        in: dateRange,
        displayedComponents: [.date]
      )
      .accessibilityIdentifier(pickerId)

      Button("Clicky") {
        triggerDatePickerPopover()
          print("Clicky Triggered")
      }
    }
    .padding()
  }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

extension NSObject {
  func accessibilityDescendant(passing test: (Any) -> Bool) -> Any? {

    if test(self) { return self }

    for child in accessibilityElements ?? [] {
      if test(child) { return child }
      if let child = child as? NSObject, let answer = child.accessibilityDescendant(passing: test) {
        return answer
      }
    }

    for subview in (self as? UIView)?.subviews ?? [] {
      if test(subview) { return subview }
      if let answer = subview.accessibilityDescendant(passing: test) {
        return answer
      }
    }

    return nil
  }
}


extension NSObject {
  func accessibilityDescendant(identifiedAs id: String) -> Any? {
    return accessibilityDescendant {
      // For reasons unknown, I cannot cast a UIView to a UIAccessibilityIdentification at runtime.
      return ($0 as? UIView)?.accessibilityIdentifier == id
      || ($0 as? UIAccessibilityIdentification)?.accessibilityIdentifier == id
    }
  }
    
    func buttonAccessibilityDescendant() -> Any? {
       return accessibilityDescendant { ($0 as? NSObject)?.accessibilityTraits == .button }
     }
}

extension ContentView {
  func triggerDatePickerPopover() {
    if
      let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
      let window = scene.windows.first,
      let picker = window.accessibilityDescendant(identifiedAs: pickerId) as? NSObject,
      let button = picker.buttonAccessibilityDescendant() as? NSObject
    {
        print("triggerDatePickerPopover")
      button.accessibilityActivate()
    }
  }
}

enter image description here

Update 2: I followed up the debug instruction. It seems that with exact same code. My inspector are missing the accessibility identifier. Not knowing why.... feels mind buggingly now. enter image description here

这里有一个下载项目https://www.icloud.com/iclouddrive/040jHC0jwwJg3xgAEvGZShqFg#DatePickerTest的链接

更新3: @罗布·梅奥夫的解决方案太棒了!对于任何阅读的人来说.如果它对你的情况不起作用,那就等着吧.这可能只是因为设备或模拟器为可访问性做好了准备.

推荐答案

SwiftUI不提供以编程方式触发日历弹出窗口的直接方法.

但是,我们可以使用可访问性API来实现这一点.下面是我的测试结果:

screen capture of iPhone simulator showing that the date picker popover opens from clicks on either a button or the date picker

你可以看到日历弹出窗口是通过点击"点击"按钮或者点击日期 Select 器来打开的.

首先,我们需要一种使用可访问性API查找选取器的方法.让我们为选取器分配一个可访问性标识符:

struct ContentView: View {
  @State var date: Date = .now
  let dateRange: ClosedRange<Date> = Date(timeIntervalSinceNow: -864000) ... Date(timeIntervalSinceNow: 864000)

  var pickerId: String { "picker" }

  var body: some View {
    VStack {
      DatePicker(
        "Jump to",
        selection: $date,
        in: dateRange,
        displayedComponents: [.date]
      )
      .accessibilityIdentifier(pickerId)

      Button("Clicky") {
        triggerDatePickerPopover()
      }
    }
    .padding()
  }
}

在编写triggerDatePickerPopover之前,我们需要一个搜索可访问性元素树的函数:

extension NSObject {
  func accessibilityDescendant(passing test: (Any) -> Bool) -> Any? {

    if test(self) { return self }

    for child in accessibilityElements ?? [] {
      if test(child) { return child }
      if let child = child as? NSObject, let answer = child.accessibilityDescendant(passing: test) {
        return answer
      }
    }

    for subview in (self as? UIView)?.subviews ?? [] {
      if test(subview) { return subview }
      if let answer = subview.accessibilityDescendant(passing: test) {
        return answer
      }
    }

    return nil
  }
}

让我们使用它来编写一个搜索具有特定id的元素的方法:

extension NSObject {
  func accessibilityDescendant(identifiedAs id: String) -> Any? {
    return accessibilityDescendant {
      // For reasons unknown, I cannot cast a UIView to a UIAccessibilityIdentification at runtime.
      return ($0 as? UIView)?.accessibilityIdentifier == id
      || ($0 as? UIAccessibilityIdentification)?.accessibilityIdentifier == id
    }
  }
}

在测试中,我发现,尽管文档中的UIView符合UIAccessibilityIdentification协议(该协议定义了accessibilityIdentifier属性),但将UIView转换为UIAccessibilityIdentification在运行时不起作用.因此,上面的方法比您可能预期的要复杂一些.

原来,选取器有一个充当按钮的子元素,该按钮是我们需要激活的.因此,让我们编写一个也搜索按钮元素的方法:

  func buttonAccessibilityDescendant() -> Any? {
    return accessibilityDescendant { ($0 as? NSObject)?.accessibilityTraits == .button }
  }

最后,我们可以编写triggerDatePickerPopover方法:

extension ContentView {
  func triggerDatePickerPopover() {
    if
      let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
      let window = scene.windows.first,
      let picker = window.accessibilityDescendant(identifiedAs: pickerId) as? NSObject,
      let button = picker.buttonAccessibilityDescendant() as? NSObject
    {
      button.accessibilityActivate()
    }
  }
}

更新

你说你对我的代码有问题.因为它对我有效,所以很难诊断问题.try 启动辅助功能判断器(从Xcode的菜单栏中, Select Xcode&>;打开开发工具&>辅助功能判断器).告诉它以模拟器为目标,然后使用右尖括号按钮遍历可访问性元素,直到您到达DatePicker.然后将鼠标悬停在判断员的层次 struct 窗格中的行上,并单击圈中的右箭头.它应该是这样的:

accessibility inspector and simulator side-by-side with the date picker selected in the inspector

注意,判断器看到的是在代码中设置的日期选取器的标识符"Picker",并且选取器在层次 struct 中有一个按钮子级.如果您的代码看起来不同,您需要找出原因并更改代码以与之匹配.

单步执行accessibilityDescendant方法并手动转储子元素(例如po accessibilityElementspo (self as? UIView)?.subviews)也可能有所帮助.

Ios相关问答推荐

Swift:从字符串目录获取带有参数的本地化字符串

带有故事板的Xcode 15 UIKit视图可以与#Preview宏一起使用吗?

当虚拟对象附加到其网格时,如何刷新创建 ARView 的 SwiftUI UIViewRepresentable?

更改初始控制器无效

不可发送类型 '[String : Any]?'在调用非隔离实例方法 XXX 时退出主参与者隔离上下文不能跨越参与者边界

在 SwiftUI 中使用 .rotation3DEffect 在视图之间翻转

小部件链接有效,但不执行任何操作

UIViewRepresentable - MKMapView 没有得到更新

有哪些可靠的机制可以防止 CoreData CloudKit 中的数据重复?

如何在 SwiftUI 的列表中使用带有 swipeActions 的 NavigationLink

在使用 Github Actions 时,我面临权限被拒绝错误

如何让我的 iOS 应用程序与 api 连接?

Select UITableViewCell 时 UIView backgroundColor 消失

光标未显示在 UITextView 中

检测 iPhone/iPad/iPod touch 的 colored颜色 ?

在 iOS 编程中使用 Storyboard 代替 xib 文件有什么好处?

UITableView 页脚,停止浮动内容

在 Swift 中上传带参数的图像

有什么方法可以加粗 NSString 的一部分?

我可以通过 UIAppearance 代理设置哪些属性?