스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
SwiftUI의 손쉬운 사용 관련 업데이트
SwiftUI를 사용하면 모두가 손쉽게 사용할 수 있는 멋진 경험을 간편하게 빌드할 수 있습니다. SwiftUI가 제공하는 손쉬운 사용 요소를 통해 보조 기술(Assistive Technology)이 앱을 파악하고 탐색하는 방법을 확인해 보세요. 아울러 이러한 경험을 더욱 맞춤화하기 위해 손쉬운 사용 제어자를 사용하여 앱의 콘텐츠와 상호작용에 관한 정보를 제공하는 방법도 공유합니다.
챕터
- 0:00 - Introduction
- 0:58 - Fundamentals
- 9:01 - View accessibility
- 16:28 - Enhanced interactions
리소스
-
다운로드
안녕하세요, 저는 Accessibility 팀의 엔지니어 Tommy입니다 손쉬운 사용은 모든 훌륭한 앱의 기본 요소로, 모든 사람이 여러분이 빌드한 경험으로 만들고 연결하고, 즐길 수 있게 합니다 SwiftUI로 Apple 플랫폼 전반에서 이러한 경험을 구현할 수 있습니다 오늘은 SwiftUI가 기본으로 제공하는 손쉬운 사용 기능과 앱의 손쉬운 사용을 개선하고 제작하는 도구를 어떻게 제공하는지 자세히 살펴보겠습니다
다음으로 뷰의 손쉬운 사용 기능을 향상시키기 위해 SwiftUI에 더 많은 정보를 제공해야 하는 부분에 대해 논의하겠습니다
그리고 탭부터 드래그 앤 드롭까지 손쉽게 사용할 수 있는 풍부한 상호작용을 구축하는 방법을 살펴보겠습니다
SwiftUI는 처음부터 기본 뷰에 대한 손쉬운 사용을 지원하고 손쉬운 사용 기술에 추가 정보를 쉽게 제공하여 앱의 경험을 개선할 수 있도록 설계되었습니다 저는 제가 가장 좋아하는 여가 생활의 기억을 공유하기 위해 SwiftUI에서 앱을 만들고 있습니다 바로 해변 방문이죠 이 앱으로 아름다운 바다 풍경을 묘사하는 메시지를 보내고 탭 한 번으로 위치를 첨부하고 파도 소리를 녹음하거나 해변에 평점을 매길 수 있습니다 모든 사람이 해변 여행에 댓글을 달 수 있고, 저는 댓글에 즐겨찾기를 표시하고 답변할 수 있죠
다른 친구들이 댓글을 달 때 맞춤 사운드를 만들 수도 있습니다!
이런 것도 좋지만, 지금은 SwiftUI가 손쉬운 사용을 제공하는 방법을 보겠습니다 SwiftUI는 주요 산출물 중 하나로 손쉬운 사용 요소를 생성합니다 손쉬운 사용 요소는 VoiceOver, 음성 명령 스위치 제어와 같은 손쉬운 사용 기술이 앱 콘텐츠를 표시하고 상호작용하는 데 사용하는 기본 구성 요소입니다 손쉬운 사용 요소는 하나 이상의 뷰를 나타내며 뷰의 콘텐츠를 설명하는 속성과 탭부터 복잡한 제스처까지 뷰와 상호작용할 수 있는 방법을 노출하는 동작을 제공합니다
VoiceOver와 같은 손쉬운 사용 기술은 요소를 통해서만 앱과 상호작용하므로 요소에 포함된 뷰의 콘텐츠만 액세스할 수 있습니다
이 토글 화면은 제가 올린 여행에 대한 댓글 달기 가능 여부를 제어하는 곳입니다
이 토글은 뷰 본문에서 선언되고 SwiftUI에서는 이를 사용하여 화면에 표시되는 내용과 손쉬운 사용 요소 출력을 구성합니다
손쉬운 사용 요소의 경우 Comments 레이블을 사용해 요소의 레이블을 만듭니다 isToggle와 isSelected 특성이 요소에 적용됩니다 그리고 설정을 토글하기 위한 press 동작이 제공됩니다
VoiceOver가 토글에 초점을 맞추면 요소가 설명되고 두 번 탭하면 누르기 동작을 수행합니다 “설정, 머리말, 댓글 전환 버튼 끔, 켜기”
SwiftUI에서는 텍스트와 토글을 하나의 요소로 결합합니다 여러 뷰를 하나의 요소로 표시해 탐색을 간소화하고 관련 정보를 연결할 수 있습니다 내 앱에 맞게 토글의 모양과 느낌을 맞춤화할 때 해당 요소의 속성과 동작을 유지하는 것이 중요합니다 SwiftUI의 강력한 뷰 스타일링 시스템은 내장된 손쉬운 사용 기능을 계속 지원하면서 뷰의 시각적 표시 방식을 변경할 수 있는 핵심 요소입니다
뷰의 시각적 스타일을 변경할 때에도 속성과 동작이 뷰에 적용됩니다
토글 스타일 한정자를 사용해 스타일을 변경하면 토글의 비주얼이 업데이트되지만 요소의 레이블, 특성은 유지됩니다
그리고 맞춤화 토글을 탭하면 해당 요소의 속성이 토글의 새 상태를 반영합니다
뷰 스타일은 버튼 및 토글 같은 제어기 레이블 및 레이블이 지정된 콘텐츠 같은 그룹화, 진행 상황 뷰와 게이지 같은 표시기에서 지원됩니다 맞춤화 뷰를 만들기보다는 가능한 한 스타일링 시스템을 사용하세요 그렇지 않으면 자동으로 적용될 수 있는 동일한 손쉬운 사용 속성을 다시 적용해야 합니다 스타일과 내장된 뷰를 사용하면 앱에서 멋진 손쉬운 사용 환경을 제공할 수 있습니다 하지만 손쉬운 사용에 대한 정보를 SwiftUI에 더 많이 제공할수록 더 나은 경험을 제공할 수 있습니다 손쉬운 사용 한정자를 사용해 이 정보를 제공할 수 있습니다
손쉬운 사용 한정자를 사용하면 손쉬운 사용 요소로 뷰 표시 방식을 맞춤화할 수 있습니다 레이블, 특성 등의 속성을 뷰에 추가할 수 있습니다 또한 맞춤형 제스처와 같은 상호작용 동작을 노출할 수 있습니다
한정자를 사용하면 뷰를 단일 요소로 결합해 탐색을 개선할 수도 있습니다
한정자를 사용해 뷰의 손쉬운 사용을 맞춤화하기 전에 앱의 환경을 개선할 수 있는 부분을 파악하는 가장 좋은 방법은 VoiceOver 같은 기술과 함께 사용하는 것입니다
제 앱에서 저의 최근 해변 여행을 VoiceOver로 살펴보고 개선할 점을 알아봅시다
“Weekend adventure, 머리말” “Saturday, It was a beautiful weekend! The waves were calm, the sun was shining on the bridge, and there wasn't a cloud in the sky 이미지” “Looks like a wonderful time, 즐겨찾기 버튼, 댓글, 버튼, Nick”
“Absolutely beautiful, 즐겨찾기 버튼, 댓글, 버튼, Jack”
SwiftUI가 제 앱에 좋은 경험을 선사했습니다 VoiceOver로 모든 콘텐츠를 탐색할 수 있었고 머리말과 버튼에 대한 표시기 등 개선 사항이 제공되었습니다 개선할 수 있는 부분이 세 가지 있었습니다 먼저 댓글 섹션에 요소가 너무 많아 탐색에 시간이 오래 걸렸습니다 또한 각 댓글과 관련된 버튼이 무엇인지 헷갈렸습니다 또한 읽지 않음 표시기와 같이 VoiceOver가 설명하지 않는 뷰도 일부 있었습니다 이 환경을 더 개선할 수 있는 방법을 살펴봅시다
여기 제 댓글 뷰입니다 댓글의 메시지를 나타내는 요소와 읽지 않음 표시기 댓글을 달고 즐겨찾기에 추가하는 버튼이 가로로 쌓여 있습니다 SwiftUI에서는 이 뷰를 위해 제목에 하나, 동작 버튼에 두 개, 이렇게 손쉬운 사용 요소 3개를 만듭니다 스택과 스페이서 뷰는 뷰의 시각적 레이아웃을 제어할 뿐 요소가 아닙니다 읽지 않음 표시기는 도형 뷰로 표시되기 때문에 기본적으로 손쉬운 사용 요소가 없어 먼저 댓글이 읽지 않은 상태인지 설명하는 레이블을 추가합니다
손쉬운 사용 레이블 한정자에는 기본적으로 내 표시기 뷰에 대한 요소가 없으므로 먼저 요소를 생성하고 요소의 레이블을 읽지 않음으로 설정합니다
표시기 뷰가 읽지 않은 상태이면 손쉬운 사용 기술이 항상 요소를 볼 수 있습니다 댓글을 읽을 때 SwiftUI가 도움을 줍니다 댓글을 읽으면 표시기의 불투명도가 0이 되어 시각적으로 숨겨집니다 또한 SwiftUI가 손쉬운 사용 기술에서 해당 요소를 자동으로 숨깁니다
댓글 뷰 탐색을 단순화하기 위해 각 스택을 단일 요소로 만들고 각 버튼을 이 요소에 대한 동작으로 바꿀 수 있습니다 accessibilityElement, children combine 한정자를 적용하면 여러 요소의 속성과 동작이 함께 결합됩니다
요소 레이블은 모든 뷰 레이블이 결합된 레이블이 됩니다 중요한 특성이 병합되고 버튼과 같은 제어기의 모든 동작이 맞춤형 동작으로 바뀝니다 이제 단일 요소가 읽지 않은 메시지와 해당 작업을 모두 나타내어 댓글을 훨씬 더 쉽게 탐색할 수 있습니다 VoiceOver 각 댓글을 다시 탐색해 보면 훨씬 쉽게 이해할 수 있습니다 “읽지 않음, Looks like a wonderful time Nick, 버튼” “즐겨찾기, 댓글”
SwiftUI가 제 뷰의 손쉬운 사용을 위해 많은 일을 해 주었지만 적절한 맥락에서 손쉬운 사용 기술에 더 많은 정보를 제공하면 누락된 콘텐츠를 수정하고 제공할 수 있습니다 해변 여행 메시지를 크게 개선했지만 친구들의 댓글에 더 큰 관심을 주고 싶습니다
앱에서 마음에 드는 댓글을 두 번 탭하면 특별 즐겨찾기 댓글이 됩니다 특별 즐겨찾기는 새로운 기호로 중요도를 표시하지만 이러한 변경으로 새로운 문제가 발생합니다 이전에는 즐겨찾기에 조합된 동작이 올바르게 레이블이 지정되었지만 이제 VoiceOver에서 특별 즐겨찾기의 이름을 반짝반짝이라고 알려 줍니다 “읽지 않음, absolutely beautiful Jack, 버튼”
“즐겨찾기” “댓글” “읽지 않음, Hope you wore sunscreen Beth, 버튼”
“반짝반짝, 댓글“
뷰에서 기호를 사용할 때 별표나 댓글 기호처럼 의미를 유추할 수 있는 기본 레이블이 제공되는 경우가 많습니다 콘텐츠를 상징하는 경우 명시적인 레이블을 추가해야 하며 그 이름을 레이블로 대체하는 경우도 있습니다 뷰에서 기호의 기본 레이블을 사용하는 경우에도 항상 손쉬운 사용 기술과 함께 사용하여 인터페이스를 정확하게 설명하는지 확인하세요 여기서는 레이블이 잘못 지정된 반짝반짝 기호를 업데이트하면 됩니다
한정자로 손쉬운 사용 레이블을 추가할 수 있지만, 이 레이블이 적용되면 특별 즐겨찾기가 아닐 때 SwiftUI에서 이미 제공하는 올바른 기본값을 재정의하게 됩니다
iOS 18에서는 이제 손쉬운 사용 한정자에 isEnabled 매개변수를 추가할 수 있습니다 한정자가 활성화되면 해당 속성이 뷰에 적용되고 그렇지 않은 경우 한정자가 적용되지 않습니다 여기서는 댓글이 특별 즐겨찾기에 해당하는 경우에만 레이블이 적용되며 특별 즐겨찾기에 해당하지 않는 경우 SwiftUI가 별 모양 기호의 기본 레이블로 대체합니다 이 기능은 SwiftUI가 기본적으로 올바른 손쉬운 사용을 제공하지만 조건부로 맞춤화해야 하는 경우 유용합니다
제 앱의 댓글 뷰로 돌아가면 특별 즐겨찾기 동작이 돌아왔고 레이블도 완벽합니다 “읽지 않음, Hope you wore sunscreen Beth, 버튼” “특별 즐겨찾기, 댓글
레이블과 같이 누락된 정보는 항상 추가해야 하지만 상호작용이 필요한 여러 정보를 표시하는 더 간단한 방법을 제공하면 손쉬운 사용 기술로 앱을 사용하는 경험을 향상시킬 수 있습니다 이러한 개선 사항을 살펴볼 수 있는 좋은 기회는 호버, 키 누르기 또는 제스처로 뷰가 동적으로 표시되는 경우입니다 제 앱을 macOS로 가져오는 작업에서 추가하고 싶은 상호작용이 있는데 호버하여 여행 첨부 정보를 보여주는 기능입니다
여행 이미지 뷰로 커서를 가져가면 위치, 기록, 해변의 평점을 포함한 첨부 파일 버튼이 나타납니다 이렇게 하면 깔끔한 화면에서 해변의 아름다운 사진에 집중할 수 있지만 이러한 첨부 파일 액세스를 위해 몇 가지 고려해야 할 사항이 있습니다 호버 동작에 반응하는 뷰처럼 동적으로 표시되는 콘텐츠는 손쉬운 사용 기술이나 키보드나 스위치처럼 터치할 수 있는 대체 입력 소스로 탐색하는 데 시간이 더 오래 걸릴 수 있습니다
마우스 커서를 움직이면 뷰가 나타나고 몇 초 만에 상호작용을 할 수 있습니다
하지만 VoiceOver가 이 뷰로 이동하려면 호버 동작에 반응하는 뷰에 초점을 맞추고 명령을 사용하여 호버 동작을 호출하고 뷰가 나타나면 하위 뷰로 이동한 뒤 상호작용 초점이 원래 뷰로 돌아가야 하는 많은 단계를 수행해야 합니다
이 상호작용은 커서를 가져갈 수 있는 뷰의 손쉬운 사용 속성과 동작을 기본 뷰의 일부로 추가하여 간소화할 수 있습니다
모든 여행에 녹음이나 위치가 첨부되어 있는 것은 아닙니다 따라서 여행 뷰에 추가할 작업 또는 레이블의 정확한 수는 유동적입니다 SwiftUI에서는 이러한 경우에 맞는 손쉬운 사용 한정자를 제공합니다 동작부터 시작하겠습니다
이것은 여행 뷰에 대한 호버 동작에 반응하는 오버레이입니다 이러한 제어기에 더 쉽게 액세스하도록 여행 뷰에서 맞춤형 동작으로 전환할 수 있습니다
손쉬운 사용 작업 한정자는 첨부 파일에 동일한 뷰를 허용합니다 제어기를 추출하여 내 여행 뷰에서 맞춤형 작업으로 변환합니다 이제 오버레이와 여행 뷰의 맞춤형 작업에 대해 같은 로직을 공유할 수 있습니다 이 변경으로 이제 VoiceOver는 오버레이 없이도 각 첨부 파일을 수행할 수 있습니다
“Saturday, It was a beautiful weekend! The waves were calm the sun was shining on the bridge, and there wasn't a cloud in the sky 이미지” “Actions 메뉴, 항목 2개 location, recording”
하나 빠진 부분이 있네요 해변에 대한 제 평점입니다 평점은 커서를 올리면 나타나지만 제 여행의 가장 중요한 부분이라 여행의 손쉬운 사용 요소에서 더욱 눈에 띄게 만들고 싶습니다
중요한 정보에 여러 번의 상호작용이 필요하다면 해당 정보를 시각적으로는 포함하지 않지만 탐색하기 쉬운 요소에 추가하는 것이 좋습니다 여러 요소를 결합하는 등 요소의 속성을 수정한 경우 콘텐츠를 상징하는 장식용 뷰에서 제공하는 추가 콘텐츠를 포함할 수 있습니다
이 두 가지 경우 모두 정적이지 않을 수 있는 콘텐츠로 뷰의 레이블을 수정해야 합니다 SwiftUI에는 이 두 가지 경우를 모두 처리할 수 있는 도구가 있습니다
여행 요소의 레이블에 평점을 추가해서 VoiceOver로 쉽게 찾을 수 있게 하겠습니다
일반 레이블 한정자를 사용하면 SwiftUI가 요소에 이미 제공하는 훌륭한 설명 콘텐츠가 사라집니다 그리고 평점은 가끔씩만 있기 때문에 일부 메시지에만 포함하고 싶습니다
이제 손쉬운 사용 레이블 한정자는 뷰를 허용하고 뷰에서 텍스트를 추출하여 여행 요소의 레이블에 설정합니다 뷰의 기존 레이블에 대한 자리 표시자도 제공하므로 내 여행에 대한 평가를 SwiftUI가 여행 요소에 대해 만든 기존 레이블과 결합할 수 있습니다
이제 여행 뷰로 돌아가면 VoiceOver가 여행의 평점과 게시물을 하나의 레이블로 올바르게 읽습니다 “별 반 개, Saturday It was a beautiful weekend! The waves were calm the sun was shining on the bridge and there wasn't a cloud in the sky 이미지”
좋습니다 커서 올리기는 이러한 API 사용을 고려할 수 있는 시작점 중 하나입니다 VoiceOver와 같은 기술에서 콘텐츠의 모든 변경 사항에 액세스 가능한지 확인하세요
앱에 상호작용을 구축할 때는 터치나 마우스를 대체하는 형태의 입력을 위한 훌륭한 경험을 디자인하는 것이 중요합니다
여러분이 구축할 많은 경험의 핵심이 되는 상호작용 중 하나는 드래그 앤 드롭입니다
VoiceOver, 음성 명령 등의 손쉬운 사용 기술은 드래그 앤 드롭에서도 작동하며 SwiftUI에서는 ‘on drag’ 및 ‘on drop’ 한정자로 손쉬운 사용 경험을 지원합니다
드래그 앤 드롭 영역은 유연하므로 여러 방법으로 앱의 경험을 향상시킬 수 있습니다 제 앱에서는 드래그 앤 드롭을 사용해 댓글이 달리면 맞춤형 알림이 생성됩니다 VoiceOver로 해보겠습니다 연락처 하나에 최대 3개의 사운드를 드래그해 알림을 만들 수 있습니다 맞춤형 드롭 위임자가 단일 뷰에서 여러 드롭 지점을 지원해 VoiceOver는 사운드를 드롭할 수 있는 여러 위치가 어디인지 알지 못합니다 “Synth, 드래그 가능” “드래그”
“Cheers, 드래그 가능, 알림, 사운드 없음”
“드롭, 드롭 준비” “드롭 완료”
손쉬운 사용 끌어서 놓기 지점 한정자는 뷰에서 드래그 또는 드롭할 수 있는 지점을 정의합니다 댓글 알림 뷰에서 알림에 설정할 수 있는 세 가지 다른 소리를 설명하는 드롭 지점을 세 개 정의했습니다 각 지점에는 수행될 상호작용을 설명하는 레이블도 제공됩니다 이제 VoiceOver가 알림 뷰에서 각 드롭 지점에 접근할 수 있습니다 “Synth, 드래그 가능” “드래그”
“Cheers, 드래그 가능, 알림, 종, 새소리” “드롭, 드롭 준비, 사운드 3 설정, 드롭 준비
“드롭 완료”
새로운 환경은 아주 멋집니다 앱 내에서 연락처로 소리를 빠르게 드래그할 수 있습니다
드래그 앤 드롭 경험을 만들 때는 항상 손쉬운 사용 기술을 사용해 제공되는 기능을 사용할 수 있는지 확인하는 것이 중요합니다 드래그할 수 있는 위치나 동작이 한정되어 있는 경우 드래그 앤 드롭 외의 다른 맞춤형 동작을 고려할 수도 있습니다
그리고 요소가 여러 지점에서 여러 상호작용을 지원하는 경우 각 지점이 손쉬운 사용 기술에 노출되도록 하세요 상호작용은 앱의 범위를 벗어나 위젯으로 확장될 수 있습니다 위젯에서는 뷰가 실시간으로 업데이트되지 않아 손쉬운 사용 작업 한정자가 작동하지 않습니다 앱 인텐트를 사용하면 위젯에서 버튼 및 토글과 같은 상호작용 뷰를 만들 수 있지만 추가 맞춤화 작업을 통해 위젯 경험을 향상시킬 수 있는 부분이 있을 수 있습니다 저는 가고 싶은 해변을 빠르게 평가하고 게시할 수 있는 앱의 새로운 위젯을 개발 중입니다 탭 한 번으로 좋아하는 해변의 순위를 업데이트할 수도 있습니다 각 버튼은 SwiftUI로 레이블이 지정되어 있으며 VoiceOver로 활성화할 수 있습니다
이 뷰에는 해변을 게시하고 순위를 매기는 상호작용 버튼 세 개가 있습니다 위젯의 가장 중요한 기능을 수행하는 맞춤형 동작을 통해 위젯 사용 경험을 더욱 향상시키고 싶습니다
이제 손쉬운 사용 동작 한정자는 앱 인텐트를 허용하며 호출되면 인텐트를 수행하고 위젯을 업데이트합니다
여기 추가한 맞춤형 동작은 좋아하는 해변을 설정하는 동작과 두 번 탭하여 사진을 찍는 매직 탭 동작입니다
이러한 변경을 통해 이제 좋아하는 해변을 표시하면 목록의 맨 위로 이동합니다 “Funston beach, 평점 없음, 버튼” “Baker beach, 평점 없음, 버튼” “즐겨찾기”
“Baker beach, 즐겨찾기, 버튼” 앱에 손쉬운 사용을 도입하는 방법에 대해 살펴보았으니 여러분의 앱에도 VoiceOver와 같은 손쉬운 사용 기술을 적용시켜 보세요 손쉬운 사용 API가 앱의 경험을 더욱 개선하는 데 도움이 될 부분을 살펴보세요 손쉬운 사용 샘플 프로젝트도 살펴보고 손쉬운 사용 API를 사용하여 SwiftUI의 일반적인 패턴을 다듬는 방법을 자세히 알아보세요 Accessibility 팀은 여러분이 앱에 구축할 놀라운 손쉬운 사용 경험을 기대하고 있습니다 감사합니다
-
-
7:27 - Accessibility Label Modifier & Opacity
struct UnreadIndicatorView: View { var isUnread: Bool var body: some View { Circle() .foregroundStyle(.blue) .accessibilityLabel("Unread") .opacity(isUnread ? 1 : 0) } }
-
8:02 - Accessibility Element Children Combine Modifier
var body: some View { HStack { UnreadIndicatorView(isUnread: message.isUnread) MessageContentsView(message: message) Spacer() Button(action: favorite) { favoriteLabel } Button(action: reply) { replyLabel } } .accessibilityElement(children: .combine) }
-
10:37 - Accessibility Conditional Modifiers
var body: some View { Button(action: favorite) { Image(systemName: isSuperFavorite ? "sparkles" : "star.fill") } .accessibilityLabel("Super Favorite", isEnabled: isSuperFavorite) }
-
13:38 - Accessibility Actions Modifier
var body: some View { TripView(trip: trip) .onHover { showAttachments = $0 } .overlay { MessageAttachments(attachments: trip.attachments) .opacity(showAttachments ? 1 : 0) } .accessibilityActions { MessageAttachments(attachments: trip.attachments) } }
-
15:16 - Accessibility Label Modifier
var body: some View { TripView(trip: trip) .accessibilityLabel { label in if let rating = trip.rating { Text(rating) } label } }
-
17:42 - Accessibility Drop Point Modifier
var body: some View { CommentAlertView(contact: contact) .onDrop(of: [.audio], delegate: delegate) .accessibilityDropPoint(.leading, description: "Set Sound 1") .accessibilityDropPoint(.center, description: "Set Sound 2") .accessibilityDropPoint(.trailing, description: "Set Sound 3") }
-
19:45 - Accessibility App Intent Action Modifier
var body: some View { ForEach(beaches) { beach in BeachView(beach) .accessibilityAction( named: "Favorite", intent: ToggleRatingIntent(beach: beach, rating: .fullStar)) .accessibilityAction( .magicTap, intent: ComposeIntent(type: .photo)) } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.