如何让 iOS VoiceOver 在文本元素(不是当前焦点)发生变化时读取它?

How to make iOS VoiceOver read a Text element (not current focus) when it changes?

我正在尝试制作一个模拟 Apple 计算器应用程序行为的应用程序。在计算器上,当您点击 =(等于)时,应用程序会从主显示视图中读取“结果,数字。”。如何在 SwiftUI 中重现此行为?

我的测试场景:

struct ContentView: View {
    
    @State var result = 0
    
    var body: some View {
        VStack {
            Text("\(result)")
                .font(.largeTitle)
                .frame(height: 100)
                .padding()
            Spacer()
            Button(action: {
                self.result += 1
            }, label: {
                Text("Add one")
                    .font(.title)
                    .padding()
            })
            Button(action: {
                self.result -= 1
            }, label: {
                Text("Minus one")
                    .font(.title)
                    .padding()
            })
        }
    }
}

我希望每次结果发生变化时,通过添加或减号按钮的交互,VoiceOver 系统检测到结果变化并读取结果。

您可以post Voice over 的通知来宣布一些事情。但是我不熟悉 SwiftUI,所以不确定在你的例子中最好把这段代码放在哪里

post通知的代码是:

if UIAccessibility.isVoiceOverRunning {
    UIAccessibility.post(notification: .announcement, argument: "message to announce")
}

也许你可以这样做:

function announceResult() {
    if UIAccessibility.isVoiceOverRunning {
        UIAccessibility.post(notification: .announcement, argument: self.result)
    }
}

Button(action: {
     self.result += 1
     announceResult()

}

您只需在视图主体上使用 accessibility(value:) 修饰符即可。

var body: some View {
    VStack {
        Text("\(result)")
            .font(.largeTitle)
            .frame(height: 100)
            .padding()
        Spacer()
        Button(action: {
            self.result += 1
        }, label: {
            Text("Add one")
                .font(.title)
                .padding()
        })
        Button(action: {
            self.result -= 1
        }, label: {
            Text("Minus one")
                .font(.title)
                .padding()
        })
    }
    .accessibility(value: Text("\(result)"))
}

这是我的最终解决方案:

struct ContentView: View {

@State var result = 0 {
    didSet {
        announce(this: "\(result)")
    }
}

var body: some View {
    VStack {
        Text("\(result)")
            .font(.largeTitle)
            .frame(height: 100)
            .padding()
        Spacer()
        Button(action: {
            self.result += 1
        }, label: {
            Text("Add one")
                .font(.title)
                .padding()
        })
        Button(action: {
            self.result -= 1
        }, label: {
            Text("Minus one")
                .font(.title)
                .padding()
        })
    }
    //.accessibility(value: Text("\(result)"))
}

private func announce(this: String) {
    if UIAccessibility.isVoiceOverRunning {
        UIAccessibility.post(notification: .screenChanged, argument: "The new result is \(this).")
    }
}

}

我正在寻找的通知是 .screenChanged,但我也想使用 @Scriptable 提到的 .announcement。

有时您必须使用 DispatchQueue.main.asyncAfter 来解决竞争条件,如在:

中提到的

在这里我找到了其他通知:Default UIAccessibilityElement after screen change