-
Dynamic Type을 위해 tvOS 앱 준비하기
Dynamic Type은 사용자가 자신에게 가장 적합한 텍스트 크기를 선택하여 편하게 읽고 앱과 상호작용할 수 있도록 해 줍니다. 서체 크기 조정을 구현하고 더 큰 콘텐츠에 맞게 레이아웃을 조정하는 실용적인 기법들을 통해 tvOS에서 Dynamic Type을 지원할 수 있도록 앱을 준비하는 방법을 알아봅니다. 또한 그리드와 캐러셀 같은 미디어 중심 인터페이스를 최적화하는 방법을 살펴보며, 다양한 텍스트 크기를 사용하는 모든 사용자에게 멋진 경험을 선사할 수 있습니다.
챕터
- 0:01 - Introduction
- 2:46 - Identify common issues
- 6:13 - Adapt your layout
리소스
관련 비디오
WWDC24
-
비디오 검색…
안녕하세요, 저는 Isis이며, 접근성 팀의 엔지니어링 매니저입니다.
올해 tvOS 27의 접근성에 관한 좋은 소식이 있습니다. 이제 큰 텍스트 지원이 제공됩니다, 플랫폼의 모든 앱에 시스템 전체 텍스트 크기 조절 기능을 제공합니다. 많은 사람들이 다양한 텍스트 크기를 필요로 하거나 선호합니다, 앱을 동적으로 만들면 모든 사람이 훌륭한 경험을 얻을 수 있습니다. tvOS 앱 고객들은 이 접근성 기능을 기다려 왔습니다. 앱에서 이 기능을 최대한 활용하는 방법을 알려드리겠습니다. 또한 더 큰 텍스트에 대한 지원을 표시할 수 있습니다 App Store의 tvOS용 접근성 영양 레이블에서. 이것은 접근 가능한 앱을 특별히 찾는 사용자에게 다가가는 훌륭한 방법입니다.
이 세션에서는 tvOS에서 큰 텍스트가 어떻게 작동하는지 먼저 다루겠습니다 그리고 사람들이 어디서 찾을 수 있는지에 대해. 그런 다음 앱의 작은 부분을 식별하는 것으로 넘어가겠습니다 더 큰 텍스트 크기에 맞게 조정이 필요할 수 있는. 마지막으로, 몇 가지 예시를 살펴보겠습니다 텍스트 크기에 맞게 레이아웃을 조정하는 방법에 대한.
개요부터 시작하겠습니다. iOS의 Dynamic Type에 익숙하다면, 이미 한 발 앞서 있습니다! Dynamic Type은 tvOS에서도 동일하게 작동합니다. UIKit과 SwiftUI는 텍스트 크기를 자동으로 조정할 수 있습니다 사람들의 선호도에 따라. 이를 통해 사람들이 텍스트 크기를 제어할 수 있습니다 자신의 필요와 편안함에 맞게. 설정 앱에서 더 큰 텍스트 크기를 켤 수 있습니다. 접근성, 디스플레이, 텍스트 크기 순으로 탐색하여. 텍스트 크기를 선택하는 옵션이 있습니다 Large부터 접근성 XXXL까지. 저는 미디어 앱을 개발하고 있습니다 사람들이 좋아하는 영화를 발견하고 시청할 수 있는. 이 앱이 더 큰 텍스트 크기에 어떻게 적응하는지 살펴보겠습니다. 이 미디어 앱은 탐색을 위한 상단 탭 바가 있습니다. 큰 제목과 몇 가지 액션 버튼이 있으며, 그 아래 컬렉션 뷰에서 탐색할 수 있는 영화 포스터 갤러리가 있습니다. 더 큰 텍스트 크기를 켜면, 상단의 탐색 제목이 훨씬 커집니다. 탭 바에서 각 레이블이 이제 더 커지고, 인터페이스 전체의 모든 텍스트가 크게 확대됩니다. 표준 UIKit 및 SwiftUI 컴포넌트 Labels, Buttons, Navigation tabBars와 같은 이를 자동으로 처리합니다. 앱의 경우, 여러분이 할 일은 식별하고 업데이트하는 것입니다 주의가 필요한 커스텀 요소들을.
큰 텍스트로 앱을 검토할 때, 식별해야 할 몇 가지 유형의 문제가 있습니다. 앱이 더 큰 텍스트 크기로 실행될 때, 고정 폰트 크기를 피하도록 해야 합니다 텍스트가 크기 조절되지 않는. dynamic type에 적응하지 않는 인터페이스, 하드코딩된 크기나 제약 조건과 같은, 텍스트 잘림과 같은 문제를 일으킬 수 있습니다 또는 UI 요소 클리핑. 일부 요소가 더 큰 크기로 커질 때 필요한 패딩과 간격을 위해 레이아웃을 조정해야 할 수도 있습니다. 미디어 앱의 예시입니다 영화 설명이 표시되는 곳입니다. 상단에 제목이 표시되고, 하단에 텍스트 레이블과 컨트롤이 있습니다.
텍스트를 가장 큰 크기로 설정하면, 대부분의 텍스트가 더 커집니다… 하지만, 왼쪽에 크기가 조절되지 않는 텍스트 요소가 하나 있습니다. 그것은 "가입 정보"라고 적힌 캡션입니다 가입할 수 있는 컨트롤 위에 있는, 영화를 구매하거나 대여하는. 이런 캡션에는 하드코딩된 텍스트 크기가 있을 수 있습니다 특정 레이아웃 이유로. 예를 들어, 원래 컨테이너 안에 있었을 수 있습니다 정적 크기나 엄격한 제약 조건을 가진. 예측 가능한 레이아웃을 만드는 데 매력적으로 보일 수 있지만, 권장되지 않습니다. 이 접근 방식은 유연성이 부족하여 더 큰 텍스트에 적응하지 못합니다. 대신, 레이아웃을 유연하게 조정하고 하드코딩된 값을 제거하세요. 미디어 앱의 설명 뷰를 나타내는 코드입니다. VStack이 있으며, "가입 정보" 캡션을 포함합니다, 그리고 버튼을 포함하는 HStack 그리고 영화에 대한 기타 텍스트 정보. 이 코드에는 더 큰 텍스트에 대한 몇 가지 다양한 과제가 있습니다. 고정 폰트 크기를 지정하여, 적응하지 않을 것입니다 누군가 크기 선호도를 변경할 때.
그리고 Text 뷰에서, 300 포인트로 고정된 너비 제약 조건이 있습니다.
텍스트가 커지면, 300 포인트가 텍스트에 충분히 넓지 않을 수 있습니다, 텍스트 잘림으로 이어질 수 있습니다. 이 캡션이 더 큰 텍스트 크기에 맞게 조정되도록 하려면, 하드코딩된 폰트 스타일을 의미론적 텍스트 스타일로 교체하겠습니다. 이 경우, "caption"을 사용하겠습니다.
이러한 변경 사항으로, 텍스트 크기가 이제 동적으로 커집니다, 하지만 콘텐츠가 이제 잘립니다.
이를 수정하려면, 고정 너비를 유연한 제약 조건으로 교체하세요 필요에 따라 뷰가 커질 수 있도록. Text 뷰에서, 고정 너비를 교체하겠습니다 maxWidth 매개변수로 그리고 이를 무한대로 설정하겠습니다. 이는 SwiftUI에게 콘텐츠에 필요한 만큼 너비를 사용하도록 지시합니다. 이러한 변경 사항으로, 텍스트 뷰가 더 큰 텍스트와 함께 아름답게 확대됩니다, 그리고 콘텐츠를 표시할 충분한 공간이 있습니다. 완벽합니다! 앱에서 이러한 일반적인 문제 유형을 찾으려면, 하드코딩된 텍스트 크기를 검색하세요 대신 표준 스타일로 마이그레이션하세요.
앱에서 하드코딩된 높이 및 너비 제약 조건을 검색하세요 콘텐츠가 커질 수 있도록 유연한 제약 조건을 사용하세요. UIKit을 사용하는 경우, 접근 방식이 비슷합니다 한 가지 추가 단계가 있습니다. 하드코딩된 폰트를 텍스트 스타일로 교체하세요, 그리고 adjustsFontForContentSizeCategory를 true로 설정하세요. 이는 UIKit에게 기본 설정이 변경될 때 자동으로 업데이트하도록 지시합니다.
때로는 하드코딩된 값을 수정하는 것만으로는 충분하지 않습니다. 더 큰 텍스트에 맞게 레이아웃을 조정하고 싶을 수 있습니다, 기본 크기의 원래 레이아웃을 유지하면서.
이 컬렉션 뷰를 살펴보겠습니다. 각각 아래에 제목이 있는 여섯 개의 영화 포스터를 포함합니다. 표준 텍스트 크기로, 전체 제목이 이미지 아래 공간에 편안하게 맞습니다. 큰 텍스트를 켜면, 가로로 여섯 개의 제목을 맞출 충분한 공간이 없습니다. 텍스트가 클 때 이 레이아웃을 조정해야 합니다. 뷰의 SwiftUI 코드입니다. LazyHStack이 있는 가로 스크롤 뷰를 표시합니다. 컨테이너 안에 각 셀에 대한 버튼이 있습니다.
이 레이아웃을 조정하려면, 먼저 dynamicTypeSize 환경 키 경로를 읽겠습니다. 그런 다음 셀의 columnCount 매개변수를 변경하겠습니다 containerRelativeFrame 수정자에서.
더 큰 텍스트 크기가 활성화될 때, 레이아웃을 6개 대신 한 번에 4개 열을 표시하도록 설정하겠습니다.
이렇게 하면 더 넓은 셀 크기가 제공되고 각 제목이 성장할 공간이 더 많아집니다. 더 긴 텍스트를 수용하려면, 커스텀 마퀴 전략을 고려하세요.
이 앱에서 레이아웃을 조정할 또 다른 예시입니다. 앱의 이 부분에는 다양한 유형의 콘텐츠에 대한 카드가 포함되어 있습니다, 해변 영상이나 캠핑 영상과 같은. 이러한 콘텐츠 카드 각각에는 이미지가 포함되어 있습니다, 제목과 자막. 더 큰 텍스트로는 요소 간에 충분한 시각적 패딩이 없습니다, 그리고 텍스트는 공간이 부족하여 쉽게 잘립니다. 한 가지 해결책은 조건부 레이아웃을 제공하는 것입니다 더 큰 크기가 켜질 때를 위한. SwiftUI에서 조건부 레이아웃을 구현하는 방법입니다. 먼저, dynamicTypeSize 환경 값을 읽으세요 접근성 크기가 켜진 시점을 감지하기 위해. 그런 다음 VStackLayout 또는 HStackLayout을 만드세요 더 큰 텍스트가 사용되는지에 따라. AnyLayout을 사용하면 이 두 가지 유형을 추상화할 수 있습니다. 새 레이아웃을 다른 스택처럼 사용하세요. 텍스트 크기에 따라 가로와 세로 간에 동적으로 전환됩니다 텍스트 크기에 따라. UIKit에서는 UIStackView를 사용하고 axis 속성을 업데이트하세요 preferredContentSizeCategory. isAccessibilityCategory에 따라.
registerForTraitChanges를 호출하고 UITraitPreferredContentSizeCategory를 전달하세요 앱이 실행되는 동안 크기 변경에 따라 레이아웃을 업데이트하기 위해.
앱의 콘텐츠 카드에서 실제로 동작하는 모습입니다. 더 큰 텍스트 크기가 활성화되면, 레이아웃이 세로 스택으로 전환됩니다. 이렇게 하면 제목과 자막이 셀 전체 너비로 확장될 수 있습니다, 이미지와 너비를 공유하는 대신. 이 레이아웃은 또한 더 많은 공간을 제공하기 위해 셀이 더 높아질 수 있게 합니다 콘텐츠를 위한.
이제 tvOS에서 더 큰 텍스트 크기로 앱을 테스트할 준비가 되었습니다. 시스템 텍스트 스타일을 사용하여 개선할 수 있는 곳을 발견하세요 그리고 텍스트 가독성을 우선시하도록 레이아웃을 조정하세요. 행동 계획입니다. 하드코딩된 폰트 대신 표준 텍스트 스타일을 사용하세요.
큰 텍스트를 활성화한 상태로 체계적으로 테스트하세요.
최상의 경험을 위해 필요할 때 레이아웃을 조정하세요. 그리고 더 큰 텍스트에 대한 지원을 표시하세요 tvOS용 앱 접근성 영양 레이블에서.
이제 여러분의 tvOS 앱을 모든 사람이 접근할 수 있도록 만들 차례입니다! 시청해 주셔서 감사합니다!
-
-
4:58 - Adopt standard text styles
// Adopt standard text styles VStack(spacing: 20) { Text("Signup information") .font(.caption.bold()) .lineLimit(1) .foregroundStyle(.secondary) .frame(width: 300, alignment: .leading) HStack(alignment: .top, spacing: 40) { //* ... *// } } -
5:10 - Use flexible constraints
// Adopt standard text styles VStack(spacing: 20) { Text("Signup information") .font(.caption.bold()) .lineLimit(1) .foregroundStyle(.secondary) .frame(maxWidth: .infinity, alignment: .leading) HStack(alignment: .top, spacing: 40) { /* ... */ } } -
5:55 - Dynamic Type with text styles in UIKit
// Hard coded text size in UIKit titleLabel.font = UIFont.boldSystemFont(ofSize: 28) // Dynamic Type with text styles in UIKit titleLabel.font = UIFont.preferredFont(forTextStyle: .headline) titleLabel.adjustsFontForContentSizeCategory = true -
7:09 - Adapt layout in response to dynamic type
// A view that shows a collection of movie posters struct MovieShelf: View { @Environment(\.dynamicTypeSize) private var dynamicTypeSize var body: some View { ScrollView(.horizontal) { LazyHStack(spacing: 40) { ForEach(Asset.allCases) { asset in Button { /* ... */ } label: { asset.portraitImage Text(asset.title) } .containerRelativeFrame( .horizontal, count: dynamicTypeSize.isAccessibilitySize ? 4 : 6, spacing: 40) } } } } } -
8:07 - Provide a conditional layout for when larger sizes are turned on
// A view that shows content in a card struct CardContentView: View { @Environment(\.dynamicTypeSize) private var dynamicTypeSize var asset: Asset var body: some View { let layout = dynamicTypeSize.isAccessibilitySize ? AnyLayout(VStackLayout(alignment: .leading, spacing: 10)) : AnyLayout(HStackLayout(alignment: .top, spacing: 10)) layout { /* ... */ } } } -
8:31 - UIKit adaptive layout that responds to content size changes
// UIKit adaptive layout that responds to content size changes class AdaptiveLayoutViewController: UIViewController { let stackView = UIStackView() override func viewDidLoad() { super.viewDidLoad() updateLayout() let sizeTraits: [UITrait] = [UITraitPreferredContentSizeCategory.self] registerForTraitChanges(sizeTraits, action: #selector(updateLayout)) } private func updateLayout() { if traitCollection.preferredContentSizeCategory.isAccessibilityCategory { stackView.axis = .vertical } else { stackView.axis = .horizontal } } }
-
-
- 0:01 - Introduction
Large Text support is now available system-wide on tvOS 27, allowing apps to automatically scale text to match someone's needs and preferences. By supporting Dynamic Type, your app can adapt its layout and display its accessibility support in its Accessibility Nutrition Labels.
- 2:46 - Identify common issues
When accommodating larger text, avoid fixed font sizes and rigid constraints that prevent text from scaling and cause truncation or clipping. Ensure all UI elements are flexible and responsive rather than relying on hardcoded values that break layouts at larger sizes.
- 6:13 - Adapt your layout
Find and replace hardcoded sizes with standard text styles to allow interfaces to grow. In cases where standard scaling isn't enough, adapt your view's layout dynamically—such as reducing the number of columns in a grid—by checking the current `dynamicTypeSize` environment value.