iOS 접근성 (SwiftUI)

[iOS - SwiftUI] Voice Over 접근성 - 우선순위 처리 방법(accessibilitySortPriority), 그룹화 방법 (accessibilityElement)

jake-kim 2024. 10. 16. 01:56

우선순위 처리 방법

  • 보통 OS에서 왼쪽 상단에서 시작하여 오른쪽을 읽고 아래로 순차적으로 읽어줌
    • 지구본의 이미지 -> Hello, world! -> toggle button

3개의 뷰 (Image, Text, Button)

ex)

접근성 디폴트 우선순위

  • Hello, world!가 가장 중요할 때, Hello, world!를 가장 먼저 읽히게 하고 싶은 경우?
    • 해당 뷰에 accessibilitySortPriority를 높게 설정하기 (값이 높을수록 가장 먼저 읽힘)
    • 따로 이 값을 설정 안하면 디폴트 값은 0

ex) "Hello, world!" 뷰를 accessibilitySortPriority를 1로 설정

struct ContentView: View {
    @State private var toggle = true
    
    var body: some View {
        VStack {
            HStack {
                if toggle {
                    Image(systemName: "globe")
                        .resizable()
                        .frame(width: 24, height: 24)
                }
                
                Text("Hello, world!")
                    .accessibilitySortPriority(1) // <-
            }
            
            Button {
                toggle.toggle()
            } label: {
                Text("toggle button")
            }

        }
        .padding()
    }
}

결과) 

 

  • 여기서 알 수 있는 것
    • accessibilitySortPriority값이 동일하면 가로로 나열된 뷰(지구본 Image)를 하단에 있는 toggle button보다 먼저 접근성 동작

그룹화 방법

  • 지구본과 "Hello, world!" 텍스트는 하나의 컴포넌트로 취급하기 위해서는 그룹화가 필요
  • accessibilityElement(children:)을 사용
    • 뷰를 접근성 컨테이너로 만들어주는 기능

https://developer.apple.com/documentation/swiftui/view/accessibilityelement(children:)

  • 이 API에서 가장 중요한 것은 children 인수 값
    • 디폴트 값은 ignore이며 3가지 옵션이 존재

  • 3가지 옵션
    • ignore
      • 마치 하나의 뷰처럼 합침 (A뷰, B뷰가 있을 때 이 2개의 뷰를 마치 하나의 뷰로 접근성에서 포커싱 됨)
      • children 뷰들의 접근성 요소들을 모두 무시 (accessibilityTraits, accessibilityLabel 모두 초기화되어 새로 accessibilityLabel를 지정해줘야함)
    • combine
      • 마치 하나의 뷰처럼 합침
      • children 뷰들의 접근성을 고려 (accessibilityLabel가 자동으로 내부에 있는 뷰 기준으로 입력됨)
    • contain
      • 접근성 포커싱 영역은 위 두 옵션과는 다르게 개별적으로 포커싱되지만 뷰가 여러개 있을 때 이 뷰들을 그룹화하고 그룹별로 순서를 주고 싶을때 사용

ex) contain 사용 예시

  • 상위에 HStack, 하위에 VStack으로 데이터가 있는 뷰에서 상위 HStack을 그룹화하여 먼저 읽히고 "Usres"라는 accessibilityLabel이 읽히도록 하고 싶은 경우 사용
var body: some View {
    ScrollView {
        VStack {
            HStack {
                ForEach(users) { user in
                    UserCell(user)
                }
            }
            .accessibilityElement(children: .contain)
            .accessibilityLabel("Users")


            VStack {
                ForEach(messages) { message in
                    MessageCell(message)
                }
            }
            .accessibilityElement(children: .contain)
            .accessibilityLabel("Messages")
        }
    }
}
  • 다시 돌아와서, 아래 뷰 지구본과 "Hello, world!" 텍스트를 하나의 뷰로 묶고 싶은 경우?
    • accessibilityElement(children: .combine) 사용하여 해결

 

HStack {
    if toggle {
        Image(systemName: "globe")
            .resizable()
            .frame(width: 24, height: 24)
    }
    
    Text("Hello, world!")
        .accessibilitySortPriority(1)
}
.accessibilityElement(children: .combine)

 

결과) 

  • 지구본 이미지와 텍스트가 하나로 묶였고, "Hello, world!"라고 Text의 접근성 값도 자동으로 적용됨

  • 주의사항)
    • children옵션 중에서 ignore도 하나로 합쳐주지만, children 뷰들의 접근성 요소들을 모두 무시되므로 "Hello, world!"문구를 읽어주지 않기 때문에 따로 accessibilityLabel() 값을 추가해야함

ex) ignore을 사용한 경우

HStack {
    if toggle {
        Image(systemName: "globe")
            .resizable()
            .frame(width: 24, height: 24)
    }
    
    Text("Hello, world!")
        .accessibilitySortPriority(1)
}
.accessibilityElement(children: .ignore)

 

결과) children 뷰들의 접근성 요소들을 모두 무시되므로 "Hello, world!"문구를 읽어주지 않음

* 전체 코드

import SwiftUI

struct ContentView: View {
    @State private var toggle = true
    
    var body: some View {
        VStack {
            HStack {
                if toggle {
                    Image(systemName: "globe")
                        .resizable()
                        .frame(width: 24, height: 24)
                }
                
                Text("Hello, world!")
                    .accessibilitySortPriority(1)
            }
            .accessibilityElement(children: .combine)
            
            Button {
                toggle.toggle()
            } label: {
                Text("toggle button")
            }
        }
    }
}

#Preview {
    ContentView()
}

 

* 참고

- https://developer.apple.com/documentation/swiftui/view/accessibilityelement(children:)

- https://developer.apple.com/documentation/swiftui/view/accessibilitysortpriority(_:)