널리 알리는 기술 소식 다양한 접근성과 사용성, UI 개발에 대한 소식을 널리 알리고 참여하세요!
Spread your knowledge!

포럼 우리 모두의 소중한 의견이 모이는 곳입니다.

검색하기
  • tip
    [jetpack compose] 활성화 하려면 두 번 탭하세요 힌트 메시지 변경하기
    Webacc NV 2022-05-26 12:57:12

    레이아웃을 기준으로 개발된 안드로이드 view 시스템에서는 replaceAccessibilityAction 메서드를 통하여 활성화 하려면 두 번 탭하세요 에 대한 힌트 메시지를 변경할 수 있었습니다.

    Jetpack compose 에서는 .clickable modifier 속성 중 onClickLabel을 통해 힌트 메시지 변경이 가능합니다.

    다만 기존 안드로이드 view 시스템과 같이 이중탭하세요 라는 기본 텍스트는 변경이 불가합니다.

    예를 들어 메일 보관 작업을 하려면 이중태바세요 라는 힌트 메시지로 변경하려면 다음과 같이 적용이 가능합니다.

        Row(
            modifier = Modifier
                .clickable(onClickLabel = "메일 보관") {
                state.value = !state.value
            }, verticalAlignment = Alignment.CenterVertically
        ) { .... }
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [ARIA 예제] 편집창에 바로 입력되지 않는 자동완성 예제 체험해보기
    Webacc NV 2022-05-24 12:03:38

    널리 아티클 및 포럼 팁을 통하여 자동완성 리스트가 편집창에 입력되는 형태가 아닌 경우에는 aria-activedescendant, role listbox, role option 속성을 사용하여 접근성 적용을 해야 함을 여러 번 언급하였습니다.

    관련 접근성이 잘 적용된 예제를 구현하여 해당 팁을 통해 공유합니다.

    스크린 리더를 실행한 상태에서 샘플 페이지를 방문하여 자동완성 접근성이 잘 적용된 예시를 체험해 보시기 바랍니다.

    센스리더의 경우 가상커서를 해제 후 테스트 해야 합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 상태정보 커스텀으로 제공해주기
    Webacc NV 2022-05-21 13:38:18

    안드로이드 11부터 제공되고 있는 ViewCompat.setStateDescription이라는 API를 아시나요?

    일반적으로 상태정보는 선택됨, 선택안됨, 켜짐, 꺼짐 등을 제공하게 되는데 상황에 따라서는 읽지 않음, 읽음, 주문완료, 주문안됨과 같은 정보를 문자 형태로 주어야 하는 경우가 있습니다.

    이런 경우 지금까지는 대체 텍스트 형태로 해당 정보들을 다 제공해 왔는데 대체 텍스트로 제공할 경우 스크린 리더 사용자가 읽기 방식을 본인 나름대로 커스텀해서 해당 정보를 들을 수 있는 방법이 없습니다.

    즉 읽음/ 읽지 않음, 주문완료/주문안됨과 같은 정보를 콘텐츠보다 먼저 듣고싶을 수도 있고 콘텐츠 다음에 듣고 싶을 수도 있는데 이러한 선택권이 없어진다는 것입니다.

    이를 해결하기 위해 사용할 수 있는 것이 ViewCompat.setStateDescription입니다.

    인자값으로는 참조해야 할 view 객체와 상태값 문자입니다.

    예: ViewCompat.setStateDescription(binding.message, "unread")

    이렇게 하면 사용자가 톡백에서 읽기 순서를 어떻게 지정했느냐에 따라 상태값을 처음 또는 끝에 읽을 뿐만 아니라 상태값이 토글되는 경우 변경된 상태값만 깔끔하게 읽어주게 됩니다.

    그러므로 모든 것을 대체 텍스트로 넣기 보다는 상태값은 상태값으로 분류하여 구현하는 것이 사용성을 높일 수 있겠습니다.

    참고로 제트백 컴포즈로 앱을 구현한다면 semantics로 stateDescription 정보를 줄 수 있습니다.

        modifier = Modifier.semantics {
             (stateDescription = "읽지 않음")
        }

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] UITextView 요소를 보이스오버가 읽을 시 쉼표가 붙는 이슈 수정 관련
    Webacc NV 2022-05-20 17:12:43

    iOS 15 초기버전부터 iOS 15.4까지 UITextView 내에 한글로 텍스트를 구현하면 VoiceOver에서 스페이스를 기준으로 모두 쉼표를 붙여 읽는 문제가 있었습니다.

    예: 이것은, 접근성, 테스트를, 하기, 위해, 작성한, 것입니다.

    그래서 스크린 리더 사용자는 해당 요소를 읽을 때 모든 글자를 다 끊어 읽어서 상당히 불편한 부분이 잇었습니다.

    해당 이슈는 iOS 15.5버전부터 해결되었습니다.

    접근성 진단 시 참고하시기 바랍니다.

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] improveAccessibility.js에 announceForAutoComplete 메서드 추가
    Webacc NV 2022-05-19 11:08:30

    편집창에 특정 글자 입력 시 자동완성이 표시되는 UI에서는 자동완성이 특정 글자를 입력했을 때만 나타나므로 이에 대한 알림을 선별해서 제공해야 합니다.

    따라서 스크린 리더 사용자가 타이핑을 빠르게 입력하다보면 자동완성 리스트가 수시로 나타났다 사라졌다를 반복할 것입니다.

    이때 알림 제공을 구현할 때 조금 더 쉽게 적용할 수 있도록 announceForAutoComplete(message) 메서드를 만들어 공유하게 되었습니다.

    사용법은 너무나 간단합니다.

    자동완성 관련 리스트가 나타나는 시점에 announceForAutoComplete("자동완성 표시됨")과 같이 적용만 해 주시면 됩니다.

    다만 자동완성이 사라질 때에는 removeAnnounceForAccessibility() 함수를 적용합니다.

    그러면 자동완성 리스트가 수시로 나타났다 사라졌다를 반복하더라도 마지막 입력한 글자에서 자동완성이 표시된다면 스크린 리더에서 한번만 해당 메시지를 출력하게 됩니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [ARIA 예제] role="progressbar" 샘플 페이지 체험해보기
    Webacc NV 2022-05-18 11:26:56

    파일 업로드와 같이 진행률을 표시하는 경우 접근성을 적용하기 위해서는 role="progressbar" 속성을 사용하여 스크린 리더 사용자도 실시간 진행 상황을 알 수 있도록 구현해야 합니다. 

    퍼센트의 범위가 0-100인 경우에는 aria-valuemin, valuemax는 생략이 가능하며 aria-valuenow를 통해 현재 퍼센트를 실시간으로 업데이트할 수 있습니다.

    마지막으로 aria-label 혹은 aria-labelledby 속성을 통해 progressbar 요소의 레이블을 정의합니다.

    이러한 것들을 적용했을 때 스크린 리더가 어떻게 읽어주는지를 체험해 볼 수 있는 샘플 페이지를 제작하였습니다.

    스크린 리더를 실행한 상태에서 아래 페이지를 실행해 보시기 바랍니다.

    샘플 페이지 가기

     

    댓글을 작성하려면 해주세요.
  • tip
    [android] 앱이 어떤 플랫폼으로 개발되었는지 디버깅으로 알아보기
    Webacc NV 2022-05-17 09:45:10

    최근 들어 안드로이드 앱이 여러 형태로 개발되고 있습니다. 

    네이티브 앱 중에서도 xml 기반의 view 형태로 개발된 앱이 대부분이지만 선언형 함수를 사용하여 jetpack compose로 개발된 앱들도 앞으로 점점 생겨날 것이라 예상하고 있습니다.

    또한 flutter로 개발된 앱들은 실제로도 많이 출시되고 있습니다.

    이러다보니 접근성 진 단 시 해결방안을 제시할 때 같은 네이티브 앱이라도 어떤 플랫폼으로 개발되었는지를 아는 것이 중요해지게 되었습니다.

    UIAutomator를 사용하여 접근성 트리를 디버깅하면 어느정도 어떤 플랫폼으로 개발하였는지를 유추할 수 있습니다. 

    우선 flutter, jetpack compose는 xml 레이아웃을 사용하지 않았기 때문에 가장 상단의 액티비티 제목 텍스트를 제외하고 Linear, Relative와 같은 요소들이 없으며 다 View라는 요소들로 레이아웃이 구성되어 있습니다.

    따라서 View > View > EditText 와 같은 구조로 트리가 구성된다면 view 시스템이 아닌 다른 플랫폼으로 개발되었을 가능성이 큽니다.

    다만 flutter인지 jetpack compose 형태로 개발되었는지를 접근성 트리를 통해 직관적으로 아는 방법은 쉽지 않습니다. 

    오히려 이것은 접근성 트리 디버깅보다는 스마트폰에서 TalkBack으로 확인하는 것이 더 쉽습니다.

    이 부분에 대해서는 추후 다루도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 테이블뷰셀 내에 있는 접근성 요소들의 초점 순서 조정은 금물
    Webacc NV 2022-05-12 18:43:42

    접근성 초점의 순서가 틀어질 때 accessibilityElements 배열을 통해서 접근성 초점 순서를 재조정할 수 있는 부분에 대해 다룬 적이 있습니다.

    그런데 테이블뷰셀 내에 있는 요소들은 accessibilityElements 배열로 접근성 초점 순서를 재조정해서는 안 됩니다.

    그 이유는 다음과 같습니다.

    우선 테이블뷰셀 내의 접근성 순서를 재조정한다는 것은 다음과 같은 코드를 사용하게 됩니다.

    cell.accessibilityElements = [cell.2, cell.4, cell.3, cell.1]

    위의 코드의 의미는 셀의 접근성 초점은 셀 자체를 거치지 말고 바로 2, 4, 3, 1 순서로 초점을 이동시키라는 의미가 됩니다. 

    따라서 이렇게 되면 한 손가락 오른쪽 혹은 왼쪽 쓸기로 탐색 시 테이블뷰셀 자체가 가지는 보이스오버 스크롤 기능을 잃게 되어 자동으로 화면이 스크롤되지 못하게 됩니다.

    이것은 안드로이드에서도 비슷합니다.

    안드로이드의 RecyclerView 레이아웃이 구현되면 접근성 초점이 이동하면서 자동으로 화면이 스크롤되도록 API가 구현되어 있습니다.

    그런데 해당 RecyclerView의 접근성 속성을 no, 즉 recyclerView.importnatForAccessibility = NO 로 설정하면 화면 아래에 더 많은 콘텐츠가 있음에도 불구하고 한 손가락 쓸기로 이동 시에 더 이상 화면 스크롤이 이동하지 않게 됩니다.

    물론 iOS 테이블뷰 내에서 아래 예시와 같이 각 셀마다 콜렉션뷰들이 중첩되어 들어 있는 경우에는 상황에 따라 tableView.accessibilityElements = [collectionView]와 같이 설정할 수는 있습니다. 

    tableView > tableViewCell > collectionView > collectionViewCell

    이 경우에도 테이블뷰셀을 거치지 않지만 콜렉션뷰를 거치게 되므로 초점 이동시 스크롤에는 문제가 없기 때문입니다. 

    게다가 각 콜렉션뷰셀 내의 요소가 단순한 텍스트라면 위의 구조에서 보이스오버는 테이블뷰셀을 기준으로 삼게 되므로, 즉 한 초점에 한 테이블뷰셀에 들어 있는 모든 콜렉션뷰셀들을 잡아버리게 되므로 오히려 tableView.accessibilityElements = [collectionView] 와 같은 코드가 더 필요할 수 있겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] improveAccessibility.js에 setAsHeading 메서드 추가
    Webacc NV 2022-05-10 09:53:13

    헤딩은 스크린 리더 사용자가 페이지의 섹션을 구분하는 측면에서 너무나 중요한 요소 중 하나입니다.

    또한 두말할 나위도 없이 헤딩 단위로 탐색이 가능하므로 대략적인 페이지의 레이아웃을 빠르게 파악하는 수단이 되기도 합니다.

    그런데 제목 요소를 헤딩으로 사용하지 않고 strong과 같은 태그에 제목 스타일을 적용하는 경우들이 종종 있습니다.

    이러한 요소들의 접근성을 조금 더 빠르게 해결하기 위해 setAsHeading 메서드를 만들어 공유하게 되었습니다.

    해당 메서드 안의 인자값으로는 타겟 즉 헤딩으로 정의하고자 하는 요소와 레벨정보(숫자)가 들어갑니다.

    그러면 해당 요소에 role="heading", aria-level="인자값으로 정의된 숫자"가 마크업됩니다.

    만약 특정 클래스를 가진 요소가 다 같은 레벨을 주어도 되는 요소라면 forEach function을 사용하여 동일한 클래스에 모두 적용되도록 할 수도 있을 것입니다.

    아래는 주의사항입니다.

    1. 헤딩 레벨은 반드시 1-6 사이의 숫자만 인가자값으로 주어야 합니다.

    2. 타겟에는 strong, div, span, em과 같은 태그 외에 button, link와 같은 요소를 지정해서는 안 됩니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [android native] ViewPager 위젯에서 getCount() 메서드가 접근성에서 중요한 이유
    Webacc NV 2022-05-07 19:10:46

    ViewPager 위젯을 사용하여 개발된 화면에서 두 손가락으로 좌 또는 우로 스크롤해서 페이지가 변경되거나 탭레이아웃에서 탭을 전환하거나 몇초 간격으로 자동으로 페이지가 전환되는 경우 TalkBack에서는 5페이지 중 3페이지와 같이 페이지 정보를 음성출력합니다.

    그런데 특정 앱에서는 TalkBack에서 페이지 번호를 19,750페이지 중 17000페이지와 같이 페이지 정보를 이상하게 출력하는 경우를 접근성 진단을 하신 분들은 경험하셨을 것입니다.

    ViewPager 위젯의 getCount()라는 메서드는 페이지의 총 개수를 정의할 때 사용합니다.

    일반적으로 페이지 콘텐츠의 갯수를 인덱스 형태로 가져와서 pageCount.size 와 같이 총 페이지 정보를 줍니다.

    그러면 이 getCount() 정보를 바탕으로 페이지의 개수가 정해지게 되며 따라서 접근성 API에서도 이 정보를 활용하여 페이지 정보를 출력하는 것입니다.

    그런데 실제 페이지는 10개이지만 getCount()에서 가져오는 정보가 1000개, 10,000개가 넘는 경우 접근성에서는 페이지 총 개수가 getCount()에서 가져온 총 개수이므로 잘못된 페이지 정보를 주게 됩니다.

    기획 의도상 실제 콘텐츠는 10개라 하더라도 페이지를 넘길 때마다 계속 순환하도록 구현하는 경우가 있기 때문입니다.

    그러나 스크린 리더 사용자는 페이지 번호는 계속 증가하지만 콘텐츠는 10개 내에서 계속 순환하게 되므로 상당한 혼란을 겪게 되는 것입니다.

    따라서 스크린 리더 사용자를 위해서 getCount는 실제 존재하는 페이지수만큼 숫자를 주는 것이 필요하며 다음 예시와 같이 TalkBack이 켜져 있을 때에는 실제 존재하는 콘텐츠 개수만큼 카운트를 줄 수 있겠습니다.

    	val a11y = AccessibilityKotlin
        override fun getCount(): Int {
    		if (a11y.isTalkBackOn(context)) {
    			return 2
    		}
            return list.size
        }
    

     

    유틸 클래스 자바 다운로드

    유틸클래스 코틀린 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [flutter] Image 위젯과 onTap 이벤트 사용시 접근성 관련 주의사항
    Webacc NV 2022-05-05 18:57:16

    플러터에서 다음과 같은 형식으로 이미지를 추가할 수 있습니다.

    leading: Image.network(book.image)

    위와 같이 이미지 위젯이 포함되면 보이스오버, 톡백 모두 해당 요소의 텍스트 뒤에 이미지라고 읽습니다.

    문제는 onTap 이벤트가 포함되는 경우입니다.

    onTap은 말 그대로 사용자가 해당 요소를 탭했을 때 실행될 이벤트를 정의하는 것인데 여러 차례 말씀드린 것처럼 안드로이드에서는 ImageView라 하더라도 클릭 이벤트가 포함되는 순간 톡백에서 해당 요소를 버튼으로 처리합니다.

    그러나 보이스오버는 톡백과 달리 클릭 이벤트를 별도로 구분하지 못합니다.

    따라서 Image 위젯과 onTap을 적용한 상태에서 톡백으로만 접근성 테스트를 하면 버튼이라고 읽어주므로 접근성에 문제가 없는 것처럼 보이지만 보이스오버로 테스트하면 단순히 이미지라고만 읽어주어 사용자가 이중탭하여 실행할 수 있는 요소라는 것을 알려주지 못하게 됩니다.

    따라서 이를 해결하기 위해 반드시 상위에 Semantics 위젯을 덧씌우고 button: true 속성을 함께 포함해 줍니다.

    즉 Image 위젯이 Semantics child로 포함되도록 하는 것입니다.

    다만 이렇게 하면 보이스오버는 버튼 이미지라고 두 개의 요소 유형을 읽게 됩니다.

    단순한 버튼으로만 읽도록 하기 원한다면 excludeSemantics: true 속성을 Semantics 위젯에 함께 줍니다.

    아래는 코드 예시 입니다.

      @override
      Widget build(BuildContext context) {
        return ListTile(
          title: Text(book.title),
          leading: Semantics(
    								button: true, child: Image.network(book.image)),
          onTap: () {
            Navigator.of(context).push(MaterialPageRoute(
              builder: (context) => DetailScreen(
                book: book,
              ),
            ));
          },
        );
      }
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] createIdForChildrenOf 메서드 추가
    Webacc NV 2022-05-02 16:04:01

    얼마전 createIdForAllTag 메서드를 공유한 적이 있습니다.

    해당 메서드는 DOM이 불러와진 상태에서 body 내의 모든 태그를 대상으로 id를 생성하는 메서드였다면 지금 소개해 드릴 createIdForChildrenOf 메서드는 메서드의 이름에서도 알 수 있듯이 인자값에 엘리먼트를 주면 그 엘리먼트를 포함한 하위 요소들의 아이디가 없는 모든 태그에 아이디를 부여할 때 사용할 수 있습니다.

    만약 인자값 없이 createIdForChildrenOf() 라고만 입력하면 기존과 같이 body 내의 아이디가 없는 모든 태그에 아이디를 생성하게 됩니다.

    해당 메서드를 가장 잘 활용할 수 있는 예시는 바로 자동완성 편집창입니다.

    검색어와 같은 자동완성을 지원하는 편집창에 값을 입력 후 화살표를 내려 자동완성 리스트를 탐색할 때 편집창에 자동완성 텍스트가 입력되지 않는 경우에는 aria-activedescendant 속성을 활용하여 현재 가리키고 있는 자동완성 링크를 아이디 값으로 연결시켜 주어야 합니다.

    이때 자동완성 리스트가 불러와질 때마다 해당 메서드를 호출하면 아이디를 자동으로 생성해 주게 되므로 aria-activedescendant 속성에 아이디를 연결하기 편리해집니다.

    아래는 aria-activedescendant로 자동완성 리스트 값을 연결할 때 주의사항입니다.

    1. 자동완성 리스트는 반드시 role listbox > role option 형태로 마크업 되어 있어야 합니다.

    2. ul > li > a 구조라면 li는 반드시 role none 속성을 줍니다.

    3. 자동완성 리스트가 표시되지 않았거나 자동완성 목록 중 하나를 가리키고 있지 않는 경우에는 편집창에 aria-activedescendant="" 형태로 마크업하고 자동완성 리스트 중 하나를 가리키고 있을 경우에는 해당 아이디를 연결해줍니다.

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] improveAccessibility.js에 createIdForAllTag 메서드 추가
    Webacc NV 2022-04-19 11:09:16

    접근성 적용시 가장 고민하는 부분 중 하나가 바로 id를 지정하는 것입니다.

    aria-describedby, aria-labelledby를 비롯하여 aria-controls로 연결할 때에도 연결하고자 하는 태그에 id가 있어야 합니다.

    그래서 body 내의 script 태그를 제외한 id가 지정되지 않은 모든 태그에 태그 이름과 숫자를 조합한 id를 생성하는 메서드를 제작하여 공유하게 되었습니다.

    적용방법은 너무나 간단합니다.

    페이지가 완전히 로딩된 상태에서 createIdForAllTag() 메서드를 실행하면 끝입니다.

    그 상태에서 스크립트 등을 이용하여 참조하고자 하는 아이디를 가져와서 사용할 수 있습니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] improveAccessibility.js에 waiAriaHasPopupMenu 메서드 추가
    Webacc NV 2022-04-18 15:52:08

    윈도 탐색기 폴더에서 오른쪽 마우스 혹은 팝업 키를 눌렀을 때 표시되는 상황에 맞는 메뉴와 같은 구조의 드롭다운 메뉴를 사용해야 하는 경우에 메뉴 레이어로 초점만 보내주는 것만으로는 스크린 리더 사용자의 레이아웃 파악이 어렵습니다.

    옵션으로 표시되는 메뉴는 모달이 아니기 때문에 레이어의 구분이 명확하지 않기 때문입니다.

    해당 UI는 ARIA 메뉴로 마크업하고 키보드 접근성만 구현하면 윈도 탐색기의 상황에 맞는 메뉴와 흡사한 탐색이 가능하여 훨씬 접근성을 높일 수 있습니다.

    그러나 옵션 메뉴의 키보드 접근성을 스크립트로 구현하기에는 많은 공수가 들어갑니다.

    이러한 부분을 조금이라도 해결하기 위해 널리에서 작년에 배포한 WAI-ARIA UI 스크립트 중 waiAriaHasPopupMenu 메서드를 조금 업데이트 하여 추가하게 되었습니다.

    해당 메서드를 사용하려면 다음 형식으로 마크업을 진행해야 합니다.

    1. 메뉴를 여는 버튼에는 버튼 혹은 role button 을 사용하고 aria-haspopup true 혹은 menu 속성을 줍니다.

    하위 1단계 메뉴에는 role menuitem을 사용합니다.

    2. 메뉴를 여는 버튼과 1단계 메뉴 그룹은 aria-controls로 연결합니다.

    3. 1단계 메뉴 그룹에는 role menu 속성을 줍니다.

    ul > li> a 구조라면 ul에는 role menu, li는 role none, a는 role menuitem입니다.

    이렇게 마크업하고 waiAriaHasPopupMenu() 메서드를 적용하면 다음과 같이 키보드가 동작합니다.

    1. 옵션 메뉴를 누르면 1단계 메뉴 첫 번째 요소로 초점이 이동합니다.

    2. 위 또는 아래 화살표를 눌러 메뉴 사이를 이동할 수 있습니다.

    직접 적용해야 하는 부분 및 참고사항:

    1. 옵션 메뉴가 열린 상태에서 탭 혹은 ESC키를 누르면 일반적으로 메뉴가 닫히고 기존 버튼으로 돌아와야 합니다. 그러나 해당 부분 구현은 페이지마다 메뉴를 닫는 방법이 다양하고 일정하지 않아서 해당 메서드에서는 제외하였습니다. 

    2. 현재는 role menuitem 사이에서만 스크립트가 적용되어 있으나 조만간 role menuitemradio 속성이 있을 경우도 함께 추가할 예정입니다.

    3. 메뉴 버튼을 눌렀을 때 메뉴 레이어가 동적으로 생성되는 경우에는 메뉴 요소들이 생성된 시점에 waiAriaHasPopupMenu() 메서드를 적용해야 합니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [flutter] CheckBox 위젯은 VoiceOver에서 스위치버튼 역할로 정의됨
    Webacc NV 2022-04-11 19:39:35

    플러터에서는 체크박스 위젯이 존재하는데 네이티브 앱 iOS VoiceOver 에는 체크박스 역할이 없습니다.

    따라서 플러터에서 체크박스 구현 시 iOS 에서는 이를 스위치 버튼으로 읽게 됩니다.

    즉 UISwitch 요소로 구현이 되는 것입니다.

    체크됨은 켜짐, 체크되지 않음은 꺼짐입니다.

    사실상 의미론적으로는 스위치와 체크박스는 다른 성격을 가지고 있으나 플러터로 앱 개발 시 현재로서는 이러한 차이가 있음을 참고하여 접근성 진단을 진행할 필요가 있겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [flutter] 화면에 보여지는 일반 텍스트의 레이블을 대체 텍스트로 대체할 경우
    Webacc NV 2022-04-10 11:52:32

    iOS, android에서 앱 접근성을 구현할 때 상황에 따라서는 화면에 보여지는 텍스트에 추가 정보를 제공하기 위해 대체 텍스트로 텍스트를 덮어 씌우는 경우가 있습니다.

    예를 들어 아이콘으로 수량이라는 의미를 표시한 다음 숫자만 화면에 텍스트로 표시되면 스크린 리더에서는 숫자만 읽어주게 되므로 구체적인 의미를 알기 어렵기 때문입니다.

    네이티브 앱에서는 accessibilityLabel 혹은 contentDescription 속성을 통해 텍스트뷰에 대체 텍스트를 적용하면 기존 텍스트가 덮어씌워집니다.

    그러나 플러터에서는 Senatics 위젯 안에 단순히 label만 제공하면 레이블과 화면에 있는 텍스트를 함께 읽게 됩니다.

    읽는 순서는 위젯 트리 구조와 같이 접근성 레이블 텍스트 + 화면에 보여지는 텍스트 순입니다.

    따라서 다음과 같이 정리를 할 수 있습니다.

    1. 대체 텍스트에서 화면에 있는 텍스트를 포함해야 하는 경우에는 추가 정보만 label에 포함합니다.

    2. 만약 대체 텍스트로 화면에 보여지는 텍스트를 완전히 덮어 씌우기를 원한다면 Semantics 위젯 안에 excludeSemantics: true 속성을 함께 줍니다.

    해당 속성은 말 그대로 하위의 모든 정보를 접근성 노드에서 무시하겠다는 이야기입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 레이아웃용으로 만들어진 GridView, RecyclerView 등에서의 항목 개수 정보를 TaalkBack이 읽지 않도록 해야 할 때
    Webacc NV 2022-04-08 13:56:56

    TalkBack으로 앱을 테스트 하다보면 특정 요소에 접근했을 때 목록에 있음 항목 x개 와 같이 음성을 출력하는 것을 많이 들어보셨을 것입니다.

    혹은 x행 x열과 같은 정보를 출력하는 것 또한 발견하셨을 것입니다.

    이는 RecyclerView 혹은 GridView 구현 시 접근성 노드 내에 CollectionInfo, CollectionItemInfo 정보가 만들어지게 되는데 전체 항목 개수및 행, 열 정보 등을 참조하여 읽어주는 것입니다.

    데이터 테이블이나 목록 그룹으로 된 데이터를 탐색할 때는 이 정보가 스크린 리더 사용자에게 큰 도움이 됩니다.

    그러나 레이아웃용으로 만들어진 그리드뷰 등에서는 오히려 전체 항목 개수를 알려주는 것이 불필요할 수 있습니다.

    이 때는 접근성 노드에서 setCollectionInfo, setCollectionItemInfo 값을 null 로 설정하면 해당 항목 개수를 가지고 오지 않게 됩니다.

    조만간 안드로이드 유틸 클래스에 해당 메서드를 추가하여 공유하도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [android wear] 워치에서의 최신 TalkBack 업데이트에 따른 클릭 리스너가 포함된 ImageView 처리방식 수정 관련
    Webacc NV 2022-04-02 12:17:29

    22년 3월, 널리 아티클을 통하여 갤럭시 워치4의 접근성 기능에 대해 다루었습니다.

    해당 아티클을 작성할 당시 ImageView 요소에 클릭 리스너가 포함되는 경우 TalkBack에서 이를 버튼이 아닌 이미지로 읽어주는 이슈가 있음을 기술하였습니다.

    그런데 얼마전 wearOS TalkBack이 한차례 업데이트 되었는데 업데이트 이후로는 스마트폰과 마찬가지로 ImageView에 클릭 리스너가 포함되면 버튼으로 읽어줍니다.

    앱 개발 시 참고하시기 바랍니다.

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] next.js 라이브러리로 웹 개발 시 페이지 타이틀 announcement 접근성 적용 관련
    Webacc NV 2022-04-01 18:37:20

    next.js 라이브러리는 react를 기반으로 하고 있습니다. 

    그래서 페이지 내에서 내비게이션의 여러 링크를 눌러 다른 페이지로 전환하더라도 페이지 전체가 새로고침되지 않기 때문에 접근성 적용을 하지 않으면 스크린 리더 사용자는 페이지가 변경되었음을 알 수 없습니다.

    그런데 next.js 라이브러리는 기본적으로 페이지 타이틀이 변경될 때 페이지 제목을 스크린 리더가 자동으로 읽어주도록 하는 태그 next-route-announcer가 기본으로 붙습니다.

    해당 태그 안에는 <p> 태그가 하나 있으며 페이지가 처음 불러와질 때에는 텍스트 없이 불러오지만 페이지가 전환되면 페이지 타이틀을 <p> 안에 자동으로 삽입하게 됩니다.

    <p> 태그에는 다음의 속성이 포함됩니다.

    <p aria-live="assertive" id="__next-route-announcer__" role="alert" style="border: 0px; clip: rect(0px, 0px, 0px, 0px); height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: absolute; width: 1px; white-space: nowrap; overflow-wrap: normal;"></p>

    즉 해당 요소의 텍스트는 스크린 리더용 메시지이며 화면상으로는 출력되지 않는 것입니다.

    그런데 실제 next.js로 페이지를 개발하다보면 해당 기능에 버그가 있는 것을 알 수 있습니다.

    1. 페이지를 전환할 때 변경된 페이지 제목을 텍스트로 가지고 오지 못하거나 또는 이전 페이지 제목을 텍스트로 삽입해버리는 경우: 내비게이션 메뉴의 특정 링크를 누르면 어떤 경우에는 페이지 제목 타이틀을 잘 가지고 오지만 어떤 경우에는 이전의 텍스트를 그대로 유지하여 실시간으로 변경되는 페이지 제목을 제대로 읽어주지 못하는 경우입니다. 한번 페이지 제목 텍스트를 갱신하지 못하면 그 다음 다른 페이지로 전환 시 이전 제목 텍스트를 가지고 오게 되어 오히려 스크린 리더 사용자에게 더 혼란을 줍니다.

    2. 스크린 리더용 제목 텍스트가 사라지지 않는 문제: next-route-announcer 태그는 페이지가 전환될 때 토스트 형태로 스크린 리더가 제목을 읽도록 하는 역할을 하는데 해당 텍스트가 스크린 리더 가상커서에 계속 남아 있습니다.

    따라서 스크린 리더 사용자가 페이지를 탐색할 때 가장 아래쪽에 토스트 메시지를 한번 더 읽어주게 되는 이슈가 있습니다.

    마치 토스트로 떴다가 사라져야 할 텍스트가 화면 상에 계속 남아 있는 것과 같습니다.

    따라서 해당 이슈가 next.js 라이브러리 자체에서 수정되기 전까지는 다음과 같은 간단한 방법으로 해결이 가능합니다.

    1. 페이지가 최초로 불러와질 때 next-route-announcer 태그는 aria-hidden true 속성을 추가하여 스크린 리더에서 숨깁니다.

    이렇게 되면 페이지 전환 시 우선 아무런 내용도 읽지 않게 됩니다.

    2. 저희가 현재 배포하고 있는 improveAccessibility.js 안에 포함되어 있는 announceForAccessibility 메서드를 활용할 수 있습니다.

    페이지 내비게이션이 있는 영역을 클릭할 때 약 0.5초 정도 딜레이를 주어 변경된 페이지 제목(document.title) 텍스트를 인자 값으로 주어 페이지 제목을 스크린 리더에서 읽도록 할 수 있습니다.

    announceForAccessibility 함수에 대한 자세한 설명은 관련 팁을 참고하시면 됩니다.

    댓글을 작성하려면 해주세요.
  • news
    2022년 3월 15일부터 17일까지 개최되는 axe 컨퍼런스 소개
    Webacc NV 2022-03-12 10:33:43

    axe 는 접근성 자동화 진단 도구로서 웹의 경우 크롬 확장 프로그램으로 설치하여 쉽게 누구나 사용할 수 있는 것이 특징입니다.

    axe 를 개발하고 있는 Deque 에서 axe 접근성 컨퍼런스를 온라인으로 개최합니다.

    3월 15일부터 17일까지 진행되는 이번 행사에는 개발, 디자인, 기관의 더 나은 접근성 및 기타 주제 등 여러 트랙으로 나누어 다양한 접근성 관련 세미나가 진행되며 등록 신청을 하면 무료로 들을 수 있습니다.

    특히 올해 컨퍼런스 키노트에서는 The Future of the Web and Accessibility 라는 주제로 Sir Tim Berners-Lee 가 발표를 진행합니다.

    자세한 내용은 axe 컨퍼런스 홈페이지를 참고해 주시기 바랍니다.

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] modalDialog, setAsModal 메서드 업데이트
    Webacc NV 2022-03-11 11:07:23

    널리 아티클 및 팁을 통하여 대화상자 콘텐츠가 화면을 덮는 형태의 대화상자 팝업에서 접근성을 적용할 수 있는 라이브러리를 공유하였습니다.

    modalDialog 메서드는 대화상자를 여는 버튼과 대화상자 콘텐츠를 연결하여 사용할 수 있는 것이고 setAsModal 은 대화상자 팝업이 동적으로 생성되는 경우 대화상자가 열린 상태에서 라이브러리를 적용할 수 있는 메서드라고 했습니다.

    해당 두 메서드의 업데이트 내용이 있어 공유합니다.

    이제는 firstTab, lastTab 을 CSS 에 별도 추가하지 않아도 됩니다.

    기존에는 대화상자 내에서 탭키로 초점이 가야 하는 요소 들 중 첫 요소와 마지막 요소에 반드시 firstTab lastTab 속성을 HTML CSS 에 추가를 해야만 했습니다.

    그러나 해당 속성이 없어도 대화상자 내의 전체 포커스 가능한 요소(탭인덱스 0 포함)을 가져와서 firstTab, lastTab 을 자동 지정하게 됩니다.

    따라서 다음과 같이 사용할 수 있습니다.

    1. modalDialog: 인자값이 없으며 modalDialog() 라고 선언하면 대화상자를 여는 버튼과 대화상자 컨테이너 전체를 가져와서 접근성을 적용합니다.

    대화상자를 여는 버튼에는 aria-haspopup="dialog" aria-controls="대화상자와 연결된 아이디"로 마크업합니다.

    대화상자 컨테이너는 대화상자가 열리고 닫힐 때 스타일의 변화가 있거나 aria-hidden true/false 값으로 변경되는 컨테이너에 role dialog, aria-modal true 속성을 추가합니다.

    대화상자 내부에 있는 요소들 중 대화상자를 닫는 요소에 closeModal CSS 속성을 추가합니다.

    2. setAsModal: 인자값으로는 대화상자 컨테이너를 지정하고 대화상자가 표시되는 시점에 해당 메서드를 적용하면 됩니다.

    setAsModal 메서드의 경우 모달이 작동하는 동안 해당 모달 영역 내에 role dialog 속성이 없으면 대화상자가 닫히기 전까지 role dialog, aria-modal true 속성을 추가합니다.

    주의: 위의 두 메서드는 대화상자가 사라질 때 role dialog 속성이 있는 곳이나 혹은 setAsModal 인자값으로 지정한 컨테이너 요소가 사라져 버리는 경우에는 절대로 사용해서는 안 됩니다. 동적으로 사라져 버리는 요소에 사용할 경우 aria-hidden 속성이 제거되지 않아 페이지 자체를 사용하지 못하게 됩니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] radioAsButton 메서드 추가 및 샘플 페이지 소개
    Webacc NV 2022-03-09 21:50:13

    라디오버튼은 여러 옵션 중 하나를 선택한다는 의미를 가지고 있습니다.

    그런데 요즘에는 라디오 버튼을 단순히 옵션을 선택하는 용도로만 사용하지 않고 콘텐츠 자체를 갱신시키는 경우에도 사용하는 경우를 종종 보곤 합니다.

    이때는 스크린 리더 사용자나 키보드를 사용하여 라디오버튼을 선택하는 경우 상당히 난감한 상황을 겪을 수 있습니다.

    라디오버튼은 위젯의 특성상 키보드에서 화살표를 누르면 이전 혹은 다음 라디오버튼이 자동 선택됩니다.

    그런데 선택되자마자 페이지가 갱신되는 경우 라디오버튼 자체의 초점을 잃을 수 있습니다.

    따라서 한 라디오그룹의 옵션이 여러 개인 경우 화살표를 누를 때마다 초점을 잃게 되므로 의도치 않은 콘텐츠 실행이 지속적으로 발생하게 되는 것입니다.

    라디오버튼은 되도록 옵션을 선택하는 용도로만 사용해야 합니다.

    그러나 부득이 그렇지 못할 경우 접근성 적용을 위해 radioAsButton() 메서드를 만들고 샘플 페이지와 함께 공유를 하게 되었습니다.

    원리는 간단합니다.

    일반적으로 라디오버튼은 접근성을 적용하기 위해 label for 와 함께 사용을 합니다.

    혹은 암묵적 레이블 하위에 라디오 버튼을 둡니다. 따라서 다음과 같은 방법을 사용할 수 있습니다.

    1. 동적으로 페이지가 변경되는 라디오버튼을 구현하는 경우에는 <input type="radio"> 는 display none 으로 숨깁니다.

    2. 각 label 에 role button 속성을 줍니다.

    3. 각 label 에 키보드 접근성을 구현합니다.

    4. 라디오가 체크되면 연결된 label 에 aria-current true 속성을 줍니다.

    위와 같이 구현하면 키보드 사용자는 각 라디오버튼은 접근이 되지 않고 키보드 접근성이 구현된 커스텀 버튼만 접근이 되므로 의도치 않은 콘텐츠 실행을 방지할 수 있습니다.

    아래는 radioAsButton 메서드 설명입니다.

    1. 인자값에는 컨테이너 즉 라디오버튼들이 있는 요소를 줍니다.

    2. 각 레이블과 라디오 인풋은 for 로 연결되어 있어야 합니다.

    위와 같이 적용하면 radioAsButton 인자값으로 준 요소 하위의 라디오, 레이블을 찾아 접근성을 구현하게 됩니다.

    단 레이블에 대한 키보드 접근성은 ariaButton() 메서드를 통해 추가할 수 있으므로 radioAsButton 에는 추가하지 않았습니다.

    샘플 페이지 테스트해보기

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] improveAcessibility.js 파일에 추가된 focusTogetherForMobile 메서드 소개
    Webacc NV 2022-03-08 15:13:10

    모바일 디바이스, 특히 아이폰에서 여러 텍스트 스타일이 하나의 링크로 구성된 페이지를 VoiceOver로 탐색할 때 수많은 초점 분리로 인해 탐색이 비효율적인 경험을 해 보셨을 줄로 압니다.

    해당 이슈는 VoiceOver에서 개선을 해 주 어야 하는 이슈이지만 임시 방편으로나마 해당 유형의 링크 탐색 시 스크린 리더 사용성을 개선할 수 있도록 focusTogetherForMobile() 메서드를 만들어 공유하게 되었습니다.

    해당 메서드의 인자값은 없으며 improveAccessibility.js 를 추가한 상태에서 해당 메서드를 실행해 주기만 하면 됩니다.

    특징은 다음과 같습니다.

    1. 아이폰, 안드로이드 모바일 디바이스에서만 동작합니다.

    2. <a href 속성이 있고 링크 안에 헤딩이나 이미지가 없는 모든 요소에서 내부 텍스트는 다 aria-hidden 으로 숨깁니다.

    3. 대신 aria-label 안에 모든 링크의 텍스트를 포함시킵니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] improveAccessibility.js 파일에 ariaCurrent() 메서드 추가
    Webacc NV 2022-03-03 14:52:00

    페이지가 새로고침되지 않은 상태에서 정렬 옵션과 같이 여러 요소 중 하나가 현재 선택된 상태임을 표시할 때 aria-current 속성을 사용할 수 있습니다. 

    aria-current 에는 page, true, step 과 같은 여러 속성들이 있는데 그 중 특정 요소 하위의 버튼들 사이에서 aria-current 속성이 true/false 로 변경되어야 하는 경우 접근성을 쉽게 적용할 수 있도록 ariaCurrent 메서드를 만들어 공유하게 되었습니다.

    인자 값으로는 aria-current true/false 값들을 품고 있는 요소명을 적어주면 됩니다.

    예: ariaCurrent(documentQuerySelector("#sort > ul"))

    그러면 다음과 같이 동작합니다.

    1. 인자 값으로 들어 있는 요소 하위의 aria-current 속성을 모두 찾습니다.

    2. aria-current 속성을 가진 요소 중 하나를 클릭하면 클릭한 것은 true, 나머지는 false로 설정합니다.

    따라서 해당 메서드를 적용하기 전에 aria-current 속성이 기본 마크업 되어 있어야 하며 모두 false 이거나 하나가 true 여야 합니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] waiAriaListBox() 메서드 추가
    Webacc NV 2022-03-01 15:24:01

    improveAccessibility.js 라이브러리에 waiAriaListBox() 메서드를 추가하여 사용법을 공유합니다.

    '정렬 선택 옵션, 인기순'과 같은 버튼이 있고 버튼을 누르면 다른 옵션 중 하나로 변경할 수 있는 리스트가 표시되는 커스텀 콤보상자 위젯에서 사용할 수 있습니다.

    해당 메서드를 사용하면 위 또는 아래 화살표키로 옵션을 선택할 수 있고 ESC, 탭, 쉬프트 탭을 눌러 옵션 축소 및 기존 버튼으로 초점을 되돌릴 수 있습니다.

    주의: role listbox, role option 마크업은 반드시 하위 옵션이 하나의 리스트로만 구현되고 옵션 리스트만 있을 때 사용하시기 바랍니다. 

    즉 커스텀 콤보박스 하위에 편집창 체크박스 등의 추가 옵션들이 있을 경우는 리스트박스형 위젯으로 접근성 적용을 하는 것은 적절하지 않습니다.

    해당 메서드를 사용하려면 다음과 같은 마크업이 필요합니다.

    1. 버튼과 옵션 리스트 컨테이너는 aria-controls로 반드시 연결되어 있어야 합니다.

    이는 버튼을 눌렀을 때 aria-expanded 속성이 true로 변경되면 그 버튼과 연결된 컨테이너에 들어 있는 옵션으로 초점을 보내기 때문입니다. 

    옵션 컨테이너에 id를 부여하고 aria-controls="id" 형식으로 연결할 수 있습니다.

    옵션 컨테이너에 id를 부여할 때에는 반드시 스타일 또는 aria-hidden 또는 하위에 요소가 추가 삭제되는 곳에 부여합니다.

    2. 옵션을 표시하거나 숨기는 버튼에는 aria-expanded="false", aria-haspopup="listbox"로 마크업을 합니다.

    3. 옵션 컨테이너에서 각 옵션들은 role="option", 옵션을 감싸는 컨테이너는 role="listbox"로 마크업합니다.

    4. 기본 선택된 옵션에는 aria-selected="true", 선택되지 않은 옵션들에는 "false" 입니다. 

    5. ul > li > a 혹은 ul > li > button 같은 구조에서는 ul은 role="listbox", li는 role="none", a 또는 button은 role="option" 입니다.

    위와 같이 마크업을 하였다면 improveAccessibility.js를 로드한 상태에서 다음과 같이 사용할 수 있습니다.

    1. waiAriaListBox() 메서드를 실행합니다.

    2. ariaExpanded() 메서드를 함께 실행합니다. 

    단 ariaExpanded() 메서드는 이미 확장 축소에 대한 구현을 해 놓은 상태라면 추가하지 않아도 됩니다.

    이렇게 하면 다음과 같이 동작합니다.

    1. 버튼을 눌러 aria-expanded 속성이 true로 변경되면 초점을 옵션 중 aria-selected true 요소로 보냅니다. 

    만약 선택된 요소가 없으면 첫 요소로 보냅니다.

    2. aria-selected true 속성에는 tabindex 0, 나머지 요소에는 tabindex -1을 적용합니다.

    aria-selected true 속성이 없으면 첫 번째 요소에 tabindex 0을 적용합니다.

    3. 위 또는 아래 화살표로 옵션을 탐색하게 하고 옵션에서 엔터를 누르면 선택한 옵션이 aria-slected true, 나머지 옵션은 false로 재조정합니다.

    4. 옵션이 사라지면 포커스는 다시 옵션을 여는 버튼으로 보냅니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] ariaExpanded() 메서드 업데이트
    Webacc NV 2022-02-25 15:20:35

    확장, 축소를 하는 버튼에 페이지 로딩 시의 기본 상태를 aria-expanded true 혹은 false 로 마크업을 하고 aria-controls 속성으로 확장 축소 영역과 연결시키면 사용자가 버튼을 누를 때마다 확장 혹은 축소되는 영역의 display 혹은 aria-hidden 속성을 캐치하여 자동으로 aria-expanded true 혹은 false 가 적용되는 스크립트를 소개한바 있습니다.

    이번에 해당 스크립트에 약간의 업데이트가 적용되었습니다.

    확장 축소되는 영역을 캐치할 때 어떤 페이지에서는 display block 속성은 유지한 채 확장되었을 때 하위 콘텐츠가 표시되었다가 축소되면 하위 콘텐츠 자체가 없어지는 형식으로 구현되기도 합니다.

    위와 같은 형식으로 영역이 컨트롤되는 경우에는 기존 ariaExpanded() 메서드로는 캐치를 할 수 없기 때문에 업데이트를 하게 되었습니다.

    따라서 현재는 aria-expanded false 인 상태에서 사용자가 클릭을 했을 때 확장되는 영역에 display block 이면서 하위에 자식 요소드링 있어야 true 로 변경되고 true 인 상태에서 사용자가 클릭을 했을 때 display block 이라도 하위에 자식 요소들이 없어지면 false 로 적용되도록 했습니다.

    물론 true 상태에서 사용자가 클릭했을 때 display 가 none 으로 변경되면 기존과 같이 false 로 변경됩니다.

    따라서 해당 스크립트를 사용할 때는 aria-controls 와 연결시키는 id 를, 확장 축소될 때 display 속성이 변경되거나 혹은 하위 요소가 사라졌다 나타났다 하는 div 에 주어야 합니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] createElementsId 메서드 업데이트
    Webacc NV 2022-02-24 15:52:16

    반복되는 텍스트가 많은 콘텐츠에서 타겟 1들을 순서대로 아이디로 지정하고 타겟2에 aria-controls 혹은 aria-describedby 속성으로 쉽게 타겟 1의 각 아이디를 연결할 수 있는 메서드를 얼마전 공유했습니다.

    해당 메서드가 다음과 같이 업데이트 되면서 사용법이 약간 변경되었습니다.

    1. 이제는 타겟 1의 아이디를 타겟 2, 3, 4와 같이 여러 요소에 aria-describedby 또는 aria-controls 속성으로 연결할 수 있습니다.

    방법은 다음과 같습니다. 

    targetValue2 라는 변수를 만들고 그 안에 아이디를 가지고 aria-descriedby 혹은 aria-controls 로 연결할 값들을 적어 줍니다.

    값은 클래스 아니면 네임입니다. 

    예시: var targetValue2 = ["button__favorite-item", "element-info-qty-minus", "element-info-qty-plus"];

    2. 기존에는 대상 범위를 문서 전체로 자동 지정하고 아이디 역시 타겟밸류1 값을 가져와서 자동 지정했으나 이렇게 하면 여러 오류가 있을 수 있어 엘리먼트 및 아이디는 함수 안에 포함하도록 했습니다.

    따라서 인자값으로는 범위를 지정하는 element, id를 하나씩 생성할 요소(클래스 혹은 네임. 대부분 텍스트를 가지고 있는 네임이나 클래스가 해당될 것입니다), 아이디(입력 시 0, 1, 2 등의 숫자가 하나씩 붙으며 자동 생성됩니다), 변수로 지정한 targetValue2, 마지막으로 aria-describedby 혹은 aria-controls 입니다.

    주의: 해당 라이브러리를 사용하실 때에는 반드시 아이디를 지정하는 요소와 해당 아이디를 바탕으로 aria-controls 혹은 aria-describedby 로 지정하는 요소의 개수가 반드시 동일해야 합니다. 

    다음은 예시 입니다.

    var targetValue2 = ["button__favorite-item", "element-info-qty-minus", "element-info-qty-plus"];
    var container = document.querySelector("#contentRegion");
    createElementsId(container, "element-info-name", "a11y", targetValue2, "aria-describedby");

    참고: targetValue2에 들어가는 요소, 즉 aria-describedby 속성이 들어가는 대상이 단 하나인 경우는 굳이 targetValue2에 대한 변수를 만들지 않고 해당 클래스명 혹은  name 속성만 넣어 주시면 됩니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] improveAccessibility.js 파일에 setAsModal 메서드 추가
    Webacc NV 2022-02-22 15:10:17

    기존에 널리 아티클로도 공개하였고 improveAccessibility.js 파일에 추가되어 있는 modalDialog 메서드와 더불어서 setAsModal 메서드를 하나 더 추가하게 되었습니다.

    기존 modalDialog 메서드는 메서드를 선언한 후부터 특정 조건을 만족할 때 동작하는 메서드라면 setAsModal 메서드는 모달 대화상자가 표시된 상태에서 바로 모달 접근성을 적용해야 할 때 사용할 수 있습니다.

    따라서 대화상자가 표시된 영역 엘리먼트를 인자 값으로 주면 됩니다.

    예: setAsModal(document.querySelector("#BaseContainer"));

    1. 엘리먼트는 대화상자가 사라질 때 스타일 혹은 속성의 변화가 있는 곳이어야 합니다.

    혹은 엘리먼트 자체가 대화상자가 사라질 때 같이 사라지는 경우도 캐치가 가능합니다.

    2. 해당 메서드를 선언하면 해당 엘리먼트의 속성이 변경될 때까지만 모달 접근성이 동작하게 됩니다.

    3. 대화상자 내부에 class="firstTab" class="lastTab" class="closeModal" 속성은 반드시 필요합니다.

    firstTab 은 대화상자 내에서 탭 키를 통해 첫 번째 포커스 되는 요소이며 lastTab은 마지막 요소, closeModal은 ESC를 눌렀을 때 동작하는 대화상자를 닫는 엘리먼트에 추가합니다.

    단 초점이 가는 요소가 단 하나라면 lastTab은 없어도 됩니다.

    4. 대화상자가 표시되었을 때 해당 메서드를 실행하면 class="firstTab" 요소에 자동 포커스 됩니다.

    5. 대화상자를 닫을 때에는 포커스가 가야 할 요소를 지정해 주어야 합니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] improveAccessibility.js에 ariaTab() 메서드 추가
    Webacc NV 2022-02-21 18:22:41

    WAI-ARIA 탭 컨트롤 적용 시 마크업만으로 키보드 관련 접근성을 적용할 수 있는 스크립트를 추가합니다.

    기존 improveAccessibility.js 파일을 다운받으면 ariaTab() 메서드가 추가되어 있습니다. 해당 메서드를 사용하면

    1. 탭키를 누르면 선택된 탭에만 초점 이동.

    2. 오른쪽 왼쪽 화살표로 탭 전환.

     

    사용하려면 다음과 같이 마크업을 합니다.

    1. <ul> <li> 하위에 <a> <button> 등의 구조일 경우 li에는 role none 속성을 추가합니다.

    2. <ul>에는 role tablist, 각 탭에는 role tab을 추가합니다.

    tab을 div가 감싸고 있으면 div 에 role tablist 를 추가합니다.

    3. 페이지 로딩 시 기본적으로 선택된 탭에는 aria-selected true 속성을, 선택되지 않은 탭에는 false를 추가합니다.

    4. 탭과 연결된 본문 컨테이너에 id를 주고 선택된 탭에 aria-controls로 id를 연결합니다.

    5. 탭을 전환하여도 초점이 유지되는 경우는 상관이 없으나 탭을 전환하는 순간 초점을 잃어버리는 방식으로 페이지가 갱신되는 경우에는 반드시 role tablist와 함께 data-mode="aria1.2" 속성을 추가해 주시기 바랍니다. 이렇게 하면 탭 사이를 화살표로 이동해도 포커스만 이동할 뿐 탭이 전환되지 않게 됩니다.

    improveAccessibility.js 다운받기

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] improveAccessibility.js에 setAriaHiddenExceptForThis 함수 추가
    Webacc NV 2022-02-18 19:51:50

    improveAccessibility.js에 포함된 여러 함수 중 하나인 modalDialog()를 사용하면 마크업 조건을 충족할 경우 모달로 지정한 줄기를 제외한 나머지 모든 영역에는 aria-hidden="true"가 자동으로 추가되는 함수가 포함되어 있습니다.

    해당 함수를 별도로 사용할 수 있도록 setAriaHiddenExceptForThis 함수를 글로벌로 추가하여 improveAccessibility.js를 업데이트 했습니다.

    모달 대화상자 접근성 적용시 다른 부분은 잘 적용되어 있는데 aria-hidden 관련 부분만 적용하지 못했을 경우 사용할 수 있습니다.

    인자값은 aria-hidden true에서 제외해야 할 요소(element)입니다.

    예: setAriaHiddenExceptForThis(document.querySelector("#containerLayer"));

    위와 같은 형식으로 모달 대화상자가 열린 시점에 적용합니다.

    해당 인자값으로 참조해야 하는 요소는 대화상자를 포함하면서 모달이 사라질 때 display: none으로 변경되는 컨테이너 요소를 사용하면 됩니다.

    혹은 해당 요소 자체가 노드 자체에서 사라져도 캐치를 합니다.

    그러면 display none으로 해당 요소가 변경되거나 아예 사라지면 자동으로 기존 aria-hidden true로 설정된 모든 요소들은 다시 원래대로 적용시키게 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] improveAccessibility.js 파일에 추가된 createElementsId 메서드 소개
    Webacc NV 2022-02-16 14:18:58

    뮤직 리스트, 상품리스트 등의 리스트 형식으로 되어 있는 페이지에는 각각의 아이템마다 재생, 삭제, 찜하기 등의 반복되는 레이블을 가진 버튼들이 표시되는 경우가 만습니다.

    이 버튼들의 레이블은 다 동일한 삭제, 재생, 찜하기 등일 것입니다.

    그런데 스크린 리더 사용자가 버튼 단위로 이동할 경우 버튼 레이블이 다 동일하므로 어떤 요소에 대한 삭제, 어떤 요소에 대한 재생 등인지를 바로바로 파악할 수 없는 문제가 있습니다.

    이를 해결하는 방법 중 하나가 각 버튼에 aria-describedby 속성을 통해서 연관된 텍스트와 연결하는 것입니다.

    그러면 버튼 단위로 이동하거나 탭키를 눌러 요소 단위로 이동할 때 마치 힌트처럼 연결된 텍스트를 읽어주게 됩니다.

    문제는 각각의 텍스트에 id 속성이 있어야 한다는 것입니다.

    그래서 기존에 id 속성이 없는 텍스트에 aria-describedby 속성을 위해 id를 생성하는 함수를 만들어 공유하게 되었습니다.

    함수 이름은 createElementsId 이며 인자값으로는 3가지 요소가 들어가게 됩니다.

    1. targetValue1: id를 생성해야 할 텍스트의 class 네임이나 name 속성값을 넣습니다.

    2. targetValue2: 만들어진 각 id를 순서대로 aria-describedby 속성으로 매칭시켜야 할 요소의 class 네임 혹은 name 속성을 넣습니다.

    3. ariaProperty: aria-describedby 혹은 aria-controls 속성 중 하나를 넣습니다.

    이렇게 하면 해당 페이지의 targetValue1, 2를 찾아 aria-describedby 혹은 aria-controls로 연결하게 됩니다.

    단 id로 지정해야 할 요소와 연결할 요소의 개수는 반드시 동일해야 합니다.

    아래는 해당 함수를 적용한 예시입니다.

    createElementsId("item_name", "sd_favorite", "aria-describedby");

    해당 함수는 improveAccessibility.js 를 다운받아 사용할 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] UITextField 요소를 VoiceOver에서 버튼으로 읽도록 해야 할 때
    Webacc NV 2022-02-05 10:12:19

    UITextField는 사용자의 입력값을 받을 때 사용되는 텍스트필드 요소입니다.

    따라서 당연하게도 UITextField 객체를 사용하는 순간 사용자가 해당 요소에 초점을 맞추면 VoiceOver에서는 입력하려면 이중탭하라는 힌트 메시지를 자동으로 출력합니다.

    그런데 상황에 따라서는 UITextField를 버튼으로 읽도록 해야 하는 경우가 있습니다.

    개발 시에 특정 값이 텍스트필드로 들어오게 한 다음 텍스트필드를 탭하면 특정 기능이 실행되도록 하는 경우가 있기 때문입니다.

    이런 경우에도 VoiceOver에서 텍스트필드로 읽는다면 스크린 리더 사용자는 데이터를 입력하는 요소로 생각하게 될 것입니다.

    이쯤 되면 접근성을 아시는 분들은 해당 객체에 accessibilityTraits = .button을 사용하면 될 것이라고 생각할 수 있습니다.

    그러나 안타깝게도 그렇게 하면 버튼 텍스트필드라고 읽어주고 입력하려면 이중탭하라는 힌트 메시지를 여전히 출력합니다.

    따라서 순수한 버튼으로 읽도록 하려면 accessibilityTraits 안에 staticText와 button을 함께 추가해 주어야 합니다.

    예: nameTextField.accessibilityTraits = [.staticText, .button]

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] improveAccessibility.js 파일에 radio.js 함수 추가
    Webacc NV 2022-02-03 19:51:41

    널리에서 지난 9월에 배포한 WAI-ARIA UI js 기능 중 radio/js 파일을 일부 업데이트하고 improveAccessibility.js에 ariaRadio() 함수를 추가하게 되었습니다.

    커스텀 라디오버튼에 다음과 같이 WAI-ARIA 속성을 추가하고 ariaRadio() 함수를 사용하면 네이티브 라디오와 유사하게 키보드 접근성이 구현됩니다. 

    1. 라디오버튼에는 role="radio" 속성 추가.

    2. 체크된 라디오버튼에는 aria-checked="true", 체크되지 않은 라디오버튼에는 false 추가.

    디폴트로 체크된 라디오버튼이 없는 경우에는 모두 aria-checked="false"로 마크업하면 됩니다.

    3. 사용 불가한 라디오버튼이 있다면 해당 요소에 aria-disabled="true" 속성 추가.

    참고: 각 라디오그룹들은 라디오를 품고 있는 요소에 role="radiogroup" 속성을 추가하여야 합니다.

    각 radiogroup에는 aria-label 혹은 aria-labelledby 속성을 통하여 각 라디오그룹의 접근성 네임을 지정할 수 있습니다.

     

    마크업이 완료되었다면 DOM이 다 불러와진 상태에서 ariaRadio() 함수를 추가하면 다음이 적용됩니다.

    1. 탭키로는 선택된 라디오버튼 하나만 초점 제공되고 화살표로 라디오버튼 선택.

    선택된 라디오버튼이 없으면 첫 번째 라디오버튼에 초점이 제공됩니다.

    2. 오른쪽과 아래쪽 화살표는 다음 라디오버튼, 왼쪽과 위쪽 화살표는 이전 라디오버튼으로 포커스 되고 aria-checked 속성이 true로 변경되며 클릭 이벤트를 보냅니다. 또한 라디오버튼 사이를 순환하여 첫 라디오버튼에서 왼쪽 혹은 위쪽 화살표를 누르면 마지막 라디오버튼으로, 마지막 라디오버튼에서 오른쪽 혹은 아래쪽 화살표를 누르면 첫 번째 라디오버튼으로 이동됩니다.

    3. aria-disabled="true" 속성이 있으면 해당 요소에는 aria-checked="true"로 변경되지 않습니다.

    업데이트된 improveAccessibility.js 다운받기

    댓글을 작성하려면 해주세요.
  • tip
    [mobile web] 텍스트 본문 마크업 시 <p> 혹은 role="paragraph"의 중요성
    Webacc NV 2022-01-26 10:25:12

    뉴스기사와 같이 긴 본문의 내용을 읽을 때는 스크린 리더에서 제공하는 연속 읽기 기능을 사용하는 경우가 많습니다.

    그런데 TalkBack에서 특정 페이지의 기사 혹은 긴 본문의 내용을 읽을 때 스타일이 분리된 여러 키워드마다 초점이 분리되어 연속으로 읽기가 불편한 경우가 있습니다.

    예를 들어 다음 문장이 있다고 가정해 보겠습니다. 

     

    오늘 아침 8시쯤 서울역에서는 특별한 행사가 있었습니다.

     

    그런데 마크업 시 접근성을 고려하지 않으면 저 문장을 하나의 초점으로 읽지 못하고 다음과 같이 초점이 여러 개로 분리될 수 있습니다.

    오늘 아침

    8시

    쯤 

    서울

    역에서는 특별한

    행사

    가 있었습니다.

     

    위와 같이 초점이 분리되면 아무리 연속 읽기로 본문을 읽는 다 하더라도 초점이 이동되는 시간 때문에 상당한 딜레이가 발생하게 됩니다.

    이러한 원인은 본문의 각 문단을 <p> 태그를 사용하지 않고 <span>과 같은 태그에 스타일을 주었기 때문입니다.

    이렇게 되면 TalkBack 입장에서는 span이 기준이 되므로 모든 span마다 초점을 분리시킵니다.

    따라서 각 문단은 <p> 태그 사용을 권장합니다.

    만약 어쩔 수 없는 경우라면 role="paragraph" 속성을 사용해도 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [android native] java 및 kotlin 접근성 유틸 클래스에 setTooltipText 메서드 추가
    Webacc NV 2022-01-24 16:05:05

    접근성 구현 시 반복되는 버튼에 대해 반복된 대체 텍스트를 제공하는 것은 권장하지 않고 있습니다.

    예를 들어 한 화면에 여러 콘텐츠가 있고 각 콘텐츠마다 구독 버튼이 있다고 가정할 때 구독 버튼에 대한 대체 텍스트는 구독, 유형 정보는 Switch 혹은 ToggleButton, 상태정보는 켜짐/꺼짐 등이 될 것입니다.

    그런데 이렇게 되면 구독이라는 대체 텍스트가 한 화면에 엄청 많아질 것이고 톡백에서 컨트롤 단위로 탐색 시 스크린 리더 사용자가 듣게 되는 레이블은 구독 뿐입니다.

    이를 해결하기 위해서 접근성을 좀 더 꼼꼼히 구현해 주시는 분들은 각 구독 요소에 어떤 콘텐츠의 구독인지를 contentDescription 형태로 함께 추가해 주는 경우도 있습니다.

    그러나 모든 정보를 contentDescription에 포함하는 것보다는 부가정보는 부가정보라는 것을 알 수 있도록 별도로 넣어 주는 것이 사용성을 높일 수 있습니다.

    그래서 setAsTooltipText 메서드를 추가하게 되었습니다.

    이 메서드 사용 시 부가정보는 기본 레이블 및 유형, 상태정보 후에 툴팁 형태로 음성 출력하며 시각적으로는 툴팁 형태로도 보이지 않습니다.

    예: 켜짐, 구독 스위치, 용의 눈물.

    해당 메서드에 필요한 인자 값은 두 개입니다.

    1. view: 어떤 view에 적용을 시킬 것인지를 지정합니다.

    2. textMessage: 레이블 및 요소 유형정보, 상태정보를 다 읽은 후에 알려 주어야 할 텍스트를 넣습니다.

    사용 예시:

    val a11yUtil = AccessibilityKotlin
                a11yUtil.setTooltipText(holder.deleteButton, items[position].toString())

    유틸클래스 자바 다운로드

    유틸클래스 코틀린 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [android native] java 및 kotlin util class에 적용된 setAsRadioButton 접근성 업데이트
    Webacc NV 2022-01-21 09:40:50

    오랜만에 android util class 업데이트를 진행합니다.

    현재 TalkBack에서는 라디오버튼에 초점을 두고 있으면 선택한 라디오버튼이든 선택이 안 된 라디오버튼이든 간에 접근성 힌트 메시지가 다 '전환하려면 이중탭하세요.'입니다.

    그런데 사실상 선택한 라디오버튼은 전환을 할 수 없습니다.

    왜냐하면 라디오버튼은 토글이 아니기 때문입니다.

    즉 힌트 메시지를 잘못 출력하고 있는 것입니다.

    그래서 setAsRadioButton을 적용하면 선택한 라디오버튼에 초점을 두었을 때는 아무런 힌트도 읽지 않도록 했습니다.

    사용법은 기존에 설명한 것과 동일합니다.

    자세한 것은 관련 팁을 참고해 주세요.

    댓글을 작성하려면 해주세요.
  • tip
    VoiceOver에서 HTML title, aria-describedby 속성 처리에 관하여
    Webacc NV 2022-01-20 09:28:44

    iOS 네이티브앱에서는 대체 텍스트를 제공할 경우 accessibilityLabel을 사용하며 힌트 정보가 필요할 경우 accessibilityHint 속성을 사용하여 실행 결과에 대한 메시지 등을 함께 읽도록 하고 있습니다.

    그런데 VoiceOver에 한정적이긴 하지만 웹에서도 title, aria-describedby 속성은 네이티브 앱에서의 accessibilityHint와 같이 처리를 하고 있습니다.

    예를 들어보겠습니다.

    1. 레이블: 인기순.

    2. 힌트: 정렬 방식을 변경하려면 이중탭하세요.

    3. 마크업: <button title="정렬방식을 변경하려면 이중탭하세요.">인기순</button>

    위와 같이 마크업할 경우 iOS 브라우저에서는 VoiceOver 힌트 읽기 기능이 켜져 있는 경우에만 title 속성으로 부여된 메시지를 읽게 됩니다.

    해당 기능은 TalkBack에서는 적용되지 않으며 TalkBack에서는 힌트 메시지 온/오프와 관계 없이 해당 속성을 다 읽습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] 일부 접근성 향상을 위해 만들어진 js 하나로 통합 관련
    Webacc NV 2022-01-19 09:48:42

    지금까지 널리에 업데이트 했던 js 중 다음 js들은 하나로 통합하여 improveAccessibility.js라는 파일로 공유합니다.

    통합한 js: aria-pressed, aria-expanded, aria-hidden, aria-checkbox, announceForAccessibility, modalDialog, roleButton.

    이로 인해 js 사용시 다음과 같이 사용법이 변경됩니다.

    1. 기존 방식: js를 추가하는 것만으로 해당 js에 적용된 접근성 향상 기능이 실행됨.

    2. 현 방식: 사이트에서 DOM이 불러와진 상태에서 해당 통합 js를 추가하고 원하는 기능 실행을 위해 함수 입력.

    3. 사용할 수 있는 함수: ariaPressed(), ariaCheckbox(), ariaExpanded(), ariaHidden(), modalDialog(), screenReaderLive(), announceForAccessibility(message), ariaButton().

    위의 함수를 적용하면 기존에 팁에서 각 js를 소개한 것과 마찬가지로 해당 기능이 실행됩니다.

    예: aria-expanded 속성에 대한 접근성을 적용하려면 ariaExpanded();

    각 함수에 대한 자세한 접근성 향상 기능은 기존 각 js에 대한 설명 글을 확인하시기 바랍니다.

     

    위에서 언급한 함수 외에도 VoiceOver, TalkBack에서 링크 안의 여러 스타일 텍스트 초점 분리 문제 해결, 탭컨트롤 적용시 키보드 접근성 적용 등의 몇 가지 함수들이 포함되어 있는데 이에 대해서는 별도로 소개하도록 하겠습니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [javascript] aria checkbox 기능 업데이트 및 몇 개의 접근성 관련 js 파일 하나로 통합 관련
    Webacc NV 2022-01-18 09:54:28

    널리에서 배포한 WAI-ARIA UI example에 포함된 js 중에 aria checkbox.js 기능을 업데이트 하고 효율적인 관리 및 사용을 돕기 위해 지금까지 소개한 몇 개의 js를 하나의 파일로 통합하게 되었습니다.

     

    aria checkbox 관련 기능 업데이트:

    현재 각각의 커스텀 체크박스에 role="checkbox" aria-checked="true/false" 기본 값이 마크업되어 있으면 사용자가 스페이스키 혹은 클릭을 했을 때 aria-checked value 값이 변경되도록 되어 있으며 전체동의와 같이 하나의 체크박스에 의해 연결된 다른 체크박스가 영향을 받는 경우에는 전체동의 체크박스에 aria-controls="1, 2, 3"과 같이 연결된 체크박스들을 넣어 주면 전체 동의 체크박스를 체크 혹은 해제 시 하위 체크박스들도 영향을 받도록 구현되어 있습니다.

    이번에 업데이트 된 부분은 다음과 같습니다.

    1. 전체동의 체크박스와 연결된 하위 체크박스가 선택 혹은 해제될 때 전체 동의 체크박스 요소의 outerHTML 변화가 감지되면 aria-checked 속성 변경: 이는 사이트에 따라서 하위 체크박스 전체를 선택하면 자동으로 전체 동의 체크박스가 선택되거나 반대로 전체 동의 체크박스가 선택되었더라도 하위 체크박스 하나를 해제하면 자동으로 전체 동의 체크박스가 해제되는 경우 등의 조건이 다양하기 때문에 이를 대응하기 위해 만들어본 것입니다.

    따라서 하위 체크박스 전체가 다 체크되었더라도 전체 동의 체크박스의 스타일 변화가 없으면 aria-checked 속성이 변경되지 않습니다.

    단 전체 체크박스의 변경 여부는 aria-controls, aria-checked 값이 들어 있는 요소의 스타일 변화가 감지될 때만 캐치가 가능합니다.

     

    업데이트 된 js를 사용하려면 improveAccessibility.js 파일을 다운로드받아 스크립트로 불러온 후 ariaCheckbox(); 함수를 사용하면 자동 적용이 됩니다.

    몇 개의 js 통합과 관련해서는 다음 팁에서 다루도록 하겠습니다.

    improveAccessibility.js 다운받기

    댓글을 작성하려면 해주세요.
  • tip
    [자바스크립트] TalkBack 최신버전에서의 aria-pressed 속성 추가지원에 따른 ariaPressed() 함수 추가 업데이트
    Webacc NV 2021-12-31 15:43:17

    2022년 2월 정도에 널리 아티클을 통해 다루겠지만 TalkBack 최신 버전을 이용하면 기존과 달리 aria-pressed 속성이 있는 요소에 포커스 해도 현재 상태, 즉 on/off 상태를 음성 출력해 줍니다.

    따라서 ariaPressed 메서드가 다음과 같이 변경/업데이트 되었습니다.

    1. 안드로이드에서도 aria-pressed 속성을 사용하도록 변경: 기존에는 TalkBack에서 aria-pressed 속성을 이중탭하여 속성 값이 변경되었을 때만 지원하여 role="switch", aria-checked="true/false" 속성으로 변경하였으나 해당 기능은 삭제했습니다.

    2. 안드로이드에서 토글 버튼을 이중탭하여 값이 변경되는 경우 속성에 따라 켜짐, 꺼짐 음성 피드백하도록 추가: 현재는 이중탭하여 속성이 변경되면 TalkBack 자체적으로는 변경된 값을 바로 읽어주지 못하여 스크립트를 통해 읽어주도록 처리했습니다.

    안드로이드에서만 적용되며 iOS 및 pc에서는 스크린 리더 자체에서 지원하므로 추가하지 않았습니다.

     

     

    이 외의 업데이트 된 내용에 대해서는 지난 팁을 참고합니다.

    사용 방법은 ariaPressed() 함수를 적용하는 시점에 마크업된 모든 aria-pressed 속성을 가지고 오게 되며

    각 버튼을 클릭했을 때 스타일의 변화가 있으면 값이 변경됩니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 같은 뷰컨트롤러에서 화면 콘텐츠 전체가 다른 콘텐츠로 교체되었을 때의 초점 관리가 중요한 이유
    Webacc NV 2021-12-30 15:30:47

    접근성 테스트를 하다보면 어느순간 한 손가락 쓸기로 다음 요소를 탐색하려고 하면 화면에는 분명히 다른 요소들이 있음에도 불구하고 바로 직전의 사라진 텍스트를 읽으며 이전 혹은 다음 요소로 이동하지 못하는 것을 발견하는 경우가 있습니다.

    뷰컨트롤러가 실행되어 화면에 콘텐츠가 표시되면 보이스오버는 접근성 API 에서 정보를 받아 접근성 초점이 가능한 객체들을 수집하고 이를 바탕으로 초점이 가능한 여러 요소들을 생성하게 됩니다. 

    이 정보들을 기준으로 여러 제스처를 사용하여 화면의 내용을 음성으로 들을 수 있게 되는 것입니다.

    그런데 화면 변경과 같은 이벤트 없이 한 화면의 전체 콘텐츠가 다른 콘텐츠로 대체되었을 때 보이스오버는 화면이 변경되었음을 인지하지 못할 수 있습니다.

    그래서 스크린 리더 사용자가 화면이 변경되었음을 알지 못하는 것도 문제이지만 한 손가락 쓸기로 화면을 탐색하면 마치 아무 것도 없는 것처럼 퉁퉁 하는 보이스오버 특유의 소리만 출력하고 마지막으로 초점을 가진 요소만 반복해서 읽는 경우가 발생합니다. 

    이런 경우 임의로 액정의 특정 부분을 손으로 터치하면 그때서야 보이스오버는 전체 변경된 콘텐츠 정보를 다시 생성합니다.

    이런 문제가 발생하지 않게 하려면 뷰컨트롤러에서 전체 레이아웃이 변경되었을 경우에는 반드시 screenChanged 혹은 layoutChanged 이벤트를 주어서 보이스오버가 화면이 변경되었음을 인식할 수 있도록 해 주는 것이 중요합니다.

    이것은 웹페이지에서도 동일합니다.

    레이어 팝업이 표시되었을 때 aria-hidden 으로 기존 백그라운드 콘텐츠를 숨겼다고 가정합시다.

    그리고 레이어 팝업쪽으로 초점을 보내주지 않으면 액정을 다시 터치하지 않는 이상 초점이 갇히는 증상이 발생할 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 자바 및 유틸 클래스에 setAsNone 메서드 추가
    Webacc NV 2021-12-13 09:13:50

    여러 번 강조한 것처럼 스크린 리더 사용자는 요소 유형이나 상태정보를 통해서 각 요소의 의미를 파악하게 됩니다.

    그런데 버튼이 아님에도 버튼 클래스를 사용하여 스크린 리더 사용자에게 혼란을 주거나 혹은 RecyclerView 하위의 각 목록 아이템 레이아웃을 구성할 때 버튼 레이아웃 위젯 등을 사용하여 톡백에서 버튼의 순서를 잘못 읽어주는 경우 등의 문제를 발견하는 경우들이 있습니다.

    참고로 후자의 의미를 조금 더 자세히 설명하자면:

    접근성 트리에서는 텍스트 혹은 대체 텍스트와 컨트롤 정보, 상태정보가 하나의 클래스에 들어가 있어야 스크린 리더에서 상태정보, 레이블, 요소 유형 등을 제대로 읽어주게 되지만 버튼 위젯 레이아웃을 사용할 경우 레이아웃 자식으로 텍스트뷰 등이 들어가게 되므로 톡백에서는 '버튼 1'과 같이 읽게 된다는 의미입니다.

    이러한 문제들을 해결하기 위해 자바 및 코틀린에서 사용할 수 있는 setAsNone 메서드를 추가하게 되었습니다.

    인자 값으로는 대상 뷰 객체만 넣어 주면 됩니다.

    그러면 톡백에서 해당 요소 유형을 읽지 않게 됩니다.

    요소 유형은 위에서도 말씀드린 바와 같이 스크린 리더 사용자에게 중요한 정보가 되므로 의미에 맞게 사용하는 것이 좋으며 어쩔 수 없는 상황에서만 해당 메서드를 사용하시기 바랍니다.

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 자바 및 코틀린 유틸 클래스에 커스텀 키패드 터치하여 입력 메서드 추가
    Webacc NV 2021-12-12 12:51:53

    우리 나라에서는 커스텀 키패드를 사용하는 경우가 종종 있습니다.

    안드로이드 11 이상에서 톡백의 설정에 따라 톡백의 고급 설정에 있는 한번 터치하여 입력하기를 활성화 한 경우 커스텀 키패드 역시 손가락을 떼면 바로 입력되도록 하는 메서드를 자바 및 코틀린 유틸 클래스 및 객체에 추가하게 되었습니다.

    메서드 이름은 setAsKeyboardKey 입니다.

    인자 값으로는 키보드로 사용되는 뷰 객체만 넣어 주면 됩니다.

    예: a11y.setAsKeyboardKey(keyButton)

    그러면 안드로이드 버전이 11 이상인지를 체크한 다음 11 이상이면 톡백의 설정에 따라 한 번 입력하는 것을 활성화 하였다면 사용자가 키보드에서 손가락을 떼면 바로 입력이 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [자바스립트] 모바일 스크린리더의 호환성 대응을 위한 mobile.js 추가
    Webacc NV 2021-12-10 09:23:34

    1. iOS 보이스오버에서 특정 상황에서의  <ul> 인식 불가 해결:

    웹페이지에서 <ul> 요소를 만나면 스크린 리더 나름대로 목록 그룹으로 인식하고 이에 대한 정보를 음성 출력합니다.

    그런데 iOS 보이스오버에서는 특정 상황에서 <ul> 요소가 있어도 목록으로 인식하지 못하는 경우가 있습니다.

    대표적인 것이 목록 스타일이 none 으로 설정되어 있는 경우입니다.

    스크린 리더 사용자 입장에서는 목록 역시 하나의 영역이며 여러 목록이 있다면 목록 단위로 이동함으로써 웹페이지에 대한 레이아웃을 조금 더 쉽게 이해할 수 있는 정보가 됩니다.

    게다가 특정 목록에 aria-label 속성을 함께 주면 현재 탐색하는 목록이 어떤 영역인지도 스크린 리더 나름대로 지원해 주기도 하므로 큰 도움이 됩니다.

    해당 js 를 사용하면 보이스오버에서도 어떤 페이지에서나 <ul> 요소를 목록으로 인식하며 특정 목록에 aria-label 속성을 주면 목록 간 이동 시 혹은 해당 영역을 터치할 시에 어떤 목록인지를 자동으로 읽어주게 됩니다.

    2. iOS 보이스오버에서의 타이머와 같이 텍스트가 스크립트에 의해 동적으로 변경되는 요소 대응:

    몇분 혹은 며칠이 남았는지를 스크립트로 구현하고 남은 시간의 텍스트가 수시로 변경되는 경우 보이스오버에서 해당 요소로 초점을 보낼 수 없거나 보내기 어려운 이슈가 있습니다.

    따라서 해당 요소에 timer 라는 속성을 마크업하고 mobile.js 파일을 추가하면 자동으로 timer 라는 속성이 있는 곳에 role="progressbar" aria-valuetext 속성을 추가하여 수시로 변경되는 텍스트 쪽으로 초점을 보낼 수 있도록 할 수 있습니다.

    사용법은 간단합니다.

    아래 js 파일을 <head> 에 <script> 형태로 추가만 해 주시면 됩니다.

    mobile.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [자바스크립트] ariaHidden 메서드 추가
    Webacc NV 2021-12-07 09:38:25

    웹페이지 접근성을 구현할 때 아리아 히든 속성을 사용해야 하는 대표적인 경우는 모달 레이어가 표시되었을 때 화면에서 가려지는 부분을 숨기는 것입니다.

    그런데 이 외에도 확장 축소되는 콘텐츠 역시 디스플레이 난, 블록 속성을 사용하지 않을 때에도 아리아 히든 속성을 사용해야 합니다.

    그렇지 않으면 분명히 버튼은 축소되었는데 스크린 리더에서는 확장 축소와 상관 없이 하위 콘텐츠를 읽게 되고 그렇게 되면 페이지 구조를 파악하기 어렵기 때문입니다.

    또한 하위 콘텐츠가 초점을 가지고 있는 버튼, 링크 등으로 구성되어 있다면 탭인덱스 -1 속성을 함께 주어야 합니다.

    그래서 ariaHidden 메서드를 만들어 해당 팁을 통해 공유를 하게 되었습니다.

    형식은 ariaHidden() 이며 다음과 같이 마크업을 합니다.

    1. 아리아 히든 트루 혹은 폴스가 적용되어야 하는 컨테이너에 아이디를 부여하고 초기 값을 aria-hidden="true" 와 같이 마크업을 합니다. 페이지 로딩 시 초기 값은 반드시 존재해야 하며 해당 영역은 화면에서 보여지거나 숨겨질 때 반드시 스타일의 변화가 있는 곳이어야 합니다.

    2. 해당 영역을 보이거나 숨기는 버튼에 screen-reader-hidden="적용한 id" 형식으로 연결을 합니다.

    그러면 다음과 같이 동작을 합니다.

    1. 페이지가 처음 로딩 되었을 때 screen-reader-hidden 과 연결된 아이디 영역이 아리아 히든 트루라면 하위 모든 초점이 가능한 요소에 탭인덱스 -1 속성이 붙어서 초점이 가지 않게 됩니다.

    2. screen-reader-hidden 속성이 있는 버튼을 누르면 screen-reader-hidden 속성과 연결된 영역 혹은 클릭한 버튼 바로 상위 요소의 스타일 변경이 감지될 경우 아리아 히든 폴스 혹은 트루로 변경되고 탭인덱스 역시 트루일 경우 -1이 붙습니다.

    아리아 히든 트루 폴스의 변경은 연결된 버튼을 클릭하기 전과 비교해서 판단합니다.

    3. 아리아 히든 속성이 있는 컨테이너 내에서 닫기와 같은 요소를 눌러 다시 스타일이 감지되면 반대로 아리아 히든 속성이 다시 트루로, 탭인덱스 -1 속성이 추가되고 초점은 해당 영역과 연결된 기존 버튼으로 돌아오게 됩니다.

    improveAccessibility.js 다운받기

    댓글을 작성하려면 해주세요.
  • tip
    [자바스크립트] aria-expanded.js 파일에 type="checkbox" role="checkbox" 확장/축소 기능 추가
    Webacc NV 2021-12-06 09:32:12

    기존까지 aria-expanded.js 파일에서는 버튼, 롤 버튼에서의 확장/축소 기능에 대한 스크립트를 지원하였습니다. 

    이와 더불어서 이번 업데이트에서는 체크박스에 대한 확장축소 기능을 지원하게 되었습니다.

    특정 체크박스의 경우 체크되면서 하위 요소들이 표시되거나 체크 해제되면서 하위 요소들이 접히는 UI 에서의 접근성을 적용하기 위함입니다.

    따라서 해당 js 파일이 포함된 상태에서 기존 체크박스에 aria-expanded 속성이 포함되어 있고 aria-controls 속성을 통해 표시되거나 숨겨지는 요소와 아이디로 연결된 경우에는 aria-controls 와 연결된 id 요소가 display none 혹은 aria-hidden true 속성으로 변경되면 aria-expanded false, 반대이면 true 로 설정되게 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [자바스크립트] ariaPressed 메서드 기능 업데이트
    Webacc NV 2021-12-03 14:59:41

    널리에서 배포한 WAI-ARIA UI example 항목 중  ariaPressed js 기능을 업데이트하여 공유하게 되었습니다.

    업데이트된 내용은 다음과 같습니다.

    1. 토글 버튼을 클릭했을 때 버튼이나 롤 버튼이 포함된 요소에 아무런 변화가 없을 경우에는 aria-pressed true 혹은 false 값이 변경되지 않도록 했습니다.

    이는 좋아요 등의 버튼을 눌렀을 때 상황에 따라서는 로그인 등의 조건을 만족하지 않을 경우 좋아요 자체가 안 될 수 있기 때문입니다.

    2. 안드로이드 디바이스 사용시에는 버튼이나 role="button" 속성이 무조건 role="switch" 로 변경되고 aria-pressed 속성 대신에 스위치 롤에서 사용해야 하는 aria-checked ture /false 값으로 변경되도록 했습니다.

    이는 안드로이드에서는 톡백의 이슈로 인해 버튼에 aria-pressed 속성을 사용하면 이중탭하여 aria-pressed 값이 변경되었을 때에는 값을 읽어주지만 초점만 갔을 경우에는 현 상태를 알려 주지 못하고 있기 때문입니다. 

    하지만 스위치 롤로 변경하면 전환 선택됨, 전환 해제됨 으로 읽어주게 됩니다.

    사용방법은 간단합니다.

    우선 마크업단에서 버튼, 타입 버튼 롤 버튼 중 하나에 페이지 로딩 시 기본으로 들어가야 하는 aria-pressed true 혹은 false 값을 넣어주고 ariaPressed 메서드를 실행하기만 하면 됩니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [자바스크립트] ariaExpanded 기능 업데이트(아리아 히든 대응)
    Webacc NV 2021-12-02 09:14:48

    기존까지 ariaExpanded 메서드에서는 아리아 컨트롤즈가 참조하는 아이디 영역이 디스플레이 블록, 난 처리되는 것을 감지하여 사용자가 클릭을 했을 때 aria-expanded true 혹은 false 값으로 변경되도록 만들어졌었습니다.

    이번 업데이트에서는 아리아 컨트롤즈가 참조하는 아이디 영역에 만약 아리아 히든 속성이 있으면 트루 혹은 폴스 값을 함께 참조할 수 있도록 업데이트 되었습니다.

    이는 확장 혹은 축소되는 영역을 디스플레이 난 혹은 블록으로 처리하지 않고 화면 상에서만 가려지도록 개발되는 경우 영역이 화면 상에서 숨었을 경우에는 아리아 히든 트루 및 링크 혹은 버튼 등의 초점 가능한 요소가 포함된 경우 탭인덱스 -1 속성을 함께 주게 되는데 이때 ariaExpanded 메서드가 잘 작동하도록 하기 위함입니다.

    따라서 확장되었을 때 아리아 히든 폴스, 축소되었을 때 아리아 히든 트루로 접근성이 고려되어 있다면 해당 js 를 통하여 aria-expanded 속성을 최초 마크업시 한번만 마크업하면 이후에는 상태 정보를 자동으로 변경해줍니다.

    기존과 같이 확장 축소를 담당하는 버튼에 디폴트 aria-expanded true 혹은 false 값을 적용하고 하위 컨테이너에 id를 부여한 다음 이를 aria-controls로 연결합니다.

    improveAccessibility.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [자바스크립트] announceForAccessibility, screenReaderLive() 메서드 공유 및 소개
    Webacc NV 2021-11-30 17:02:38

    웹페이지에서 특정 콘텐츠가 업데이트 될 때 이를 스크린 리더 사용자에게 알려야 한다면 우리는 aria-live 또는 role="alert" 속성을 사용합니다.

    하지만 특정 상황에서는 화면에 없는 메시지를 스크린 리더 사용자에게 알림 형태로 제공해 주어야 하는 경우가 있습니다.

    페이지 전체가 변경되지 않는 상황에서 화면이 변경되었거나 사용자가 입력한 그 무언가가 설정되었을 때 등입니다.

    iOS, android 네이티브앱에서는 화면에 보이지 않는 알림을 스크린 리더 사용자에게 제공하는 공식적인 메서드를 지원하지만 웹에는 공식적인 메서드가 없습니다.

    그래서 화면에 보이지 않는 스크린 리더용 알림 메시지를 제공하기 위한 announceForAccessibility 메서드를 만들어 공유하게 되었습니다.

    아래에서 js 파일을 다운받아 실제 사이트에서 호출한 다음 스크립트에서 해당 메시지를 알려야 할 시점에 announceForAccessibility("날짜 설정됨") 과 같이 추가할 수 있습니다.

    그러면 화면에 보이지 않는 스크린 리더 라이브 영역을 생성하고 스크린 리더가 해당 영역을 인식했을 시점에 다시 사라지게 함으로써 알림 메시지를 자동으로 읽도록 합니다.

    이 외에도 버튼을 클릭했을 때 음소거, 음소거 해제와 같이 버튼의 텍스트가 변경되는 다중기능 버튼의 경우 screenReaderLive() 메서드를 활용할 수 있습니다.

    해당 버튼 태그에 screen-reader-live 라는 속성만 HTML에 넣고 screenReaderLive() 메서드만 실행하면 모든 스크린 리더에서 버튼이 변경되었을 때 텍스트를 읽도록 스크립트가 동작합니다.

    해당 기능은 스크린 리더 호환성을 고려하여 만들어졌기 때문에 아이폰 보이스오버와 aria-label 속성이 포함된 버튼의 경우 pc 스크린 리더에서는 작동하지 않도록 했습니다.

    이는 버튼이 변경되었을 때 읽어주는 방식이 스크린 리더마다 차이가 있기 때문입니다.

    improveAccessibility.js 다운받기

    댓글을 작성하려면 해주세요.
  • tip
    [자바스크립트] modal.js 버그 수정 관련
    Webacc NV 2021-11-29 18:46:40

    일부 모달 팝업에서 대화상자가 사라졌음에도 이를 감지하지 못하는 버그를 발견하여 이를 수정했습니다.

    모달 영역으로 지정된 그 어떤 요소를 클릭했을 때 모달 속성이 디스플레이 난 처리되면 모달 관련 접근성 기능을 없애는 이벤트를 적용했는데 일부 모달에서 모달이 클릭됨을 인지하지 못하는 경우가 있어서 클릭이 아닌 MutationObserver 이벤트를 활용하여 모달의 디스플레이 난 속성을 감지할 수 있도록 했습니다.

    아래의 modal.js 팁에 있는 링크를 통해 최신 파일을 내려받을 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [자바스크립트] aria-expanded.js 버그 수정 관련
    Webacc NV 2021-11-29 10:17:40

    얼마전 aria-expanded.js 업데이트 버전을 올려드렸습니다.

    몇 가지 버그가 있어 아래에 수정 사항을 정리했습니다.

    1. 기존 aria-controls 를 사용하지 않는 aria-expanded 속성에는 영향을 미치지 않도록 수정: aria-controls 속성 없이 aria-expanded 속성 처리를 하는 경우 저희가 업데이트 한 js 를 넣을 경우 에러가 발생했었습니다. 따라서 기존 aria-controls 속성이 없는 aria-expanded 속성에는 영향을 미치지 않도록 수정했습니다.

    다음은 기능추가입니다.

    1. aria-expanded true 로 변경되어 하위 요소들이 비미달 형태로 확장되었을 때 별도 닫기를 제공하는 경우가 있습니다. 이때 닫기를 눌렀을 때에도 aria-controls 로 지정된 요소의 디스플레이 속성을 캐치하여 none 으로 변경될 경우 aria-expanded 속성이 false 로 변경되도록 업데이트 했습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 섹션별 제목 제공
    Webacc NV 2021-11-25 15:25:13

    Android에서 동일한 형식에 내용만 조금씩 다른 반복된 요소를 만들 때, 가장 많이 사용하는 UI 위젯은 목록 형태의 위젯입니다. 목록 형태의 위젯은 오래전에 쓰이던 ListView와 최근에 많이 쓰이는 RecyclerView가 있습니다. 일반적으로 목록 시작 부분에 HeaderView가 제공되어 있는 경우에는 화면에서 보이는 텍스트를 스크린리더가 제목으로 인식하여 읽어주기 때문에 접근성에 문제가 없습니다.

    하지만 상단에 제목이 제공되어 있지 않은 리스트의 경우 스크린 리더가 인식할 수 있는 섹션 제목을 따로 제공해 주어야 합니다. 웹에서는 ul 영역에 aria-label을 이용하여 대체 텍스트를 넣어 주는 것으로 눈에 보이지 않는 목록 타이틀을 제공할 수 있는데 이와 비슷한 방식으로 안드로이드에서는 android:contentDescription를 사용할 수 있습니다. 

    contentDescription를 사용하여 리스트뷰 영역에 대체 텍스트 제목을 제공했을 경우. 해당 목록의 첫 항목으로 스크린 리더의 초점이 이동하는 동시에 contentDescription로 제공된 제목을 읽어 주게 됩니다. 이로 인해 스크린 리더 사용자는 여러 섹션으로 나뉘어진 페이지에서라도 탐색을 통해 각 섹션의 제목을 인식할 수 있게 됩니다. 

    자바:
    RecyclerView myRecyclerView = findViewById(R.id.MyRecyclerView)
    myRecyclerView.setContentDescription("널리 포럼")

    코틀린 변환:
    val myRecyclerView:RecyclerView = findViewById(R.id.MyRecyclerView)
    myRecyclerView.contentDescription="널리 포럼"

    요소에 속성으로 포함하는 경우
    android:contentDescription="널리 포럼"

    댓글을 작성하려면 해주세요.
  • tip
    [자바스크립트] modal.js 업데이트
    Webacc NV 2021-11-23 17:46:22

    얼마전 널리 아티클을 통하여 모달 대화상자 접근성 적용 시 쉽게 적용이 가능한 modal.js 를 만들어 공유했습니다.

    해당 js 에서 다음 사항을 업데이트 하여 글을 쓰게 되었습니다.

    업데이트 내용: 대화상자가 열렸을 때 role="dialog" 속성에 aria-label 또는 aria-labelledby 속성이 없으면 대화상자를 여는 버튼의 텍스트를 aria-label 속성으로 가지고 옵니다.

    따라서 대화상자의 접근성 이름을 지정하지 않더라도 대화상자 타이틀을 대화상자를 여는 버튼의 이름으로 읽어주어 '공유하기 대화상자'와 같이 음성 출력하게 됩니다.

    다만 조금 더 직관적인 대화상자의 레이블 제공을 위해서는 마크업으로 대화상자의 접근성 이름을 지정해 주는 것이 좋습니다.

    modal.js 다운받기

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 자바 및 코틀린 유틸 클래스에 announceToast 메서드 추가.
    Webacc NV 2021-11-23 09:58:48

    접근성 관점에서 무언가 일어난 일에 대해 알림을 줄 때는 톡백이 알림을 말하게 하거나 알림과 관련된 상호 작용이 함께 포함되는 경우 초점을 해당 요소로 보내주는 것으로 접근성 대응을 하게 됩니다.

    안드로이드에서 Toast, SnackBar 클래스를 사용하여 화면에도 보이는 알림을 제공할 때에는 톡백에서 이를 자동으로 읽어주므로 문제가 되지 않지만 화면상에 보이지 않는 알림을 줄 때는 announceForAccessibility 메서드를 사용합니다.

    그런데 announceForAccessibility 메서드에서 알림을 주려면 무언가 화면에 있는 뷰를 참조해야만 합니다.

    만약 특정 상황에서 참조할 뷰가 없다면 announceForAccessibility 메서드를 사용할 수 없으며 type_announcement 라는 sendAccessibilityEvent 를 이용해야 하고 이때 여러 줄의 추가 코드가 필요합니다.

    따라서 어느 상황에서나 단 한 줄의 코드로 톡백 사용자에게 알림을 제공하는 메서드를 만들어 공유하게 되었습니다.

    자바 및 코틀린에서 모두 사용 가능하고 해당 메서드는 announceToast 입니다.

    인자 값으로는 context, 알림을 보내고자 하는 스트링 두 개가 들어갑니다.

    context 가 필요한 이유는 해당 이벤트는 AccessibilityManager 클래스를 활용하는데 해당 클래스를 사용하려면 context 객체가 필요하기 때문입니다.

    따라서 다음 예시와 같이 사용할 수 있겠습니다.

    AccessibilityUtil.announceToast(getApplicationContext(), "This is a test announcement.");
    댓글을 작성하려면 해주세요.
  • tip
    [자바스크립트] aria-expanded.js 업데이트
    Webacc NV 2021-11-22 17:34:28

    널리에서 얼마전 WAI-ARIA UI를 조금 더 간편하게 적용할 수 있는 라이브러리를 공개했습니다.

    해당 라이브러리 중 aria-expanded.js 부분의 접근성 기능을 조금 업데이트 하여 팁으로 가지고 나오게 되었습니다.

    aria-expanded.js 특징은 특정 버튼이나 role="button" 에 true 혹은 false aria-expanded 속성이 있으면 해당 버튼을 누를 때마다 마크업된 속성을 기반으로 true 이면 false 로, false 이면 true 로 변경되는 기능을 합니다.

    해당 js 에서 업데이트 된 사항은 다음과 같습니다.

    1. 참조하는 aria-controls id 요소가 display none 혹은 block 으로 변경되는지를 확인합니다.

    특정 버튼은 눌렀다고 해서 무조건 확장 혹은 축소되지 않고 현 상태가 유지될 수 있습니다.

    따라서 실제로는 확장되지 않았는데 aria-expanded 속성이 true 로 변경된다면 오히려 사용자에게 혼란을 주기 때문에 

    참조하는 aria-controls 아이디 요소의 display 상태를 체크하도록 했습니다.

    따라서 마크업할 때 aria-controls 는 반드시 display block, none 처리되는 요소와 연결해야 합니다.

    2. a 버튼을 확장한 상태에서 b 버튼을 다시 확장하면 이전에 확장된 a 버튼이 축소되는 구조들이 많습니다. 

    따라서 버튼을 클릭하여 true 상태가 되면 다른 aria-expanded 속성과 aria-controls 와연결된 요소들을 검사하여 display none 속성으로 변경된 요소가 있으면 해당 버튼 속성도 false 로 변경합니다.

    업데이트된 aria-expanded.js 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 자바 및 코틀린 라이브러리에 setAsIgnoreSelected 메서드 추가
    Webacc NV 2021-11-18 10:22:01

    접근성 테스트를 하다보면 선택됨의 상태정보를 가지고 있지 않음에도 선택됨 요소를 함께 읽어주는 경우를 보곤 합니다.

    안드로이드에서는 selected true 속성을 통해서 선택됨 상태를 체크하게 되는데 실제 선택됨 상태정보를 가지고 있지 않은 요소에도 개발의 편의성을 위해서 selected 속성을 사용하는 경우가 있습니다.

    알림 보기 아이콘이 있을 경우 알림이 있을 때는 selected true 로 설정되어 있는 것이 하나의 예입니다.

    톡백에서는 selected true 상태이면 무조건 선택됨 정보를 읽기 때문에 스크린 리더 사용자에게 혼란을 주는 경우가 있습니다.

    이를 간편하게 해결하기 위하여 필요시 selected 속성을 톡백에서 읽지 않도록 하는 메서드를 추가하게 되었습니다.

    자바 혹은 코틀린 접근성 유틸 라이브러리를 아래 팁을 참고하여 추가한 다음 setAsIgnoreSelected 메서드를 사용하면 됩니다.

    인자 값으로는 대상 뷰 객체가 들어갑니다.

    예시: AccessibilityUtil.setAsIgnoreSelected(notiView);

    댓글을 작성하려면 해주세요.
  • tip
    [HTML 접근성 리마인드]상태/선택정보, 힌트를 제공할 때 title속성에 의존해서는 안 되는 이유
    Webacc NV 2021-11-16 16:32:57

    사용자가 많건 적건, 대한민국에서 사용되는 대표 스크린리더는 총 세가지로, 국산 스크린리더인 엑스비전테크놀로지의 센스리더와 NVAccess의 파이썬 기반 오픈소스 스크린리더 NVDA(Non Visual Desktop Access), Freedom Scientific의 유로 스크린리더 JAWS(Job Access With Speech) for Windows가 있습니다. 그 중 센스리더는 한국인을 위해, 한국 소프트웨어/웹 환경에 맞춰서 제작된 대한민국의 스크린리더로, 뒤에 설명한 외산 스크린리더 두 종류와는 다른 부분 많이 있습니다. 대표적인 사례가 title 속성이라고 할 수 있습니다.

    센스리더에서 title은 Tab키로 객체를 직접 탐색하거나, 가상커서(위/아래 화살표키)로 탐색하거나 모두 같은 결과를 보여줍니다.

    센스리더는 이 두가지 동작 시에 요소, 유형, 상태, 타이틀, 이 모두를 읽습니다. 장점이라고 할 수도 있으나, 우리나라에서는 Sense Reader를 기준으로 접근성을 테스트하기 때문에 문제시되지 않고, 특정 웹사이트에서는 title로 선택정보나 힌트, 상태정보와 같은 스크린리더 사용자에게 전달되야 하는 요소의 중요 정보를 제공했었습니다.

    센스리더의 가상커서로 선택정보가 title로 제공된 링크를 탐색하여 음성 출력 내용을 표시함. 화살표키로 이동해도 선택됨이라는 타이틀을 잘 읽음.

    그럼, 다른 스크린리더는 어떨까요? 우리가 쉽게 접할 수 있는 NVDA를 보면, Tab키로 이동 시에는 이 title을 들을 수 있으나, 가상커서(NVDA에서는 브라우즈 모드)로 탐색할 때는 title정보를 읽지 않습니다. 매우 중요한 정보가 있음에도 듣지 못하는 것이지요. 아래와 같이 말이죠.

    title로 선택정보를 제공한 링크의 가상커서 탐색을 음성 출력 뷰어로 본 모습, NVDA에서는 title을 읽지 못함

    NVDA나 JAWS등 외산 스크린리더에서는 링크에 aria-current라는 속성을 사용하여, 현재 링크, 현재 페이지등을 읽게끔 할 수 있으나, 센스리더는 아직은 이 기능일 지원하지 않는 문제가 있습니다.

    그래서 현재로서 두 스크린리더를 만족시킬 수 있는 가장 최선의 방법은 ir기법을 사용하여, 링크 안에 스크린리더로만 들을 수 있는 보 하지만, 이것 또한 만능은 아닙니다. 링크가 아닌 버튼 등을 눌렀을 때, 바로 바로 변경된 텍스트를 알려주지는 않기 때문이지요.  aria-label을 사용하는 것 또한 방법이기는 하나, 부가레이블을 제공하는 것이 아니라, 텍스트를 덮어 씌우는 속성 특성상, 꺼림직한 부분이 있습니다.

    다만, 링크 요소만큼은 이 방법이 최선이라고도 할 수도 있습니다. 링크를 누르면 필연적으로 페이지를 다시 불러오게 되고, 사용자는 다시 해당 영역을 탐색해야 하기에 스크린리더가 부가정보를 업데이트하여 읽을 필요성이 없기 때문입니다.

    최근에도 아직 많은 사이트에서 title에 선택정보를 제공하고 있음을 보았습니다. 그래서 이번 시간에는 리마인드 차원에서 title 사용에 주의하자는 내용으로 팁을 작성합니다.

    아래는 스크린샷에 사용된 마크업입니다.

    <!DOCTYPE HTML>
    <html lang="ko">
    
    <head>
    <title>Document</title>
    ...
    <style>
    .mark{
        display:none;
    }
    .selected .mark{
        display:inline;
        color:red;
    }
    </style>
    </head>
    
    <body>
      <p><a href="#">page1</a></p>
      <p><a href="#" title="선택됨" class="selected"><span class="mark" aria-hidden="true">></span>page2</a></p>
      <p><a href="#">page3</a></p>
    </body>
    
    </html>
    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 컨테이너 단위 접근성 포커스 이동순서 재조정과 vertical scroll bar 이슈
    Webacc NV 2021-11-16 15:01:18

    얼마전 중첩된 레이아웃에서의 접근성 순서 재조정에 대해 다룬 적이 있습니다.

    한 화면의 레이아웃 접근성 초점이 틀어져서 뷰컨트롤러 자신의 accessibilityElements 배열에 각각의 컨테이너들을 레이아웃 순서에 맞게 배열할 경우 대부분 의도한대로 접근성 포커스 순서가 올바르게 조정됩니다.

    그런데 기획에 따라 달라질 수 있겠지만 TableView 내에 수직 스크롤막대가 표시되는 경우 접근성 초점 순서를 재조정했을 때 수직 스크롤막대가 중간에 끼어들어가는 경우에는 한 손가락 쓸기를 해도 다음 요소로 이동이 되지 않는 버그가 있습니다.

    예를 들어보겠습니다.

    하나의 화면에 3개의 컨테이너가 있고 상단 헤더뷰, 중간 테이블뷰, 하단 푸터뷰가 있다고 가정해 봅시다.

    보이스오버 순서가 중간, 상단, 하단으로 이동하는 문제가 있어 accessibilityElements 배열에 헤더, 테이블, 푸터뷰 순서로 초점 순서를 재조정했습니다.

    그런데 테이블뷰 끝에 수직 스크롤막대가 있을 경우에는 푸터뷰에 있는 요소쪽으로 수직스크롤막대에서 한 손가락 오른쪽 쓸기를 해도 이동을 하지 않는다는 것입니다.

    신기한 것은 반대로 푸터에 있는 첫 번째 요소에서 한 손가락 왼쪽쓸기를 하면 수직스크롤막대로 정상 이동이 됩니다.

    접근성 테스트 시 참고하시길 바라며 해당 이슈는 애플에서도 알고 있는 이슈로 빠른 시간 내에 해결되길 바라봅니다.

     

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 자바 및 코틀린 접근성 적용 라이브러리에 setAsEditTextHint 메서드 추가
    Webacc NV 2021-11-15 17:28:38

    EditText 내에 화면상으로 보여지는 레이블 메시지를 추가할 수 없을 때 톡백에서만 인식 가능한 레이블 메시지를 쉽게 추가할 수 있도록 메서드를 추가했습니다.

    setAsEditTextHint 메서드 안에 대상 뷰와 힌트 메시지 텍스트만 넣어 주면 됩니다.

    예시: accessibilityUtil.setAsEditTextHint(editText, "이름");

    유틸클래스 자바 다운로드

    접근성 유틸 클래스 코틀린 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 자바 및 코틀린 접근성 적용 라이브러리에 드롭다운 요소 유형 추가
    Webacc NV 2021-11-15 11:07:29

    안드로이드에서 Spinner 위젯을 사용하면 톡백에서 드롭다운 즉 웹으로 치면 콤보상자라고 읽어주게 됩니다.

    그러나 Spinner 위젯을 사용하지 않고 커스텀으로 사용하였을 경우 접근성을 적용하여 이를 드롭다운으로 읽어줄 수 있도록 setAsDropdown 이라는 메서드를 추가하였습니다.

    옵션을 변경하는 요소의 경우에는 드롭다운으로 적용할 경우 톡백의 기본 힌트 메시지도 변경하려면 이중탭하세요 로 출력되므로 스크린 리더 사용자에게 조금 더 직관적인 피드백을 줄 수 있습니다.

    사용방법은 setAsDropdown 안의 인자 값에 해당 뷰 객체명만 넣어주면 됩니다.

    유틸클래스 자바 다운로드

    접근성 유틸 클래스 코틀린 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 접근성 적용 라이브러리에 sendToFocusView 메서드 추가(자바 및 코틀린)
    Webacc NV 2021-11-11 10:50:19

    접근성 초점을 특정 뷰로 보내야 할 때 안드로이드에서 흔히 sendAccessibilityEvent 메서드를 사용하도록 안내했습니다.

    그런데 해당 메서드는 특정 상황에서 작동이 안 됩니다.

    1. 해당 이벤트가 적용되어 있으나 스마트폰 볼륨키를 눌러 화면 레이아웃이 변경되었을 때.

    2. importantForAccessibility 메서드를 이용하여 모든 뷰들을 접근성에서 숨겼다가 다시 나타나게 했을 때.

    따라서 이런 모든 상황에 영향을 받지 않게 하려면 performAccessibilityAction 메서드를 초점을 보내고자 하는 뷰에 적용해야 합니다.

    따라서 이를 좀 더 간단하게 적용할 수 있는 메서드를 유틸 클래스 및 객체에 추가하게 되었습니다.

    사용방법은 너무나 간단합니다.

    특정 뷰로 초점을 보내고자 하는 시점에 sendToFocusView 메서드를 적용하며 인자 값에는 대상 뷰 객체명만 넣어주면 됩니다.

    예시: 

    AccessibilityUtil.sendFocusThisView(filterButton);

    접근성 유틸 클래스 자바 다운로드

    접근성 유틸 클래스 코틀린 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 확장 축소 라디오버튼 적용 유틸 추가
    Webacc NV 2021-11-10 12:52:15

    현재 업데이트 하고 있는 안드로이드 접근성 적용 유틸 자바 및 코틀린 라이브러리에 라디오버튼 확장축소 메서드가 추가되었습니다.

    라디오버튼이 체크되는 동시에 하위에 또 다른 요소들이 생겨나거나 해제되면서 축소되는 기능을 가진 요소에 사용이 가능합니다.

    메서드는 expandCollapseRadioButton 이며 인자값에는 뷰 객체와 isChecked 조건 변수가 들어갑니다.

    라디오 버튼이 체크되면서 확장될 때 대상 뷰의 selected 속성이 트루로 설정되고 축소될 때 flase 로 설정된다면 한번 적용하는 것만으로 추가 접근성 적용은 필요치 않습니다.

    그러나 그렇지 않다면 체크되면서 확장되거나 체크 해제되면서 축소될 때 isCheck 변수를 true false 로 변경해 주어야 합니다.

    이로써 지금까지 사용 가능한 메서드로는 버튼, 라디오버튼, 탭, 체크박스, 토글버튼, 확장축소버튼, 확장축소라디오버튼, 톡백 실행여부 체크, 실행하려면 이중탭하세요 힌트 없애기 입니다.

    유틸 클래스 자바 다운로드

    유틸 클래스 코틀린 다운로드

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 버튼 확장 축소 자바 및 코틀린 라이브러리 추가
    Webacc NV 2021-11-09 10:36:40

    현재 자바 및 코틀린 접근성 적용 라이브러리를 꾸준히 업데이트 하고 있습니다.

    오늘은 어제에 이어 확장 및 축소 버튼을 적용하였습니다.

    특정 요소를 이중탭했을 때 하위 항목이 확장되거나 축소되는 버튼에서 적용이 가능합니다.

    적용 방법은 다음과 같습니다.

    1. 다음 예시와 같이 확장 혹은 축소가 적용되는 메서드를 해당 뷰에 추가합니다.

    AccessibilityUtil.expandCollapseButton(fruitButton, false);

    확장되었을 때 대상 뷰에 selected 속성이 추가된다면 별도의 작업이 필요 없습니다.

    그러나 해당 속성이 없다면 다음 예시와 같이 true false 속성을 상황에 맞게 변경해 주어야 합니다.

    AccessibilityUtil.expandCollapseButton(fruitButton, isFruitContainerExpanded);

    코틀린 역시 코틀린 형식으로 객체를 만들어 사용할 수 있습니다.

    해당 메서드를 사용하게 되면 톡백에서 확장 축소 상태를 읽어주며 확장 축소에 대한 커스텀 액션이 자동 추가됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] frequentlyUpdated accessibilityTraits 접근성 구현 시 참고사항
    Webacc NV 2021-11-08 16:16:36

    특정 요소에 보이스오버 포커스가 머무르고 있는 동안 퍼센트가 변경되거나 정보가 수시로 업데이트 되어 중간에 보이스오버 사용자가 이를 바로바로 피드백 받을 수 있도록 하기 위해 사용하는 속성이 바로 frequentlyUpdated accessibilityTraits 입니다.

    그런데 해당 속성을 적용하더라도 텍스트가 변경될 때마다 바로바로 음성 피드백을 주는 용도로는 사용할 수 없습니다.

    이는 해당 속성은 텍스트 정보가 업데이트 될 때마다 읽어주는 것이 아니라 몇 초 간격으로 중간중간에 상황 피드백을 하는 용도이기 때문입니다.

    따라서 만약 보이스오버 초점이 머무르고 있는 동안 특정 텍스트가 변하는 것을 모두 읽어주도록 구현해야 한다면 UIAccessibilityPostNotification .announcement 이벤트를 사용해야 합니다. 

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 접근성 클래스 라이브러리에 isTalkBackOn, removeClickHintMsg 메서드 추가
    Webacc NV 2021-11-08 13:09:01

    위에서 소개한 안드로이드 접근성 적용 클래스 라이브러리에 톡백 감지 및 활성화 하려면 이중탭하세요 라는 클릭 힌트 메시지 없애는 메서드를 추가하였습니다(자바 및 코틀린 모두 해당).

    1. isTalkBackOn(Context context): 조건문에 활용 가능한 메서드로 톡백이 켜져 있을 때 사용 가능합니다.

    예시: if (AccessibilityUtil.isTalkBackOn(context)) {

    주의하실 점은 해당 메서드를 사용하기 위해 다음 예시와 같이 Context 객체를 초기화 해 주어야 합니다.

    예시: Context context = (Context) getApplicationContext();

    코틀린의 경우 

    val accessibilityUtil = AccessibilityKotlin

    과 같이 객체를 만들고 그 안에서 자바와 같은 형식으로 적용하면 됩니다.

    2. removeClickHintMsg(View view): 클릭 리스너 자체를 없애는 것이 가장 좋지만 그렇지 못할 경우 인자 값에 없애고자 하는 뷰를 넣어 주면 활성화 하려면 이중탭하세요 라는 힌트 메시지가 제거됩니다. 

    그러나 가능하다면 클릭 속성 자체를 제거하시는 것이 좋습니다.

    유틸 클래스 자바

    유틸 클래스 코틀린

    댓글을 작성하려면 해주세요.
  • tip
    [android native] AccessibilityUtil 자바 클래스 소개
    Webacc NV 2021-11-06 13:31:10

    이틀전 공개해 드린 AccessibilityUtil 코틀린 객체에 이어서 오늘은 자바 플랫폼에서 사용할 수 있는 유틸 클래스를 소개하려고 합니다.

    코틀린과 마찬가지로 현재는 버튼, 라디오버튼, 체크박스, 토글버튼, 탭 요소에 적용 가능하고 여러 다른 요소들을 업데이트 할 예정입니다.

    다음과 같이 적용을 합니다.

    1. 접근성 적용 자바 파일을 다운받아 프로젝트에 적용합니다.

    자바 파일의 경우 브라우저에서 링크를 누르면 파일이 다운받아지지 않고 자바 파일을 텍스트 형태로 보여주게 되므로 팝업키나 오른쪽 마우스를 눌러 다른 이름 저장을 눌러 다운로드 합니다.

    프로젝트에 포함을 시킬 때는 가장 상단에 있는 패키지 네임은 실제 프로젝트 패키지 네임으로 변경해 주어야 합니다.

    2. 이미지뷰, 텍스트뷰, 레이아웃과 같은 객체에 다음과 같은 접근성 적용이 가능합니다.

    setAsRadioButton, setAsButton setAsTab, setAsToggleButton, setAsCheckbox.

    3. 괄호 안 인자 값으로는 대상 뷰 객체가 기본으로 들어가며 버튼을 제외한 모든 요소에는 true 혹은 false 속성이 함께 들어갑니다. 이는 해당

    요소들은 선택됨 혹은 선택안됨 정보를 가지고 있기 때문입니다.

    예시: AccessibilityUtil.setAsTab(meat, false);

    참고: 대상 뷰는 반드시 텍스트 혹은 대체 텍스트 정보가 들어가 있는 뷰여야 합니다.

    4. 선택됨, 선택안됨 상태는 대상 뷰의 isSelected true/false 값으로 상태가 변경된다면 별도의 작업이 필요 없습니다.
    그러나 대상 뷰에 isSelected 속성이 없다면 속성을 추가하거나 상태정보가 변경되는 시점에 앞에서 가지고 온 객체의 true/false 값 변경 상태를 추가해 주어야 합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 접근성 적용을 통해 스크린 리더 사용자에게 알림 제공하기
    Webacc NV 2021-11-05 12:10:51

    스크린 리더를 통해 주기적으로 업데이트가 되는 영역의 정보를 탐색하여 가져와야 하는 경우, 해당 방식에 대한 접근성이 없어 번거로운 경험을 하게 되는 일이 많습니다. 사용자가 일일이 재탐색하지 않고 영역이 업데이트될 때마다 스크린 리더가 그 내용을 읽어 주는 기능을 접근성에서 넣어 줄 수 있습니다. android에서는 accessibilityLiveRegion을 이용할 수 있습니다. 웹에서 흔히 알고 있는 aria-live의 특성과 비슷합니다. 한 영역을 라이브 영역으로 지정해 놓고, 그 영역에서 동적으로 변경되는 내용이 있을 때마다 스크린 리더가 해당 내용을 읽어 줍니다. 즉 accessibilityLiveRegion의 경우, 시각적으로 변경된 콘텐츠, 텍스트의 상태를 실시간으로 읽어 주는 역할을 합니다. 

    또 다른 형태의 알림 제공 방식으로 announceForAccessibility가 있습니다. accessibilityLiveRegion와는 다르게 시각적으로 보이는 콘텐츠를 그대로 읽어 주는 게 아닌 레이아웃 변경 상태를 스크린 리더 사용자에게 직접적인 메시지를 통해 알려 줍니다. 저울에서, 중량을 표시해 주는 숫자 텍스트 영역을 라이브 영역으로 지정해 두었다고 합시다. accessibilityLiveRegion는 해당 저울에 올라간 물건이 바뀔 때마다 변하는 중량을 읽어 줍니다.  announceForAccessibility의 경우 저울에 무언가 올라가 있다, 혹은 내려가 있다, 구체적으로는 어떤 물건이 어떻게 올라가 있는지까지 등 변화하는 환경의 사실을 알려 주는 용도로 쓰이게 됩니다. 두 메서드를 적절히 이용한다면 접근성에 있어 좋은 해결 방식이 될 수 있습니다. 

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 코틀린 접근성 적용을 위한 유틸 객체, AccessibilityKotlin.kt 소개
    Webacc NV 2021-11-04 17:33:51

    안드로이드 접근성 적용 시 커스텀 컨트롤에 대한 요소 유형 및 상태정보를 조금 더 쉽게 제공할 수 있도록 메서드만 넣어 주면 관련 속성을 자동으로 만들어주는 코틀린 유틸 객체를 만들어 공유하게 되었습니다.

    현재는 버튼, 라디오버튼, 체크박스, 토글버튼, 탭 요소에 적용 가능하고 여러 다른 요소들을 업데이트 할 예정입니다.

    다음과 같이 적용합니다.

    1. 접근성 적용 코틀린 파일을 다운받아 프로젝트에 포함시킵니다. 

    가장 상단에 있는 패키지 네임은 실제 프로젝트 패키지 네임으로 변경해 주어야 합니다.

    2. 실제 변경해야 하는 요소가 있는 코틀린 파일에서 다음 예시와 같이 AccessibilityKotlin 객체를 만듭니다.

    val radio1 = AccessibilityKotlin

    3. 2번에서 만든 객체를 통해서 텍스트뷰, 이미지뷰, 레이아웃뷰와 같은 요소들에 다음과 같이 접근성 적용이 가능합니다.

    setAsRadioButton, setAsButton setAsTab, setAsToggleButton, setAsCheckbox.

    4. 괄호 안 인자 값으로는 대상 뷰 객체가 기본으로 들어가며 버튼을 제외한 모든 요소에는 true 혹은 false 속성이 함께 들어갑니다. 이는 해당 요소들은 선택됨 혹은 선택안됨 정보를 가지고 있기 때문입니다.

    예시: radio1.setAsRadioButton(button1, false)

    참고: 대상 뷰는 반드시 텍스트 혹은 대체 텍스트 정보가 들어가 있는 뷰여야 합니다.

    5. 선택됨, 선택안됨 상태는 대상 뷰의 isSelected true/false 값으로 상태가 변경된다면 별도의 작업이 필요 없습니다.

    그러나 대상 뷰에 isSelected 속성이 없다면 속성을 추가하거나 상태정보가 변경되는 시점에 앞에서 가지고 온 객체의 상태를 변경해 주어야 합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [리마인드] 접근성 관점에서 iOS의 뷰컨트롤러, 안드로이드에서의 액티비티
    Webacc NV 2021-11-03 12:57:55

    아래에 다루는 내용은 예전에 팁을 통해 이미 다룬 적이 있으나 접근성에서 중요한 비중을 차지할 뿐만 아니라 리마인드 차원에서 다시 한번 정리할 필요성이 있다 판단되어 해당 글을 게시하게 되었습니다.

    액티비티, 뷰컨트롤러는 스크린 리더 사용자에게 화면 전환에 대한 정보를 주는 역할을 합니다.

    뷰컨트롤러가 변경되면 보이스오버에서 자체적으로 다른 화면으로 전환되었다는 사운드를 재생시킵니다.

    액티비티가 변경되면 톡백에서 각 액티비티 타이틀을 읽는 동시에 사운드를 출력함으로써 다른 화면으로 전환되었음을 알립니다.

    위의 전제를 가지고 생각했을 때 접근성 관점에서 다음과 같이 정리를 할 수 있겠습니다.

    1. 화면이 전환되는 경우에는 다른 뷰컨트롤러, 액티비티 사용을 권장한다.

    2. 안드로이드에서 액티비티 타이틀은 화면의 표시 유무와 관계 없이 각 화면의 제목을 적절하게 삽입한다. 액티비티 레이블은 매니페스트의 activity)label 혹은 액티비티 클래스 내에 setTitle 메서드를 사용하여 삽입할 수 있다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 테이블뷰 하나의 셀 내의 여러 텍스트를 각각의 초점으로 제공해 주어야 할 때
    Webacc NV 2021-11-01 14:25:31

    하나의 테이블뷰 셀에 여러 콜렉션뷰들이 있고 각 콜렉션뷰 안에는 UILabel 텍스트가 있다고 가정해 봅시다.

    지난 팁에서 공유한 바와 같이 한 셀에 여러 텍스트가 있으면 하나의 초점으로 제공될 것입니다.

    이는 콜렉션뷰 자체는 보이스오버의 접근성 초점과는 의미가 없기 때문입니다.

    그런데 각 텍스트가 버튼이 아니라 별개의 텍스트임에도 별도로 접근성 초점을 제공해야 하는 경우 어떻게 해야 할까요?

    방법은 간단합니다.

    해당 셀의 accessibilityElements 를 셀이 아닌 콜렉션뷰로 변경해 주면 됩니다.

    예: self.accessibilityElements = [self.collectionView]

    이렇게 되면 보이스오버의 접근성 초점은 셀이라는 것을 거치지 않고 바로 콜렉션뷰로 가게 되므로 각 텍스트의 초점이 별도 분리됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 맥락상 분리되어야 하는 TextView 초점이 하나로 합쳐져 있을 때 점검사항
    Webacc NV 2021-11-01 09:32:58

    접근성 진단을 하다보면 분명히 텍스트뷰는 두 개인데 초점이 하나로 합쳐져서 맥락상 맞지 않는 경우를 발견하는 때가 있습니다.

    예를 들어보겠습니다.

    가는날, 11월 1일, 오는 날, 11월 2일 이라는 요소들이 있고 쉼표를 기준으로 뷰들이 나누어져 있다고 한다면 총 뷰들은 4개 입니다.

    각 날짜는 이중탭하여 날짜 변경이 가능한 뷰로 되어 있다고 가정하겠습니다.

    그런데 톡백으로 탐색해 보면 가는날 오는날, 11월 1일, 11월 2일과 같이 두 개의 텍스트뷰가 하나이 초점으로 탐색되는 경우가 있습니다.

    이때 우리는 해당 오류 내용을 분석하기 위해 디버깅을 하게 되는데 두 가지 경우를 유심히 볼 필요가 있습니다.

    1. 4개의 뷰를 감싸고 있는 상위 뷰에 focusable 속성이 true 로 되어 있거나 클릭 리스너가 포함되어 있지 않은지: 상위의 focusable 속성이 true 이거나 클릭 리스너가 있으면 하위에서 별도 포커스 및 클릭 속성이 없는 요소들은 하나로 통합됩니다. 만약 1번의 경우라면 이 문제를 해결하기 위해 상위 뷰의 클릭, focusable 속성을 제거해야 합니다.

    2. onCreateViewHolder 메서드를 사용하여 해당 영역을 뷰그룹으로 묶은 경우: 흔히 목록뷰와 같은 RecyclerView, ListView 등에 많이 사용되는 방법으로 이런 경우에도 클릭, focusable 속성이 별도로 없는 텍스트뷰는 초점이 하나로 합쳐지게 됩니다. 만약 이 상황에서 초점을 분리시키려면 각 텍스트뷰에 focusable true 속성을 주어 마치 다른 포커스를 가진 뷰처럼 만들어 주어야 합니다. 다만 이렇게 하면 하드웨어 키보드로 탐색 시 탭키를 눌렀을 때 각 텍스트뷰가 포커스 되는 문제는 있겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 접근성 포커스 캐치하여 SeekBar 볼륨키로 조절 가능하게 하기
    Webacc NV 2021-10-30 18:01:43

    2020년, 오류 유형별 모바일 접근성 해결방안을 통하여 안드로이드 뮤직 플레이어에서의 SeekBar 접근성 개선 방안에 대해 설명한 적이 있습니다.

    슬라이더를 구현할 때 onStopTrackingTouch 이벤트가 감지되어야 슬라이더가 조절되도록 개발되는 경우가 대부분이므로 이에 대한 접근성을 해결하기 위해 접근성 서비스가 감지되었을 때에는 해당 메서드 없이도 슬라이더를 조절할 수 있게끔 하는 방법이었습니다.

    그런데 접근성 서비스가 켜져 있는지를 감지하는 것보다 조금 더 효율적인 방법이 있는데 바로 접근성 초점을 캐치하는 것입니다.

    접근성 서비스 실행 여부를 캐치하는 것도 좋은 방법이지만 실행 여부만 캐치할 경우 액티비티가 실행된 이후에 접근성 서비스가 켜지는 경우에는 해당 메서드 반영이 되지 않습니다.

    예를 들어 저시력 사용자가 뮤직 플레이어 화면에 들어와서 톡백을 실행하는 경우에는 접근성 서비스 실행 시 구현된 이벤트가 동작하지 않는다는 의미입니다.

    물론 접근성 서비스 실행 여부와 더불어 실시간으로 접근성 서비스가 켜지거나 꺼지는 것을 감지하는 accessibilityStateChangeListener 이벤트를 함께 사용할 수도 있지만 이렇게 되면 그만큼 코드가 길어지게 됩니다.

    따라서 톡백의 초점이 해당 슬라이더에 있을 때에는 onStopTrackingTouch 이벤트를 적용하지 않도록 하는 방법이 훨씬 코드 양도 줄이고 톡백을 언제 실행하더라도 바로바로 적용되므로 효율적입니다.

                public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                    isFromUser = b;
                    value1 = i;
                    value2 = b;
                    if (mSeekBar.isAccessibilityFocused()) {
                        if(mPlayer!=null && value2){
                            mPlayer.seekTo(value1*1000);
                        }
                    }
                }
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [HTML5 접근성 기본 리마인더] 시멘틱 영역 유형(랜드마크)와 레이아웃
    Webacc NV 2021-10-28 09:13:32

    오래전에 WAI-ARIA 아티클에서 랜드마크에 대해 다룬 적이 있습니다. 하지만, 호랑이 담배 피던 시절처럼 너무 오래된 것 같습니다.

    이번 팁은 랜드마크에 대해 다시 짧게 알아보는 시간을 가지도록 하겠습니다.

    웹에도 랜드마크가 있어요? 랜드마크가 뭐예요?

    랜드마크, 상징적인 건물이나 조형물, 환경, 기타 등등, 지역이나 공간을 대표하거나, 정체성 중 하나인 요소를 말합니다.

    어떤 것들을 랜드마크라고 부르시는지는 대부분 아시겠지만, 미국에 가면, 자유의 여신상, 더 좁게는 뉴욕에 가면, 멘헤튼의 엠파이어 스테이트 빌딩이 떠오릅니다. 

    서울하면, 경북궁같은 조선시대 궁궐, 현대에 와서는 63빌딩이나 롯데타워 같은 높아서 유명한 건물들도 랜드마크죠. 경주하면 불국사의 역사적인 여러 명소와 건축물이 떠오릅니다.
    이렇게, 특정 지역 하면 수학공식처럼 떠오르는 상징적인 것을 랜드마크라고 합니다.

    HTML5 기반의 웹에도 이런 상징적인 역할을 부여하는 랜드마크 요소가 있습니다.

    웹 랜드마크, 어떤 것이 있나요?

    우리가 웹에서 가장 먼저 보게 되는 것부터 얘기하도록 하지요.

    header 태그는 일반적으로 웹페이지의 대 제목(h1) 로고가 들어갑니다. Windows나 Mac의 창 위에 제목 막대가 있는 것처럼 말이죠.
    그리고, 간편하게 사용자가 자주 사용하는 기능, 링크 등을 노출해 놓기도 합니다.

    WAI-ARIA 명세에서는 role="banner"을 사용하여 header 태그를 대신하는 div를 만들 수 있습니다. 우스갯소리지만, role 명칭이 banner로 돼 있어서, 간혹 국내에서 캐러셀과 혼동하기도 합니다.

    다음은 nav태그입니다. 높은 확률로 header 태그 다음으로 보게되는 영역입니다. navigation의 앞 세자로 줄여서 만든 태그로, 사이트를 탐색하는 링크들을 모아놓는 역할을 합니다.

    GNB, LNB라는 약자를 보신 적 있거나, 사용하시고 계실 것입니다. 이는 Global Navigation Bar와 Local Navigation Bar의 약자로, div가 아니라 nav태그를 사용하면 조금 더 영역에 의미를 부여할 수 있고, 
    스크린 리더 사용자는 랜드마크로 활용할 수 있게 됩니다.

    그 다음 주로 나오는 것은 main태그이며, 본문 영역을 나타냅니다. role 또한 동일한 role="main"값을 사용하며, 맨 마지막으로 페이지의 맨 아래에 배치되는 회사 정보 영역은 footer 태그를 사용하며 role으로는 role="contentinfo"로 푸터 영역임을 나타낼 수 있습니다.

    이외에도 독립적인 각각의 글에 사용되는 article 태그(role="article")가 있으며 대표적으로 페이스북의 각 포스팅이 이 article 영역으로 이루어져 있습니다. 그리고 연속되고 연관된 내용을 영역으로 나누는 section(role="region") 태그가 있습니다(예: 각 책의 장, 절 등. 또는 카테고리 레이아웃 ).

    section 태그 혹은 role="region" 랜드마크는 반드시 aria-label 혹은 aria-labelledby 속성으로 영역 이름에 대한 레이블을 지정해 주어야합니다.

    이 외에도 보조 영역을 표시할 때 사용하는 role="complementary", 검색 영역임을 알려줄 때 사용하는 role="search" 랜드마크가 있습니다.

    이렇게, 기존에 div의 클래스만으로 구분해 놓았던 것들을 시멘틱 태그로 교체하거나, 시맨틱 태그를 대체하는 role만을 제공하더라도 스크린리더 사용자에게는 페이지를 이해하는 데에 많은 도움이 됩니다. 레이아웃 구성 시 시멘틱 태그를 잊지말고, 사용하는 것을 실천합시다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 보이스오버 초점이 테이블뷰 내부에 있을 때의 데이터 재구성 관련
    Webacc NV 2021-10-27 09:40:10

    테이블뷰 내부의 요소중 하나를 탭하여 실행하면 테이블뷰 전체의 데이터를 재구성(reload)하는 경우가 있습니다. 

    이 때 보이스오버 초점이 이중탭한 요소에 머무르지 못하고 다른 요소로 초점이 튀어버리는 문제가 발생합니다. 

    따라서 이런 경우 아래 코드 예시와 같이 보이스오버 초점이 실행 가능한 요소에 있을 때는 reloadData 메서드 대신 reloadItems 메서드를 사용하여 데이터가 변경되는 요소만 재구성하는 것이 필요합니다.

    해당 이슈에 대한 자세한 설명은 11월에 발행될 널리 아티클을 참고하시기 바랍니다.

    여기서는 관련된 코드 예시를 첨부합니다.

    // 보이스오버 초점을 가지기 시작했을 때와 잃었을 때의 값을 가지고 오기 위한 변수를 만듭니다.
        var isVoiceOverRunning: Bool = false
        
        override func accessibilityElementDidBecomeFocused() {
    // 보이스오버 초점을 가질 때의 값을 가지고 오기 위한 메서드입니다.
            self.isVoiceOverRunning = true
        }
        
        override func accessibilityElementDidLoseFocus() {
    // 보이스오버가 초점을 잃었을 때의 값을 가지고 오기 위한 메서드입니다.
            self.isVoiceOverRunning = false
        }
    // 보이스오버 초점을 가지고 있으면 테이블뷰의 전체 데이터를 재구성하지 않고 변경되는 영역만 찾아 재구성시킵니다.
            if cell.isVoiceOverRunning {
                let beforeIndexPath = IndexPath(row: beforeFilter?.rawValue ?? 0, section: 0)
                self.collectionView.reloadItems(at: [beforeIndexPath,indexPath])
            } else {
                            self.collectionView.reloadData()
                self.collectionView.layoutIfNeeded()
                }
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [WEB] 링크와 버튼을 구분하여 사용하기
    Webacc NV 2021-10-22 09:23:33

    부제: 커스텀 버튼을 만드는 것은 시간 낭비이며 삽질에 가깝다

    링크는 현재 웹사이트와 다른 웹사이트를 이어주거나, 웹사이트 내의 다른 영역을 이어주는 역할, 비유하자면 교통수단 역할을 합니다. 그러나, 링크의 수난은 여전히 계속되고 있습니다. 홍길동도 아닌 것이 링크이되 링크로 동작하지 못하는 링크가 많은 웹사이트에 있습니다.

    앞에서 언급했듯, 링크는 페이지와 페이지를 연결하는 웹사이트의 교통수단?같은 존재입니다. 목적지를 관리하는 누군가가 문만 열어놓았다면, 누구나 어디서든 출입할 수 있게끔 해주는 어느 애니메이션의 “어디로든 문” 같은 존재이지요.

    그런데, 많은 웹사이트에서 이러한 링크를 버튼 기능으로 사용하고 있습니다. 오래전부터 링크와 버튼에 대해서 접근성 이슈를 얘기해 왔으나, 아직도 말이지요.

    링크와 버튼은 “눌러서 실행한다” 라는 작동 방법에 있어서 공통분모가 있습니다.

    그래서 일까요? 사람들은 링크의 기본 기능을 무시하고, 버튼으로 사용합니다. Button이라는 요소가 있음에도 말이지요.

    과거보다는 당연히 여러 Front End 개발자가 스크린 리더에 대해 알고 있습니다. 그러나 여전히 많은 개발자는 용도만 같다면 상관 없기에 링크를 버튼으로 둔갑시키고 있습니다.

    그래서? 그게 나쁜 겁니까?

    개발자 입장에서는 나쁜 의도는 하나도 들어가 있지 않습니다. 당연한 애기이겠지요. 그러나, 잘못된 사용으로 인해 고통받는 서비스 이용자나, 그것을 고쳐야 하는 개발자를 포함한 서비스 제공자에게 있어서, 감히 나쁘다고 말할 수 있습니다. 서로가 편리하지 않을 뿐더러, 그렇다고 어느 한 쪽이 편리하지 않습니다. 서로 지고 있는 것이지요.

    그러면 도대체 왜 나쁜 겁니까?

    스크린 리더를 조금이라도 아는 분도 있을 것이며, 모르는 분도 있을 것입니다. a 태그는 a 태그일 뿐, button이 아닙니다. 스크린 리더는 그 점을 여과없이 보여줍니다. a 태그라면 ‘링크’, button이라면 버튼이라고 읽어주지요.

    스크린 리더 사용자는 개발/기획 주체와 서비스 제공자의 생각과 의도를 알 수 없습니다

    먼저 사용자 측면부터 얘기해 보겠습니다. 당연히 스크린 리더 사용자는 컴퓨터를 배울 때, “링크”는 어떤 요소인지 교육을 받습니다. 링크는 누르면 다른 페이지로 이동하는 요소라고 말이지요. 높은 확률로 링크 요소에는 별도의 상태정보를 주지 않은 사이트가 많습니다. 링크에는 이렇다고 할만한 여러 상태 정보를 줄 속성이 없기 때문입니다. 스크린리더 사용자는 당연히 링크 요소이니까, 누르면 막연하게 어느 페이지로 나를 인도할 것이라고 생각하고, 페이지가 불러와질 때 까지 기다릴 것입니다.

    누르면 즉각적으로 스크린 리더를 반응하게 할만한 상태 정보나 초점의 변화와 같은 이벤트가 없기 때문에 스크린리더 사용자는 개발자가 링크를 어떤 요소처럼 사용했는지 이해할 수 없습니다. 반대로, 버튼을 링크의 목적으로 사용하는 것은 허용되나, 링크는 버튼 요소와 철저히 구분되어야 합니다. 링크는 오로지 연결을 위해 만들어진 요소이기 때문입니다.

    개발자와 서비스를 제공하는 기업에게는 어떤 이득이 있습니까?

    자, 이제 사용자 측면만 이야기했으니 웹 문서를 작성하는 사람 입장에서의 단점도 이야기 해 보겠습니다. 링크를 버튼처럼 사용하기 위해서, 가장 많이 사용하는 방법은 두 가지가 있습니다. 링크의 href 값에 “javascript:void(0);”을 넣고, 클릭 이벤트를 넣는 방법과 href=”#”을 넣고 클릭 이벤트를 넣는 방법, 이 두가지가 가장 원시적인 방법이고, 많은 웹사이트에서 사용하는 방법일 겁니다. 이 두 가지는 차라리 나은 방법이라고 할 수 있을지도 모릅니다.  href값을 아예 안 줘서 초점 조차 안 가는 경우도 있으니까요.

    href=”#”을 사용하는 방법은 빈 해시 링크를 생성하는 것이기 때문에 누를 때마다 주소 입력상자 끝에 #이 붙게 됩니다. 보통 이것을 원치 않기 때문에 href=”#”을 쓰는 링크의 클릭 이벤트에는 event.preventDefault()라는 이벤트 객체의 기본동작을 방지하는 메서드를 함께 불러옵니다. Button 태그였다면 필요하지 않았을 작업인데 말이지요. 뭐 그깟 코드 한 줄 추가되는 것 갖고 그러냐 싶겠지만, 비효율적인 것은 비효율적이라고 말할 수 밖에 없지요.

    그리고 앞서, 스크린 리더 사용자는 링크로 받아들여 페이지 로딩을 기다린다는 문제점 때문에 버튼처럼 인식하게 하기 위해 WAI-ARIA의 role 속성을 통해 스크린 리더에게 해당 링크를 버튼으로 읽게끔 합니다. 버튼 태그는 눈으로 보기에 단순해 보이지만, 커스텀 기능으로 구현할 때, disabled와 같은 속성을 일일이 구현해 줘야 하며, Space 키로 활성화하는 등 하지 않아도 될 불필요한 일을 너무 많이 하게 됩니다. 간혹, 앵커 태그가 CSS 스타일링이 쉬워서 앵커 태그를 쓴다는 얘기를 듣기도 하지만, 작은 프로젝트이건, 큰 프로젝트이건, 버튼 태그도 스타일링이 그리 어렵지 않습니다.

    많은 서비스에서 현실적으로 당장의 수정이 어렵기 때문에 a 태그를 유지하되 role=”button”을 구현합니다. 하지만, 이는 정말 지저분하고 비효율적이며, 얘기치 않은 버그를 만들기도 합니다.

    직접 구현하는 경우도 있지만, 일일이 구현하는 것은 시간 낭비이기 때문에 우리는 빠른 개발을 위해서 다른 개발자가 만든 라이브러리를 가져다 씁니다. 하지만, 잘못 사용된 링크 요소 부분을 button 태그로 바꿀 수 있다면 그 라이브러리는 필요치 않게 됩니다. 그리고, 서버에 별도의 추가 스크립트 파일을 두거나, CDN 요청을 덜 해도 됩니다.

    사용자를 위해 button태그를 쓴다고 생각하지 말고, 서비스를 제공하는 기업, 사용자, 모두가 편하기 위한 것임을 생각하고, 링크가 아닌 button이 필요한 순간에는 button 태그를 더 많이 사용했으면 합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 스크린 리더용 영역 정보 제공하기 2탄
    Webacc NV 2021-10-13 18:17:22

    예전에 TableView 에 accessibilityLabel 을 주면 테이블뷰에 접근했을 때 어떤 영역인지 읽어준다는 것을 공유한 적이 있습니다.

    이처럼 화면에 보여지는 제목을 줄 수 없을 때 영역 정보를 주는 것은 레이아웃을 이해하도록 도울 수 있습니다.

    그런데 TableView 가 아닌 UIView, UIScrollView 와 같은 영역에 어떤 영역인지에 대한 정보를 주어야 하는 경우가 있습니다. 

    해당 컨테이너뷰들은 테이블뷰와 달리 accessibilityLabel 만 준다고 해서 영역 정보를 읽어주지 못합니다.

    accessibilityLabel 과 동시에 accessibilityContainerType 을 semanticGroup 으로 재정의해 주어야 합니다.

    다음 예시를 참고합니다.

            scrollView.accessibilityLabel = "banner section"
            scrollView.accessibilityContainerType = .semanticGroup
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 커스텀 액션 네임이 특정 요소에서 추가되거나 삭제될 때
    Webacc NV 2021-10-12 12:56:34

    커스텀 액션을 적용할 때 액션 네임이 항상 고정되어 있는 경우는 별 문제가 없겠지만 특정 액션 네임이 

    상황에 따라 삭제되거나 추가되는 경우 커스텀 액션 적용 시 주의가 필요합니다.

    예를 들어 공유하기 버튼을 커스텀 액션으로 추가했다고 생각해 봅시다.

    그러면 각 요소마다 보이스오버로 접근 시 공유하기 커스텀 액션이 표시될 것입니다.

    하지만 특정 요소에는 공유하기뿐만 아니라 댓글달기 기능도 있다고 생각해 봅시다.

    그러면 댓글 달기는 모든 요소에 있는 것이 아니기 때문에 커스텀 액션 적용을 할 때 주의가 필요하다는 의미입니다.

    이때는 어떻게 하면 될까요? 

    사실 해결 방법은 너무나 간단합니다.

    댓글달기가 화면에 표시되는 경우에는 accessibilityCustomActions 안에 댓글까지 포함한 배열 형태의 액션을 추가하도록 하면 됩니다.

    즉 댓글이 포함된 커스텀 액션 메서드와 댓글이 미포함된 커스텀 액션 메서드 두 개를 만들어 두고 조건문을 활용하여 댓글이 화면에 표시되는 경우와 그렇지 않은 경우를 구분하여 이미 지정한 메서드를 추가하면 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] TableViewCell 내에 여러 UILabel 이 포함되는 경우
    Webacc NV 2021-10-08 18:50:52

    테이블뷰를 사용하여 화면을 구성하는 경우 각각의 TableViewCell 내에 텍스트, 버튼 등의 여러 객체들이 들어가게 됩니다. 

    그런데 이때 접근성 관점에서 유념해야 하는 것이 하나 있는데 바로 한 셀에 여러 UILabel 과 같은 텍스트뷰들이 있으면 보이스오버는 해당 요소들이 하나의 초점으로 잡힌다는 것입니다.

    따라서 만약 한 셀에 4개의 이미지 버튼과 4개의 텍스트를 가진 UILabel 을 배치했다면 보이스오버 초점은 4개의 UILabel 텍스트가 단 하나의 초점으로 제공되며 각 버튼이 별도로 포커스 됩니다.

    그러나 UILabel 의 accessibilityTraits 를 버튼으로 주게 되면 초점은 분리됩니다.

    따라서 한 셀에 여러 이미지 버튼과 여러 레이블을 배치하는 형태로 레이아웃을 구성하는 경우에는 이미지 버튼과 이미지 버튼에 해당하는 레이블 상위에 UIView 를 하나씩 두고 accessibilityTraits 및 accessibilityLabel 을 적절하게 삽입하는 것이 보이스오버 사용자를 고려한 접근성 적용 방법이 될 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 톡백에서 활성화 하려면 이중탭하세요 힌트 아예 없애기
    Webacc NV 2021-10-08 18:15:50

    접근성 테스트를 하다보면 이중탭을 해도 아무런 동작이 없으나 톡백에서는 활성화 하려면 이중탭하라는 메시지가 출력되는 경우가 있습니다.

    이는 클릭을 했을 때의 동작을 정의하지 않더라도 클릭 혹은 롱클릭 리스너가 시스템 자체에서든 수동으로든 추가되는 순간 AccessibilityAction 내에 클릭 액션이 추가되기 때문입니다.

    이런 경우 클릭에 대한 접근성 힌트를 없애려면 클릭 리스너 자체를 삭제하는 것이 가장 깔끔한 방법입니다.

    그러나 여러 상황 상 그렇게 할 수 없는 경우에는 AccessibilityAction 에 추가되어 있는 클릭 액션 자체를 삭제하는 방법으로도 이를 해결할 수 있습니다.

    AccessibilityAction 내의 클릭 액션을 삭제하는 방식으로 접근성이 적용된 대표적인 예는 구글에서 제공하는 MaterialDesignTabLayout 입니다.

    최신 탭레이아웃 라이브러리를 적용하여 탭을 개발하게 되면 선택된 탭의 경우 활성화 하려면 이중탭하라는 힌트 메시지를 발화 하지 않는 것을 확인할 수 있습니다.

    아래는 AccessibilityAction 내의 클릭 액션 자체를 제거하는 코드 예시입니다.

    핵심은 AccessibilityNodeInfo 내에서 clickable false, removeAction 메서드를 사용하면 됩니다.

            ViewCompat.setAccessibilityDelegate(view, new AccessibilityDelegateCompat() {
                @Override
                public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
                    super.onInitializeAccessibilityNodeInfo(host, info);
                    info.setClickable(false);
                    info.removeAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK);
                }
            });

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 중첩된 레이아웃으로 인해 접근성 초점 순서가 틀어질때
    Webacc NV 2021-10-07 13:10:23

    iOS 와 마찬가지로 안드로이드에서도 레이아웃이 중첩되면 접근성 초점 순서가 틀어질 수 있습니다.

    WebView, RecyclerView 등을 화면 전체로 하고 해당 레이아웃 위 또는 아래에 다른 도구 버튼들을 두는 경우가 대표적입니다.

    이런 경우에도 accessibilityTraversal before, after 속성을 사용하여 접근성 초점을 재조정할 수 있습니다.

    예를 들어 backRView, frontRView  두 개의 RecyclerView 가 있고 화면 상으로 배치된 레이아웃 순서와 달리 접근성 초점은 back RecyclerView 가 먼저 접근된다고 합시다.

    이 때 다음과 같이 backRView 에 대한 접근성 순서를 변경할 수 있습니다.

    android:accessibilityTraversalAfter="@id/frontRView"

    다만 ViewPager, RecyclerView 와 같이 시맨틱한 레이아웃이 아닌 LinearLayout, RelativeLayout 등은 레이아웃 자체의 초점 순서를 변경할 수 없으며 하위의 뷰들을 참조해야 합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] EditText 내부에 화면에 보여지는 힌트를 제공할 수 없을 때 해결 방법 코드 예시
    Webacc NV 2021-10-07 11:00:39

    지난번 EditText 내의 레이블을 제공하기 위해서 contentDescription 사용을 해서는 안 된다는 게시글을 작성한 적이 있습니다.

    당시 해당 게시글을 통해 AccessibilityNodeInfo 객체에서 hintText 를 추가함으로써 문제를 해결할 수 있다고 공유를 하였습니다.

    오늘은 해당 게시글에 대한 코드 예시를 공유하고자 합니다.

    코드 예시에서는 편집창에 금액 입력이라는 화면에 보이지 않는 접근성 레이블을 넣어 보도록 하겠습니다.

    접근성 적용시 참고하시기 바랍니다.

            ViewCompat.setAccessibilityDelegate(editText, object : AccessibilityDelegateCompat() {
                override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfoCompat?) {
                    super.onInitializeAccessibilityNodeInfo(host, info)
                    info?.hintText = "금액 입력"
                }
            })

     

    댓글을 작성하려면 해주세요.
  • news
    [HTML] 톡백과 크롬에서의 <input type="number" 밸류 값 읽지 못하던 이슈 관련
    Webacc NV 2021-10-06 10:50:05

    톡백 최신 버전과 크롬 정식의 최신 웹뷰를 사용하더라도 아래와 같은 마크업 구조에서 톡백이 밸류 값을 읽지 못하는 문제가 있습니다.

    <input type="number" value="3" aria-label="수량>

    해당 문제는 2021년 10월 6일 현재 크롬 카나리 최신 버전에서 해결이 되었습니다.

    따라서 약 2개월 정도 있으면 정식 버전 크롬에도 반영될 것이라 생각합니다.

    관련하여 참고 하시기 바랍니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 두 손가락 문지르기 제스처로 뒤로 이동하도록 구현하는 코드 예제
    Webacc NV 2021-09-24 12:08:15

    지난 번 iOS 뒤로가기 제스처 접근성에 대한 아티클을 작성한 적이 있습니다. 

    본 팁에서는 accessibilityPerformEscape 적용 예제를 공유하려고 합니다.

    내비게이션 컨트롤러의 네이티브 뒤로가기 버튼을 사용하지 않을 때 아래 예시를 참고하여 뒤로가기 버튼의 접근성을 적용할 수 있겠습니다.

        override func accessibilityPerformEscape() -> Bool {
            self.navigationController?.popViewController(animated: true)
            return true
        }

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android 짧은 팁] 동적으로 생성한 요소의 접근성 초점 순서를 변경해야 할 때
    Webacc NV 2021-09-24 11:12:36

    들어가기 앞서, 본 팁은 코틀린(Kotlin)으로 작성되었습니다.

    Android 앱을 만들다 보면 동적으로 생성되는 View의 순서를 바꾸고 싶을 때도 분명 있을 겁니다.

    그럴 때는 View.generateViewId() 메소드와 단순한 연산만으로 유니크한 id 정수를 부여할 수 있습니다.

    물론 for문으로 View를 생성하는 상황은 많지 않지만, for문으로 예제를 하나 만들어보도록 하겠습니다.

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)
      var buttonIdBase = View.generateViewId()
      val buttonList = arrayListOf<Button>()
      val myButtonGrid:GridLayout = findViewById(R.id.MyGridLayout);
      for (i:Int in 1..12) {
        val btn:Button = Button(this)
        btn.id = ButtonIdBase
        btn.contentDescription = i
        buttonList.add(Btn)
        buttonIdBase += 1
        myButtonGrid.addView(btn)
      }
      // 만약, 필요에 의해 10번째 버튼 다음에 12번째 버튼에 접근성 초점이 가게 하려면
      ButtonList[9].accessibilityTraversalAfter = ButtonList[11].id
    }

    generateViewId는 유니크한 int값을 하나 만듭니다. 이 상태에서 for문을 통해 숫자를 하나씩 더해가며 유니크한 연속된 아이디 값을 만들어 각 버튼에 부여하고, 버튼을 배열에 담았습니다.

    accessibilityTraversalAfter나 accessibilityTraversalBefore는 반드시 id를 참조해야 하는데, 반복으로 생성된 요소에는 일반적으로 id가 없기 때문에 접근성 초점 순서를 조절할 때 곤란할 수 있습니다. 방법이야 달라지겠지만 동적으로 생성되는 요소에도 이렇게 id를 부여하여, 접근성 초점의 순서를 변경시킬 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 중첩된 레이아웃 구조로 인해 접근성 포커스 순서가 틀어질 때
    Webacc NV 2021-09-24 10:33:17

    예전에 iOS 접근성 초점 순서 재조정하기에 대해 다루었습니다. 

    이번에는 해당 초점 재조정에 대한 응용 편입니다.

    한 컨테이너 안에서의 특정 뷰들 사이에서 접근성 순서를 재조정하는 것이 아닌 레이아웃 자체가 틀어졌을 때의 순서 재조정 방법입니다.

    예를 들어 TableView 가 전체 화면을 차지하고 있고 TableView 위 아래로 레이아웃이 중첩되어 다른 버튼들이 표시되는 경우에는 중첩된 레이아웃으로 인해 초점 순서가 틀어지는 경우가 있습니다.

    이런 경우에도 accessibilityElements 변수를 활용해서 관련 문제를 수정할 수 있습니다.

    다만 위에서 든 예시와 같은 경우는 레이아웃 자체의 초점이 틀어진 것이므로 해당 컨테이너가 들어 있는 상위 즉 view 의 accessibilityElements 를 재조정하면 됩니다.

    예를 들어 뷰컨트롤러 내에 headerView, tableView, footerView 가 있다면 다음과 같이 수정할 수 이겠습니다.

    self.view.accessibilityElements = [headerView, tableView, footerView]

    댓글을 작성하려면 해주세요.
  • news
    [인터넷] 센스리더 7.7 업데이트에 따른 일부 확장축소, 선택됨 및 팝업 읽어주기 기능 추가 관련
    Webacc NV 2021-09-18 10:23:36

    접근성을 적용한 마크업 시 확장/축소, 선택됨, 팝업 있음 같은 정보를 스크린 리더 호환성을 고려하여 제공하기 위해 고민을 하신 분들이 많으리라 생각합니다.

    이번 센스리더 7.7 업데이트를 통해 WAI-ARIA 속성 중 aria-expanded, aria-haspopup, aria-selected true 속성을 지원하게 되었습니다.

    따라서

    1. 링크나 버튼에 aria-expanded true false 적용 시 확장됨, 축소됨 정보를 읽어주게 됩니다.

    2. 버튼에 aria-haspopup 적용 시 풀다운 버튼 메뉴 라고 읽어줍니다.

    3. 탭컨트롤에 aria-selected true 적용 시 가상커서를 켠 상태에서는 선택된 탭에는 선택 이라는 정보를 함께 읽습니다. 

    접근성 구현 및 테스트 시에 참고하시기 바랍니다.

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 커스텀 탭막대 접근성 코드 적용 예제
    Webacc NV 2021-09-16 18:45:49

    얼마전 널리 아티클을 통해 커스텀 탭막대에 대한 접근성 적용하기에 대해 다루었습니다.

    본 팁에서는 커스텀 탭막대 접근성 적용 시 참고하실 수 있도록 간단한 코드 예제를 공유하려고 합니다.

    	// 커스텀 탭 요소들이 들어 있는 상위 superView 에 tabBar trait 적용
            footerView.accessibilityTraits = .tabBar
            // 기본으로 선택된 탭에 selected trait 추가
            self.footerButton.accessibilityTraits.insert(.selected)
        // 다른 탭 실행 시 선택됨 상태 변경 및 화면 변경 이벤트 적용
        @IBAction func onPlusModeButtonClicked(_ sender: Any) {
                self.footerPlusButton.accessibilityTraits.insert(.selected)
                self.footerButton.accessibilityTraits.remove(.selected)
                UIAccessibility.post(notification: .screenChanged, argument: self.footerPlusButton)
            self.isPlusMode = true
            self.tableView.reloadData()
        }
        

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 이중탭했을 때의 동작 재정의하기
    Webacc NV 2021-08-17 21:30:34

    가끔 접근성을 테스트 하다보면 분명히 탭을 해서 실행 가능한 요소임에도 불구하고 보이스오버를 켠 상태에서 이중탭을 하면 실행이 안 되는 경우들을 보곤 합니다.

    여러 가지 이유가 있겠지만 대표적인 원인으로는 접근성 초점을 가지는 요소의 하위 뷰 들 중 실행 가능한 요소가 있을 경우 보이스오버가 해당 뷰에 정확하게 초점을 맞추지 못해 발생하는 경우 입니다.

    예를 들어 보겠습니다.

    테이블뷰셀 내에 두 개의 UILabel, 한 개의 UISwitch 가 있습니다.

    그러면 보이스오버에서는 총 3번의 초점이 제공될 것입니다.

    만약 하나의 초점으로 제공해 주는 것이 맥락상 자연스럽다면 테이블뷰셀에 접근성 초점을 주고 accessibilityLabel, accessibilityTraits, accessibilityValue 를 각 레이블 및 스위치에서 정보를 오버라이드 할 것입니다.

    그런데 이렇게 할 경우 정작 이중탭을 하면 스위치가 온오프 되어야 하지만 동작이 안 될 수 있습니다.

    이때 사용할 수 있는 메서드는 accessibilityActivate 입니다.

    해당 메서드는 보이스오버를 실행한 상태에서 이중탭을 했을 때 실행할 동작을 재정의할때 사용합니다.

    안드로이드와 비교하자면 replaceAccessibilityAction 메서드 내의 ACTION)CLICK 의 동작을 재정의하는 것과 비슷하다고 할 수 있겠습니다.

    따라서 다음 코드 예시와 같이 사용할 수 있겠습니다.

    class SwitchTableViewCell: UITableViewCell {
    
        @IBOutlet weak var settingTitleLabel: UILabel!
    
        @IBOutlet weak var settingSubtitleLabel: UILabel!
    
        @IBOutlet weak var settingSwitch: UISwitch!
    
        override func awakeFromNib() {
    
            super.awakeFromNib()
    
            isAccessibilityElement = true
    
        }
    
        override var accessibilityLabel: String? {
    
            get { "\(settingTitleLabel.accessibilityLabel ?? ""). \(settingSubtitleLabel.accessibilityLabel ?? "")" }
    
            set {}
    
        }
    
        override var accessibilityTraits: UIAccessibilityTraits {
    
            get { settingSwitch.accessibilityTraits }
    
            set {}
    
        }
    
        override var accessibilityValue: String? {
    
            get { settingSwitch.accessibilityValue }
    
            set {}
    
        }
    
        override func accessibilityActivate() -> Bool {
    
            settingSwitch.isOn = !settingSwitch.isOn
    
            return true
    
        }
    
    }
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] onInitializeAccessibilityEvent 메서드 이해하기
    Webacc NV 2021-07-24 18:36:20

    우리가 널리 아티클 및 포럼에서 여러 번 다루었던 onInitializeAccessibilityNodeInfo 는 톡백에서 읽어주는 요소 유형이나 상태정보 혹은 접근성 액션과 같은 노드 정보를 수정할 때 사용하는 메서드 입니다. 

    오늘 함께 살펴보고자 하는 것은 onInitializeAccessibilityEvent 입니다.

    해당 메서드는 메서드 이름만 봐도 알 수 있듯이 접근성 이벤트와 관련된 메서드로 특정 접근성 이벤트가 발생했을 때를 캐치하여 무언가의 작업을 할 때 사용합니다.

    예를 들어 우리가 클릭 리스너가 있는 뷰를 이중탭했다고 생각해 봅시다.

    그러면 톡백에서 두둑 하는 사운드가 발생합니다. 이것은 접근성 이벤트 중 클릭 이벤트가 발생했다는 것입니다.

    또는 한 손가락 쓸기를 통해서 특정 요소에 포커스 하였고 톡백에서 포커스 한 내용을 읽었다고 하십시다.

    이때 접근성 이벤트 중 AccessibilityFocused 이벤트가 발생합니다.

    이와 같은 접근성 이벤트가 발생했을 때 이를 조건문으로 활용하여 원하는 기능을 구현할 수 있으며 이때 해당 메서드를 사용합니다.

    적용방법은 간단합니다.

    onInitializeAccessibilityEvent 메서드 및 super 메서드를 오버라이드 한 다음 if 문을 사용하여 접근성 이벤트 타입 중 클릭, 접근성 포커스됨, 뷰에 포커스됨 등의 이벤트가 있으면 무언가를 해줘 라고 구현하면 됩니다. 다음 예시를 참고합니다.

            ViewCompat.setAccessibilityDelegate(nextTrack, new AccessibilityDelegateCompat() {
                @Override
                public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
                    super.onInitializeAccessibilityEvent(host, event);
                    if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
                        Handler handler = new Handler(Looper.getMainLooper());
                        handler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                timePosition.announceForAccessibility(timePosition.getText());
                            }
                        }, 500);
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 액션 핸들러를 이용하여 커스텀 액션 적용하기
    Webacc NV 2021-07-08 17:30:04

    지난번에 iOS 커스텀 액션 구현 방법에 대해 공유한 적이 있습니다.

    해당 예제에서는 선택자, 즉 이미 구현된 함수 메서드를 가져와서 accessibilityCustomActions 변수 안에 배열 형태로 삽입함으로써 사용자가 커스텀 액션을 이중탭하였을 때 실행하고자 하는 기능을 삽입하는 방식이었습니다.

    그런데 이 방법 외에도 iOS 에서는 액션 핸들러를 이용하여 커스텀 액션 내에서 실행될 것들을 직접 구현할 수도 있습니다. 

    따라서 오늘은 해당 액션 핸들러를 사용하는 예제를 공유하려고 합니다.

    액션 핸들러를 사용하더라도 accessibilityCustomActions 변수 안에 배열 형태로 액션을 넣는 것은 같습니다.

    그런데 액션 핸들러를 사용할 때는 선택자가 아닌 액션 네임과 각 액션 핸들러가 들어가게 됩니다.

    액션 네임은 보이스오버에서 읽어줄 커스텀 액션 네임이며 핸들러는 기능 실행에 대한 것입니다.

    다음과 같이 코드를 구현할 수 있습니다.

     func makeAccessibilityCustomActions() -> [UIAccessibilityCustomAction] {
     let action = UIAccessibilityCustomAction(name: actionName) { (action) -> Bool in
                UIAccessibility.post(notification: .announcement, argument: "\(actionName)됨")
                self.setFavorite()
                self.accessibilityCustomActions = self.makeAccessibilityCustomActions()
                return true
            }
            return [action]
        }
    

    현재는 하나의 액션만 넣었지만 let action2, let action3 과 같이 여러 액션을 만들어서 리턴해 주면 해당 개수만큼 커스텀 액션이 늘어나게 됩니다.

    단 액션 네임이 관심 선택, 관심 해제와 같이 상황에 따라 변경되어야 하는 경우에는 스트링 변수를 사용하여 네임을 만든 다음 액션 네임이 변경되어야 할 시점에 스트링 네임을 변경하고 그것을 다시 accessibilityCustomActions 배열에 넣어 주어야 합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 뷰에 onTouchListener 구현 시에는 performClick 적용 필요
    Webacc NV 2021-06-19 16:30:52

    동영상 플레이어에서 화면 터치를 통해 숨겨진 화면 컨트롤을 다시 보이게 하거나 숨기는 작업 등을 구현할 때 onTouchListener 를 사용하게 됩니다. 그런데 이러한 onTouchListener 사용시에는 톡백 사용자는 이중탭을 해도 기능 실행이 전혀 안 됩니다. 따라서 다음 코드 예시와 같이 onTouchListener 사용 시에는 손가락 떼기 이벤트 발생 시 performClick 메서드를 함께 적용해 주어야 합니다.

    자세한 설명은 7월달에 공개될 동영상 플레이어 접근성 적용하기 아티클을 참고해 주시기 바랍니다.

      public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
          case MotionEvent.ACTION_DOWN:
            isTouching = true;
            return true;
          case MotionEvent.ACTION_UP:
            if (isTouching) {
              isTouching = false;
              performClick();
              return true;
            }
            return false;
          default:
            return false;
        }
      }
      // performClick 메서드를 오버라이드 할 때 실행될 동작을 아래에서 정의합니다.
      @Override
      public boolean performClick() {
        super.performClick();
        return toggleControllerVisibility();
      }

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 버튼과 텍스트가 분리된 접근성 개선하기 두 번째
    Webacc NV 2021-06-11 17:08:22

    작년에 버튼 이미지와 텍스트 레이블 초점이 분리된 경우의 접근성 개선 방안에 대해 다룬 적이 있었습니다.

    해당 방법대로 접근성 개선을 할 경우 보이스오버 사용자가 버튼과 텍스트 레이블을 하나의 포커스로 읽을 수 있습니다.

    그러나 위와 같은 방법 적용시 다음과 같은 문제가 있습니다.

    저시력 사용자가 보이스오버를 켠 상태에서 화면에 있는 이미지 혹은 텍스트 레이블 터치 시 내용을 읽어주지 않음.

    이는 두 요소 중 하나의 요소를 접근성 초점에서 완전히 제거했기 때문에 화면에는 두 영역이 있으나 보이스오버에서는 한 영역만 초점이 제공되기 때문입니다.

    따라서 오늘은 다른 방법을 추가적으로 공유를 하려고 합니다.

    1. 버튼과 버튼에 해당하는 텍스트가 있는 요소를 별도의 컨테이너 안으로 옮깁니다.

    즉 예를 들어 하나의 컨테이너에 6개의 객체들이 있는데 그 중 두 객체가 버튼과 이미지라고 합시다.

    그렇다면 기존 컨테이너 안에 또 다른 buttonAccessibilityContainer 와 같은 UIView 컨테이너를 만들고 버튼 이미지와 레이블 텍스트를 해당 컨테이너 안으로 옮기는 것입니다.

    2. 버튼과 텍스트만 들어 있는 컨테이너의 isAccessibilityElement true 로 설정합니다.

    그러면 보이스오버는 하위 두 요소에는 초점을 제공하지 않습니다.

    3. 해당 컨테이너에서 accessibilityLabel, accessibilityTraits 를 설정합니다.

    이렇게 하면 보이스오버에서도 하나의 초점으로 버튼 이미지와 레이블을 읽을 수 있고 접근성 프레임 영역도 두영역이 다 하나로 잡히게 됩니다.

    스토리보드에서 컨테이너를 추가로 만들어 초점을 하나로 합치고자 하는 객체를 넣은 다음 다음과 같은 함수를 만들어 사용할 수도 있겠습니다.

        private func setContainerViewAccessibility(containerView: UIView, traitsView: UIView, label: String) {
            
            containerView.isAccessibilityElement = true
            containerView.accessibilityLabel = label
            containerView.accessibilityTraits = traitsView.accessibilityTraits
        }

    이러한 함수를 만들었다면 다음과 같이 적용이 가능합니다.

    self.setContainerViewAccessibility(containerView: self.confirmView, traitsView: self.confirmButton, label: self.confirmLabel.text ?? "확인")

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] UIAccessibilityNotification .announcement 코드 예제
    Webacc NV 2021-06-09 16:57:48

    접근성을 개선할 때 참고하실 수 있도록 여러 상황에 대한 코드 예제를 팁 형태로 올려드리고 있습니다.

    보이스오버에서 특정 상황에서 알림 메시지를 자동으로 보이스오버가 말하도록 해야 할 때 사용하는 메서드에 대해 소개는 하였으나 정작 이에 대한 코드 예제가 없어 이를 공유하게 되었습니다.

    이전 배너, 다음 배너 버튼을 누를 때마다 배너가 넘어가면서 배너의 내용을 자동으로 읽어주도록 하는 것을 예로 들어 예제 코드를 공유합니다.

    알림 메시지에 대한 접근성 적용시 참고하시기 바랍니다.

        @IBAction func next(_ sender: UIButton) {
            self.count = self.count + 1
            if self.count > self.banners.count - 1  {
                self.count = 0
            }
      
            let x = UIScreen.main.bounds.width * CGFloat(self.count)
            self.scrollView.contentOffset = CGPoint(x: x, y: 0)
            sleep(1) // 알림 메시지를 주기 전 약간의 딜레이 주기
            UIAccessibility.post(notification: .announcement, argument: self.banners[self.count])
        }
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 편집창, 체크박스와 레이블이 분리된 경우 labelFor 속성 활용하기
    Webacc NV 2021-06-03 18:05:15

    편집창이나 체크박스를 구현할 때 레이블을 해당 뷰 안에 넣는 경우도 있지만 텍스트뷰와 분리하는 경우도 많습니다.

    이런 경우 톡백에서 체크박스나 편집창에 초점을 주면 어떤 체크박스나 편집창인지를 읽지 못합니다.

    물론 체크박스나 편집창에서 한 손가락 오른쪽 혹은 왼쪽 쓸기를 했을 때 이에 해당하는 텍스트뷰를 읽어주기는 하지만 여러개의 편집창이 있거나 혹은 컨트롤 단위로 이동했을 때 바로 해당 레이블을 듣지 못하게 되어 접근성 수정이 필요합니다.

    방법은 아주 간단합니다.

    1. TextView 에 labelFor 속성을 추가합니다. labelFor 에는 텍스트뷰와 연결되는 편집창 혹은 체크박스의 id 를 연결합니다.

    2. 해당 체크박스에는 1번에서 labelFor 속성에 명시한 id 를 추가합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 탭하여 실행 가능한 이미지뷰에 반드시 대체 텍스트를 제공해야 하는 이유
    Webacc NV 2021-06-01 18:18:55

    아이폰, 안드로이드 모두 이미지뷰에 대체 텍스트를 삽입하지 않으면 보이스오버, 톡백에서 해당 이미지뷰는 장식용으로 간주하고 초점 자체가 가지 않습니다.

    그런데 안드로이드의 경우에는 해당 이미지뷰에 클릭 리스너 이벤트가 추가되는 순간, 즉 탭할 수 있는 동작이 들어가는 순간 대체 텍스트가 없다 하더라도 이미지뷰에 초점을 받을 수 있게 됩니다.

    물론 톡백에서 이미지뷰에 포커스를 하면 라벨 지정되지 않음 버튼, 혹은 id 값이 있다면 btnCancel 버튼과 같이 읽어줄 것입니다.

    하지만 보이스오버의 경우에는 이미지뷰에 UITapGestureRecognizer 이벤트가 추가되어 이중탭하여 무언가를 실행할 수 있게 되었다 하더라도 안드로이드와 달리 대체 텍스트가 없으면 접근성 초점이 제공되지 않습니다.

    따라서 실행 가능한 이미지뷰에 대체 텍스트가 없으면 보이스오버 사용자는 해당 객체를 실행조차 못하는 접근성 문제가 발생하게 됩니다.

    따라서 대체 텍스트 삽입의 중요성에 대해 우리는 더욱 생각하며 개발을 해야 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 라디오버튼 접근성 적용하기
    Webacc NV 2021-05-31 16:55:01

    여러 팁을 통해 커스텀 스위치, 커스텀 체크박스, 커스텀 탭 등의 접근성 적용 예제에 대해 다룬 적이 있습니다.

    사실 커스텀 라디오 버튼 역시 구현 방법은 비슷하지만 샘플 코드가 있으면 개발 시에 참조하기 편리하므로 아래에 커스텀 라디오 버튼에 대해서도 샘플 코드를 공유하려고 합니다.

    참고: 본 예제의 라디오버튼은 이미지뷰로 적용하였으며 단품, 세트 중에서 선택하는 커스텀 라디오 버튼이라고 가정하겠습니다.

    접근성 구현 시 참고가 되었으면 좋겠습니다.

            // 이미지뷰의 접근성 정보 수정을 위해 AccessibilityDelegate 객체 만들기
            button1.accessibilityDelegate = object : View.AccessibilityDelegate() {
                override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) {
                    super.onInitializeAccessibilityNodeInfo(host, info)
                    info?.className = RadioButton::class.java.name
                    info?.isCheckable = true // 체크, 체크 해제에 대한 음성 안내
                    info?.isChecked = button1.isSelected // 조건문에 따라 checked true false 적용
                    info?.isSelected = false // 선택된 요소에 selected 적용한 경우 체크됨과 중복되므로 해제
    
                }
            }
    
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 탭레이아웃 라이브러리에 적용된 탭 요소 접근성 적용과 커스텀 탭에 대한 접근성 대응
    Webacc NV 2021-05-31 15:13:57

    안드로이드 탭 레이아웃 접근성 기능 업데이트에 대해 말씀드린 적이 있었습니다.

    지난번에도 언급한 바와 같이 탭 요소를 탭이라고 읽어주도록 업데이트가 되었는데 원칙적으로는 안드로이드 접근성 API 에는 탭이라는 접근성 역할이 없습니다.

    그럼 라이브러리에서 톡백이 어떻게 탭이라고 읽도록 구현했을까요?

    그에 대한 정답은 바로 roleDescription 입니다.

    구글에서도 톡백의 각 탭을 탭으로 읽도록 하기 위해서 AccessibilityNodeInfoCompat roleDescription 메서드를 사용한 것입니다.

    따라서 탭을 구현할 때 탭레이아웃을 사용하면 접근성 구현이 별도 필요하지 않습니다.

    하지만 기본 탭레이아웃을 사용하지 않고 텍스트뷰 등으 탭으로 사용한 경우에는 다음 예시와 같이 roleDescription 을 사용하여 탭 요소를 탭으로 읽도록 구현할 수 있습니다.

        ViewCompat.setAccessibilityDelegate(customTab, new AccessibilityDelegateCompat() {
                @Override
                public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
                    super.onInitializeAccessibilityNodeInfo(host, info);
                    info.setRoleDescription("탭");
                }
            });

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 확장/축소 접근성 적용 예제
    Webacc NV 2021-05-31 12:20:52

    작년 8월 즈음에 안드로이드에서의 확장/축소 접근성 적용에 대해 한번 다룬 적이 있었습니다.

    이번 시간에는 확장 축소가 적용된 샘플 코드를 공유해 보려고 합니다.

    코드 적용에 대한 자세한 설명은 8월에 발행될 접근성 블로그 아티클을 참고해 주시기 바랍니다.

    참고: 이번에 다룰 코드 예시에서는 과일 버튼이 있다고 가정하고 과일 버튼은 확장 축소가 가능한 버튼이며 기본적으로는 축소되어 있습니다.

            // 접근성 정보 수정 
            fruitButton.setAccessibilityDelegate(new View.AccessibilityDelegate() {
                // 확장/축소 정보 삽입
                @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
                    super.onInitializeAccessibilityNodeInfo(host, info);
                    info.setClassName(Button.class.getName());
                    
                    if (isFruitContainerExpanded) {
                        // 확장된 상태이므로 축소 액션 삽입
                        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
                    } else {
                        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
                    }
                }
    
    // 톡백에서 작업 메뉴를 열어 확장 축소 적용 가능하도록 구현
                @Override
                public boolean performAccessibilityAction(View host, int action, Bundle args) {
                    if (super.performAccessibilityAction(host, action, args)) {
                        return true;
                    }
                    if (action == AccessibilityNodeInfo.ACTION_COLLAPSE) {
                        // 톡백에서 축소하는 액션을 작업 메뉴에서 실행 시
                        isFruitContainerExpanded = false;
                        collapseFruitContainer();
                    } else if (action == AccessibilityNodeInfo.ACTION_EXPAND) {
                        isFruitContainerExpanded = true;
                        expandFruitContainer();
                        return true;
                    }
                    return false;
                }

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 버튼, 레이블과 같은 요소의 상위 컨테이너에 접근성 초점을 주는 의미
    Webacc NV 2021-05-28 13:27:36

    iOS 보이스오버는 기본적으로 UIView, UICollectionView 와 같은 컨테이너에는 접근성 초점이 제공되지 않습니다. 

    그 이유는 해당 컨테이너들은 접근성에서는 의미를 가진 요소가 아니며 버튼, 레이블, 텍스트와 같은 요소들을 품고 있는 요소들이기 때문입니다.

    그런데 만약 이러한 컨테이너에 접근성 초점을 주면 어떻게 될까요?

    containerUiView.isAccessibilityElement = true

    이렇게 하면 해당 컨테이너 안에 포함된 그 어떤 요소에도 접근성 초점이 제공되지 않습니다.

    따라서 접근성 초점을 줄 때는 상당히 주의가 필요합니다.

    하지만 상황에 따라서는 이러한 상위 컨테이너 뷰에 접근성 초점을 주어야 하는 경우도 있습니다.

    대표적인 예가 하위 컨테이너 뷰들을 접근성 초점에서 재구성해야 할 때입니다.

    하위 뷰들의 초점 순서가 논리적이지 못하거나 하위 뷰들의 헤더와 데이터가 별도의 뷰로 구현되어 있거나 분리되어 있어야 할 뷰들이 하나로 합쳐져 있는 것 등 이미 구현된 뷰를 접근성에서 재구성해야 할 때 사용을 합니다.

    즉 가상의 접근성 요소들을 만드느 것입니다.

    앞으로 몇 차례에 걸쳐 해당 주제에 대해 다루어 보도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] accessibilityTrait 변경 시 유의사항
    Webacc NV 2021-05-26 17:27:57

    커스텀 컨트롤에서 각 요소의 유형이나 상태를 접근성 서비스에서 적절하게 읽어줄 수 있도록 하기 위해 우리는 accessibilityTraits 속성을 많이 변경하곤 합니다. 

    그런데 안드로이드나 웹과는 달리 iOS accessibilityTraits 속성은 요소 유형, 상태정보 속성을 함께 포함하고 있습니다.

    따라서 웹이나 안드로이드는 특정 요소에 대한 상태정보를 변경할 때 역할(role) 속성이 이미 정의되어 있다면 상태 정보만 상황에 따라 변경해 주면 됩니다.

    그러나 iOS에서는 위에서 설명한 것처럼 accessibilityTraits 를 재정의하는 순간 기존의 모든 역할, 상태정보 등은 초기화 되므로 이에 대한 주의가 필요합니다.

    예를 들어 UIButton 으로 되어 있는 요소에 선택됨 정보를 주어야 한다고 생각해 봅시다.

    선택됨 정보는 accessibilityTraits = .selected 를 통해서 줄 수 있습니다.

    그러나 바로 위의 코드와 같이 적용하면 기존 버튼 속성은 사라져버립니다.

    우리는 버튼과 선택됨을 함께 읽도록 해야 하므로 accessibilityTraits = ].button, .selected] 와 같이 속성을 주어야 하는 것입니다.

    혹은 accessibilityTraits 추가시에는 insert, 삭제 시에는 remove 를 사용하는 것도 방법이 될 수 있습니다.

    댓글을 작성하려면 해주세요.