SwiftUI VoiceOver 使某些视图无法呈现

SwiftUI VoiceOver making some views not be rendered

我正在构建一个应该与 VoiceOver 配合使用的 SwiftUI-App,但是当 VoiceOver 处于活动状态时,我的 UI 的部分不会在屏幕上呈现。它在我打开视图然后激活 VoiceOver 时起作用。它只是无法与 VoiceOver 一起正确加载。我试着做一个简单的例子来说明我所做的事情,但一切似乎都很好,所以我不知道该怎么做。

我有一棵元素树,我想在屏幕上为每个元素绘制一个表单。因此我使用 Rectangle/Circle/Path 和其他(但主要是路径)。每个元素都有一个创建与其关联的视图的功能,该功能还会自动创建子视图。有与绘制的元素关联的 Tap 和 DragGestures,所以这些很重要,如果它们没有在屏幕上呈现,那么洞的东西就不再起作用了。 实际系统要复杂得多,并且使用 TreeElements 的子类。我没能做一个简单的例子来重现这个问题。

所以我想问的是,是否有人知道任何可能对我的方法造成问题的事情,或者知道当 VoiceOver 处于活动状态时通常不起作用的事情?

这是显示我的代码总体思路的简化示例:

struct TestView: View {
    let tree: TreeElement = createTree()
    let gesture: DragGesture = DragGesture()
    
    var body: some View {
        tree.createView(with: gesture)
    }
}

// This function is only there to create a Tree for testing
func createTree() -> TreeElement {
    let tree = TreeElement()
    var counter: Int = 1
    for _ in stride(from: 0, to: 2, by: 1) {
        counter += 1
        let childTree = TreeElement()
        for _ in stride(from: 0, to: 2, by: 1) {
            counter += 1
            childTree.children.append(TreeElement())
        }
        tree.children.append(childTree)
    }
    print("There are \(counter) Tree Elements.")
    return tree
}

class TreeElement: ObservableObject, Identifiable {
    @Published public var children: [TreeElement] = []
    public var id: UUID = UUID()
    
    func createView(with gesture: DragGesture) -> AnyView {
        return AnyView(TreeView(tree: self, gesture: gesture))
    }
}

struct TreeView: View {
    @ObservedObject var tree: TreeElement
    var gesture: DragGesture
    
    var body: some View {
        ZStack {
            Rectangle()
                .onTapGesture(count: 2) { print("A Treeview was tapped") }
                .onAppear { print("A Treeview was created") }
            
            ForEach(tree.children) { child in
                child.createView(with: gesture)
            }
        }
    }
}

我发现了问题,它与使用的结构无关,而是与我打开视图的方式有关:

打开视图中树的变量设置为默认的空树,当点击按钮时,会计算实际的树。但这是在导航链接内的按钮操作中完成的。一旦 VoiceOver 处于活动状态,仅使用 NavigationLink 进入应显示树的视图,因此未创建树。

我解决此问题的方法是向 NavigationLink 添加辅助功能操作,以确保在打开 link 之前完成所需的计算。

struct OpenerView: View {
    var file: String
    
    @State var showTreeView: Bool = false
    @State var myTree: TreeRoot = TreeRoot(withParent: nil)
    
    var body: some View {
        NavigationLink(
            destination: ActualTreeView(show: $showTreeView, myTree: myTree)
                .navigationBarHidden(true)
                .accessibility(addTraits: [.allowsDirectInteraction]),
            isActive: $showTreeView,
            label: {
                Button {
                    let data: Data = TreeViewHelper.createDataFromAsset(asset: file)
                    myTree = TreeViewHelper.createUITreeFromData(data: data)
                    showTreeView.toggle()
                } label: {
                    MenuButtonLook(name: LocalizedStringKey(file))
                }
            }).accessibilityAction {
                let data: Data = TreeViewHelper.createDataFromAsset(asset: file)
                myTree = TreeViewHelper.createUITreeFromData(data: data)
                showTreeView.toggle()
            }
    }
}