ios - 如何正确设置 RN AccessibilityInfo setAccessibilityFocus

标签 ios react-native accessibility

React 原生应用程序,在 IOS 中测试。 期望的行为: 在 TextInput 中输入无效值并单击提交按钮。屏幕阅读器 shell 发出错误提示,焦点 shell 返回到 textInput。设置焦点外壳不打开键盘。

实现中的问题: 焦点返回到 textInput 似乎是在辅助功能标签更新为错误消息之前。屏幕阅读器发音为旧的辅助功能标签。 如果在 Voice Over 发音后触摸 textInput,则会发音带有错误消息的更新辅助功能标签。

该代码也可以在世博小吃中找到, snack

import * as React from 'react';
import { AccessibilityInfo, TextInput, Button, Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
import AssetExample from './components/AssetExample';

import { Card } from 'react-native-paper';

const errorBtn = "Click to simulate invalid text input";
const clearBtn = "Click to clear the error";
const errorLabel = "Wrong Number has being typed, try again";
const clearLabel = "Phone Number";
const description = 
"Enable Voice Over, If Error hapens on textInput, screen reader shell pronounce the error message and set focus to the textInput where the error happend"
const placeholder = "Type 10 digit phone number"

export default class App extends React.Component {
  state = {
           error: false,
           text: "", 
           buttonTitle: errorBtn
           }
errorMsg= () => {this.setState({error: true})}
clearMsg= () => {this.setState({error: false})}

onPress = () => {
  const TextInputNativeTag =   
                    this
                      .textOInputRef
                      ._inputRef
                      ._nativeTag
  if (this.state.buttonTitle == errorBtn) 
    this.errorMsg();
  if (this.state.buttonTitle == clearBtn) 
    this.clearMsg();
  if (this.textOInputRef) 
    AccessibilityInfo
    .setAccessibilityFocus(TextInputNativeTag);

  this.state.buttonTitle == errorBtn &&
  this.setState({buttonTitle:clearBtn});

  this.state.buttonTitle == clearBtn && 
  this.setState({buttonTitle:errorBtn}) ;
  }
  render() {
    return (
    <View>
      <View >
        <Text accessibilityRole='header' 
              style={styles.header}
        >
          Accessabiity Focuse Tester
        </Text>
        <Text style={styles.paragraph}>
          {description}
        </Text>
      </View>
        <View style={styles.body}>
          <View style={styles.sectionContainer}           >
            <Text 
              accessibilityRole='header' 
              style={styles.sectionTitle}
            >
              Accessability Label
            </Text>
            <Text style={
              styles.sectionDescription}
            >
              {this.state.error 
                ? errorLabel 
                : clearLabel 
              }
            </Text>
          </View>

          <View style={
            styles.sectionContainer}
          >
            <TextInput
                ref={r => 
                  this.textOInputRef = r
                }
                style={
                  {height: 40, 
                   borderColor: 'gray', 
                   borderWidth: 1}
                }
                accessibilityLabel={
                  this.state.error 
                   ? errorLabel 
                   : clearLabel 
                }
                underlineColorAndroid=
                  "transparent"
                keyboardType="numeric"
                returnKeyType="done"
                onChangeText={
                  text => 
                    this.setState({text})
                }
                placeholder={placeholder}
                placeholderTextColor=
                  "rgb(143,143,143)"
                value={this.state.text}
              />
          </View>

          <View style={styles.sectionContainer}>
            <Button 
              onPress={this.onPress} 
              title={this.state.buttonTitle}
              color="#841584"
              accessibilityLabel={this
                                  .state
                                  .buttonTitle}
            />
          </View>
        </View>
    </View>
  ); 
 }
}

const styles = StyleSheet.create({
  header: {
    margin: 24,
    fontSize: 18,
    textAlign: 'center',
    fontWeight: 'bold',
  },
  paragraph: {
    margin: 24,
    fontSize: 18,
    textAlign: 'center',
  },
  body: {
    backgroundColor: '#fff',
  },
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
    color: '#000',
  },
  sectionDescription: {
    marginTop: 8,
    fontSize: 18,
    fontWeight: '400',
  },
});

最佳答案

按下按钮时,屏幕阅读器会在 onPress 方法运行期间读取按钮标签。因此需要提前安排渲染时间。 另外,标签更新为新值后,还需要调度setfocus到textInput。 以下是代码和 snack

import * as React from 'react';
import { AccessibilityInfo, TextInput, TouchableWithoutFeedback, Text, View, StyleSheet } from 'react-native';

const errorBtn = "Click to simulate invalid text input";
const clearBtn = "Click to clear the error";
const errorLabel = "Wrong Number has being typed, try again";
const clearLabel = "Phone Number";
const description = 
"Enable Voice Over, If Error hapens on textInput, screen reader shell pronounce the error message and set focus to the textInput where the error happend"
const placeholder = "Type 10 digit phone number"

export default class App extends React.Component {
state = {  
           error: false,
           text: "", 
           buttonTitle: errorBtn
           }
label=clearLabel;
accessabilityMsg=null;
textInputRef=null;

onPress = () => {
  const textInput = this.textInputRef && this.textInputRef._inputRef._nativeTag;
  if (this.state.buttonTitle == errorBtn) {
    console.log('onPress set label & state to error');
    setTimeout(()=>{
      this.setState({error: true,buttonTitle:clearBtn});
      setTimeout(()=>{textInput && AccessibilityInfo.setAccessibilityFocus(textInput)},500);
    },6000);
    this.label=errorLabel;
  } else {
    console.log('onPress set label & state to normal') 
    this.setState({error: false,buttonTitle:errorBtn});
    this.label=clearLabel;
  }
}

  render() {
    return (
    <View >
      <View >
        <Text accessibilityRole='header' 
              style={styles.header}
        >
          Accessabiity Focus Tester
        </Text>
        <Text style={styles.paragraph}>
          {description}
        </Text>
      </View>
        <View style={styles.body}>
          <View style={styles.sectionContainer}           >
            <Text 
              accessibilityRole='header' 
              style={styles.sectionTitle}
            >
              Accessability Label
            </Text>
            <Text style={
              styles.sectionDescription}
            >
              {this.label}
            </Text>
          </View>

          <View style={
            styles.sectionContainer}
          >
            <TextInput
                ref={r => 
                  this.textInputRef = r
                }
                style={
                  {height: 40, 
                   borderColor: 'gray', 
                   borderWidth: 1}
                }
                accessibilityLabel={this.label}
                underlineColorAndroid=
                  "transparent"
                keyboardType="numeric"
                returnKeyType="done"
                onChangeText={
                  text => 
                    this.setState({text})
                }
                placeholder={placeholder}
                placeholderTextColor=
                  "rgb(143,143,143)"
                value={this.state.text}
              />
          </View>

          <View style={styles.button}>
            <TouchableWithoutFeedback
              accessible
              accessibilityRole='button'
              accessibilityLabel={this.state.buttonTitle}
              onPress={this.onPress}
            >
              <Text> {this.state.buttonTitle} </Text>
            </TouchableWithoutFeedback>
          </View>
        </View>
    </View>
  ); 
 }
}

const styles = StyleSheet.create({
  header: {
    margin: 24,
    fontSize: 18,
    textAlign: 'center',
    fontWeight: 'bold',
  },
  paragraph: {
    margin: 24,
    fontSize: 18,
    textAlign: 'center',
  },
  body: {
    backgroundColor: '#fff',
  },
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
    color: '#000',
  },
  sectionDescription: {
    marginTop: 8,
    fontSize: 18,
    fontWeight: '400',
  },
  button: {
    margin: 24,
    alignItems: 'center',
    backgroundColor: '#DDDDDD',
    padding: 10,
    borderColor: '#000',
    borderWidth: 1
  },
});

最后,任务还没有完成。 我们无法设计基于 setTimout 的应用程序。 仍然需要找到更好的方法。 感谢您的任何评论和帖子。

关于ios - 如何正确设置 RN AccessibilityInfo setAccessibilityFocus,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57317960/

相关文章:

ios - 无法从 Webviewcontroller 打开 Safari 浏览器

ios - 如何将透明 'large title' UINavigationBar重置为默认外观设置?

android - 切换到 HEREMap premium SDK 后性能下降

function - 打开 native 屏幕时调用函数

html - 我应该如何使这个带有标签的隐藏复选框可访问

html - 侧边栏的 ARIA 地标角色,其中包含主导航

accessibility - 隐藏在蜜 jar 领域的咏叹调

ios - 无法触摸 UIScrollView 中的 UIButton

ios - 通过单击按钮显示或隐藏项目

android - 定位 R+(版本 30 及更高版本)需要将已安装 APK 的 resources.arsc 未压缩存储并在 4 字节边界上对齐