SwiftUI 접근성 내부

발행: (2025년 12월 27일 오전 06:39 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

Accessibility Is a Parallel View Tree

SwiftUI builds two trees:

  • The visual view tree
  • The accessibility tree

They are related — but not identical. A single visual view can:

  • expose multiple accessibility elements
  • merge with siblings
  • be hidden entirely
  • change role dynamically

Understanding this explains most “why doesn’t VoiceOver read this correctly?” bugs.

SwiftUI가 접근성 요소를 생성하는 방법

기본 설정:

  • 대부분의 컨트롤(Button, Toggle, TextField)은 접근성 요소를 자동으로 생성합니다.
  • 컨테이너(HStack, VStack, ZStack)는 일반적으로 생성되지 않습니다.

Example

HStack {
    Image(systemName: "heart.fill")
    Text("Favorites")
}

VoiceOver는 SwiftUI에 별도로 지시하지 않는 한 “heart fill, Favorites”라고 읽을 수 있습니다.

그룹화 vs 분리 요소

SwiftUI는 그룹화에 대한 명시적인 제어를 제공합니다.

자식들을 하나의 요소로 결합

.accessibilityElement(children: .combine)

결과: “Favorites, button”

자식들을 완전히 무시

.accessibilityElement(children: .ignore)

이제 모든 것을 수동으로 정의합니다.

자식들을 별도로 포함 (기본값)

.accessibilityElement(children: .contain)

각 자식이 자체 의미를 가질 때 사용합니다.

역할, 특성 및 의미론

접근성은 단순히 레이블이 아니라 의미입니다. SwiftUI는 동작을 설명하기 위해 특성을 사용합니다:

  • isButton
  • isHeader
  • isSelected
  • isDisabled

예시

Text("Settings")
    .accessibilityAddTraits(.isHeader)

이제 VoiceOver는 텍스트뿐만 아니라 계층 구조를 이해합니다.

포커스 시스템 (탐색에 필수)

SwiftUI의 접근성 포커스는 상태 기반입니다.

@AccessibilityFocusState var focused: Bool
Text("Error occurred")
    .accessibilityFocused($focused)

포커스 트리거:

focused = true

필수 사용 사례:

  • 폼 검증 오류
  • 탐색 전환
  • 알림 및 시트
  • 동적 콘텐츠 업데이트

포커스 제어가 없으면 사용자가 길을 잃게 됩니다.

상태 변화 및 접근성 업데이트

SwiftUI는 다음과 같은 경우 자동으로 변경 사항을 알립니다:

  • 텍스트 변경
  • 값 업데이트
  • 컨트롤 토글

사용자 정의 뷰는 명시적인 알림이 필요합니다:

UIAccessibility.post(
    notification: .announcement,
    argument: "Upload complete"
)

필요할 때만 적게 사용하세요 — 하지만 의도적으로 사용하십시오.

접근성 및 NavigationStack

Navigation은 접근성 트리에 영향을 줍니다. 탐색할 때:

  • 이전 요소가 제거됩니다
  • 새로운 트리가 구축됩니다
  • 제어되지 않는 한 포커스가 초기화됩니다

탐색 후 권장되는 방법:

.accessibilityFocused($focusOnTitle)

이는 UIKit의 “screen changed” 동작을 그대로 반영합니다.

제스처 vs 접근성 액션

사용자 정의 제스처는 기본적으로 접근성이 제공되지 않습니다.

잘못된 패턴

.onTapGesture { submit() }

VoiceOver 사용자는 이를 발견할 수 없습니다.

올바른 패턴

.accessibilityAction {
    submit()
}

또는 실제 Button을 사용하세요.

장식 요소 숨기기

장식용 뷰는 접근성에서 보이지 않게 해야 합니다:

Image("background")
    .accessibilityHidden(true)

그렇지 않으면 VoiceOver가 의미 없는 내용을 읽어줍니다.

동적 타입은 레이아웃 문제다

Dynamic Type은 단순히 글꼴만이 아니라 레이아웃에도 영향을 줍니다. SwiftUI는 자동으로:

  • 글꼴 크기를 늘립니다
  • 텍스트를 재배치합니다
  • 줄 높이를 조정합니다

레이아웃은 성장할 수 있도록 허용해야 합니다.

잘못된 관행

  • 고정 높이
  • 잘린 텍스트
  • 경직된 스택

올바른 관행

  • 유연한 프레임
  • 다중 행 텍스트
  • 적응형 레이아웃

접근성 테스트 올바르게 하기

  • 시뮬레이터: VoiceOver, Dynamic Type, Reduce Motion, Increase Contrast
  • Xcode Accessibility Inspector: element order, labels, traits, hit targets

경험법: 탐색이 어색하게 느껴진다면 → 아마도 그렇다.

접근성 디자인 규칙 (내부 수준)

  • 접근성은 상태 기반이다
  • 포커스는 명시적이다
  • 의미론이 레이블보다 중요하다
  • 커스텀 뷰는 커스텀 접근성이 필요하다
  • 네비게이션은 처리되지 않으면 포커스를 재설정한다
  • 제스처는 접근성 액션이 필요하다
  • 레이아웃은 다이내믹 타입을 지원해야 한다

Final Thoughts

SwiftUI 접근성은 부가 기능이 아니라 일급 시스템이며 다음과 연결됩니다:

  • 렌더링
  • 상태
  • 내비게이션
  • 레이아웃
  • 인터랙션

시작부터 접근성을 염두에 두고 디자인하면:

  • UI가 더 명확해집니다
  • 아키텍처가 개선됩니다
  • 앱이 더 “Apple‑like”하게 느껴집니다
  • 모두가 혜택을 받습니다 — 보조 기술 사용자뿐만 아니라
Back to Blog

관련 글

더 보기 »

SwiftUI 제스처 시스템 내부

!Sebastien Latohttps://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%...

SwiftUI에서 ScrollView와 좌표 공간

스크롤 기반 UI는 현대 앱 어디에나 존재합니다 - collapsing headers - parallax effects - sticky toolbars - section pinning - scroll‑driven animations - pull‑to‑r...