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()
}
}
}
我正在构建一个应该与 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()
}
}
}