아티클

UIKit을 위한 접근성 확장기능 소개 및 몇 가지 유용한 팁

엔비전스 접근성 2023-09-25 10:21:52

안녕하세요, 엔비전스입니다.

이번에는 오랜만에 UIKit 접근성 구현과 관련된 주제로 글을 작성하게 되었습니다.

본 문서에서는 UIKit 접근성을 조금 더 간단하게 구현할 수 있도록 만든 몇 가지 extension이하: 확장기능) 소개 및 팁에 대해 함께 살펴보고자 합니다.

참고: 각 코드에 대한 자세한 예시 및 사용법은 포럼 팁을 통해 자세히 다루었습니다. 따라서 각 섹션 제목에 연결된 링크를 참고하여 포럼 팁을 함께 확인하시면 이해에 도움이 됩니다.

접근성 확장기능 소개

extension은 Swift의 기존 객체에 새로운 메소드를 정의하는 방법입니다. 접근성 확장기능은 지난번 소개한 안드로이드 접근성 유틸클래스처럼 swift에서 자주 사용하는 기능 구현을 위해 extension 키워드를 활용하여 여러 메서드들을 만들어 기존 UIKit 객체에 추가했습니다.

접근성과 관련된 extension을 기존 객체에 맞게 잘 만들어 두면 매우 간편하게 여러 접근성 기능을 구현할 수 있습니다.

(Getter) focused:Bool

요소가 VoiceOver 초점을 가지고 있는지 bool 값으로 반환합니다.

물론 accessibilityElementDidBecomeFocused() 메서드를 사용할 수 있지만 해당 메서드를 사용하려면 초점을 얻어야 하는 요소에 대해 커스텀 하위 클래스를 만들어야 하고 조건문을 설정하기 위한 변수를 만드는 등의 추가 코드들이 필요합니다. 그러나 focused를 사용하면 커스텀 하위 클래스 선언 없이 뷰컨트롤러 내에서도 구현이 가능하고 아래 예시와 같이 단 한 줄의 코드로 체크가 가능합니다.

if self.accessibleView.focused {/* VoiceOver가 포커스 되었을 때 실행할 코드 */}

(Method) observeVoiceOverState(handler:(bool)->Void)

VoiceOver가 실행 중인지 아닌지를 확인하는 확장 기능입니다. 우리가 잘 알고 있는 isVoiceOveRunning은 단순히 VoiceOver의 실행 여부를 알기 위한 부울값 속성이기 때문에 뷰 컨트롤러가 실행된 이후에 VoiceOver를 켜거나 끄면 이를 인지하지 못합니다. observeVoiceOverState는 지속적으로 VoiceOver의 켬/끔 여부를 인지할 수 있도록 구현이 필요할 때 사용할 수 있습니다.

예를 들어 VoiceOver를 중간중간 사용하는 저시력 사용자가 동영상 플레이어를 실행했다고 생각해 봅시다. VoiceOver가 켜지면 일시정지, 재생과 같은 컨트롤이 항상 표시되고, 끄면 전체화면 모드로 변경된다면 영상 시청 시에는 전체 화면으로 감상하기 위해서 VoiceOver를 껐다가 조작 시에 VoiceOver를 켜서 원하는 작업을 수행할 수 있을 것입니다.

(Method) announceForAccessibility(_ string:string)

UIKit에서는 UIAccessibility.post(notification: .announcement, argument: announcementString)과 같이 알려야 할 텍스트를 쉽게 구현할 수 있는 API가 존재하기에 굳이 해당 기능이 필요한지 의문을 가질 수 있습니다. 그러나 이러한 알림을 구현하다 보면 약 0.1초 정도의 딜레이가 없이는 알려야 하는 내용을 VoiceOver가 읽지 못하는 경우가 많습니다. 그래서 단 한 줄의 코드를 통해서 딜레이와 VoiceOver 알림을 함께 구현할 수 있도록 하기 위해 해당 확장 기능을 만들게 되었습니다.

(Method) sendFocusTo(view:UIView)

메서드 이름 그대로 특정 요소로 접근성 초점을 보내는 코드를 구현할 때 사용할 수 있습니다. 해당 확장 기능을 사용하면 0.5초의 딜레이와 함께 지정된 요소로 접근성 포커스를 이동시켜 줍니다.

몇 가지 유용한 팁

특정 테이블 셀을 탭 요소로 구현해야 할 때

커스텀 탭막대 구현하기 아티클에서 버튼이나 UILabel 등을 탭으로 읽어주도록 하는 방법에 대해 살펴본 적이 있습니다. 그런데 tabbar traits를 적용해야 할 UIView가 없이 특정 테이블 셀에 커스텀 탭을 구현할 수도 있는데, 이 때는 탭이 구현된 셀에 tabbar traits를 적용하면 됩니다.

다만 테이블 셀은 하위에 여러 접근성 요소들이 있어도 요소 유형이 없는 한 무조건 하나의 초점으로 잡히게 되므로, 초점을 나누기 위해 하위 탭 요소에 반드시 button traits를 적용해 주어야 합니다. button traits를 적용하더라도 상위에 tabbar traits가 붙게 되므로 요소 유형은 탭으로 읽게 됩니다.

하나의 컬렉션 뷰를 페이지처럼 구현했을 때

iPhone의 홈 화면과 같이 여러 페이지로 화면을 나누어 놓고 각 페이지마다 특정 콘텐츠를 삽입하는 경우 일반적으로 UIPageViewController를 사용합니다. 각 페이지 콘텐츠는 별도의 ViewController로 만들어 놓고 스크롤될 때마다 각 ViewController로 전환되게끔 하는 형태입니다.

CollectionView(이하 컬렉션 뷰)나 TableView(이하 테이블 뷰) 등은 기본적으로 스크롤 이벤트를 가지고 있습니다. 그래서 많은 콘텐츠가 있어도 한 손가락 오른쪽 혹은 왼쪽 쓸기로 이동하면 화면이 자동으로 스크롤되면서 콘텐츠를 탐색할 수 있습니다.

그런데 UIPageViewController를 사용하지 않고 하나의 컬렉션 뷰에 모든 데이터를 넣어 놓고 스크롤은 가로로 되게 한 다음 페이지별로 일정 콘텐츠를 표시할 때가 있습니다.

문제는 VoiceOver 초점이 컬렉션 뷰외 다른 콘텐츠 영역에 있다가 컬렉션 뷰 안으로 들어올 때 사용자의 의도와 다르게 자동으로 첫 번째 페이지 혹은 마지막 페이지로 자동 스크롤이 되어 버린다는 것입니다.

예를 들어서, 두번째 페이지가 선택되어 있는데, 컬렉션 뷰 밖에서 컬렉션 뷰 안으로 들어가게 되면 표시중인 페이지와 관계없이 첫번째 페이지나 마지막 페이지를 탐색하게 됩니다.

요소를 제거하거나 isHidden 등으로 숨기는 것이 아니라면 VoiceOver로 가려진 영역에 초점이 가는 것은 자연스러운 동작이며, VoiceOver 초점이 간 요소가 눈에 보이도록 스크롤이 맞춰지는 것이 기본 동작이기 때문입니다.

따라서 이런 경우에는 VoiceOver가 켜져 있을 때에는 다른 페이지 콘텐츠로 스크롤하는 것 자체를 막는 것이 필요합니다. 다만 페이지를 조절하는 UIPageControl이 잘 작동하는 전제이며 VoiceOver 사용자는 페이지 조절가능을 활용하여 페이지 조절이 가능하기 때문에 사용에 이슈는 없습니다.

댓글 0
댓글을 작성하려면 해주세요.