스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
iPad의 SwiftUI: 인터페이스 구조화
SwiftUI 목록 및 표를 통해 iPad 앱 인터페이스를 강화할 시간입니다. 선택 상호 작용 및 컨텍스트 메뉴를 추가하고 내 앱 사용자의 생산성을 높일 수 있도록 지원하는 방법을 보여드립니다. 또한 탐색 구조화에 대한 모범 사례를 제시하고 Split View를 사용하여 모달리티를 방지함으로써 최고의 데스크탑급 iPad 경험을 제공할 수 있는 방법을 살펴보겠습니다. 이것은 2부작 시리즈의 첫 번째 세션입니다. 이 비디오를 최대한 활용하려면 SwiftUI의 몇 가지 기본 사항을 알아두시는 것이 좋습니다. 이 세션을 시청하고 나면 ‘iPad의 SwiftUI: 도구 막대, 제목 등 추가'를 확인하여 SwiftUI를 통해 iPad 앱의 도구 막대 성능을 향상하는 방법을 알아보세요.
리소스
관련 비디오
WWDC22
- 탐색을 위한 SwiftUI 쿡북
- iPad 앱 디자인의 새로운 기능
- iPad의 SwiftUI: 도구 막대, 제목 등 추가
- SwiftUI 앱에 여러 윈도우 구현
- SwiftUI의 새로운 기능
- WWDC22 넷째 날 요약
WWDC21
-
다운로드
♪ ♪
안녕하세요, 'SwiftUI로 iPad 인터페이스 정리하기'를 시작하겠습니다 제 이름은 Raj고 SwiftUI를 담당하죠 iPadOS 16 업데이트로 풍부한 기능을 포함한 생산적이고 전문적인 앱을 만들 수 있습니다 이번 세션에서는 그중 일부인 iPad에서도 멋지게 작동하는 SwiftUI 인터페이스 정리를 다루죠 먼저 목록과 표에 관해 소개해 드리고 SwiftUI 선택 모형과 선택 영역에 메뉴를 통합하는 방법을 다루겠습니다 마지막으로 Split View 기능으로 iPad 앱 내비게이션의 구조를 만드는 방법을 다루도록 하죠 하지만 더 있습니다 이 영상은 2부로 구성된 시리즈의 첫 세션인데요 2부에서는 제 동료인 Harry가 도구모음, 제목 등에 관해 소개할 겁니다 해리는 SwiftUI iPad 앱을 다음 단계로 발전시키는 주요 업데이트를 다룰 예정이죠 두 강의를 모두 시청하시기 바랍니다 그러면 목록과 표부터 시작하죠 최근 독서 모임에 가입했는데 뒤처지고 있습니다 책을 읽을 수 있는 조용한 곳을 찾기가 힘들죠 그래서 독서에 집중하기 위해 조용한 곳을 검색해 주는 앱을 만들기 시작했습니다 조용한 곳은 독서의 오아시스여서 책을 순식간에 읽을 수 있죠 이 앱은 제가 찾은 조용한 곳을 기록해 둡니다 iPhone을 위한 앱을 만들었는데 iPad 용으로 업데이트하여 큰 화면을 활용하는 것도 괜찮겠죠 iPad에 맞춘 앱을 만든 뒤 개발을 더 진행하여 Mac 용으로 발전시킬 수도 있을 겁니다 이 세션에서는 Mac을 직접적으로 다루지는 않지만 화면에 나오는 API는 macOS에도 적용됩니다 지금까지 제가 찾은 조용한 장소의 목록이죠 이 목록은 앱을 업데이트하기에 좋은 시발점입니다 iPad 버전 작업을 시작했어요 나쁘지는 않지만 큰 화면을 활용하지 못하고 있죠 낭비하는 공간이 많고 정보 집약도가 낮습니다 다행스럽게도 iPadOS 16에 좋은 해결책이 있습니다 이렇게 정보 집약적인 상황에 말이죠 다단 표를 사용하는 겁니다 어떤 모습인지 보여 드리죠 SwiftUI API의 다단 표를 적용한 '모든 장소' 화면입니다 앞으로 몇 분간 작업해서 이 스크린숏처럼 만들 거예요 SwiftUI의 다단 표는 macOS Monterey에 도입됐죠 iPadOS 16부터 이 API가 iPad에도 제공됩니다 Mac처럼 iPad의 표도 다단 기능과 정렬을 지원하죠 iPad에 표를 추가한 것과 더불어 이제 SwiftUI가 iPad와 Mac의 표의 섹션을 지원합니다
'Mac의 SwiftUI: 기초부터 만들기' 세션에서 소개한 표에 관한 지침은 계속 iPad에 적용되니까 아직 못 보셨다면 그 세션도 시청하십시오
그러면 iPhone 목록에서 시작하여 아까 보여 드린 표를 만들죠 처음 보여 드린 장소 목록의 코드입니다
일단 목록을 표로 바꿀게요 표는 목록과 구조가 다릅니다 뷰 빌더 대신 컬럼 빌더를 사용하죠
첫 번째 컬럼은 장소의 이름을 추가할게요 컬럼에는 제목과 뷰 빌더에 쓰일 이름을 입력해야 컬렉션의 각 요소에 이름이 나타납니다 값의 키 경로도 지정했는데 표의 정렬을 추가할 때 중요한 요소가 되죠 뷰 빌더도 목록 기반의 구조와 비슷하다는 걸 알 수 있죠 이전에 사용한 PlaceCell 유형도 다시 사용할 수 있습니다
작은 크기의 클래스에서는 표가 첫 번째 컬럼만 보여 주는데 iPhone과 iPad의 Slide Over에서도 표가 멋지게 나오죠
모습만 보면 목록과 비슷한 것 같지만 단순히 목록을 표로 대체한 게 아닙니다 표를 재활용하여 사이즈 클래스를 전환할 때도 스크롤 위치나 선택 영역을 유지하기 때문이죠 따라서 첫 번째 컬럼은 작은 화면을 고려하여 사용하시고 iPad 앱은 Slide Over와 같은 다양한 환경에서 테스트하시기 바랍니다
다음으로 넘어가죠 이제 편안함과 소음 수준 컬럼을 추가할게요 텍스트만 들어가는 컬럼의 경우 TableColumn의 API를 통해 값이 스트링일 때는 뷰 빌더를 삭제할 수 있습니다 또한 편안한 정도를 나타내는 컬럼은 많은 공간을 요구하지 않아서 폭을 고정했습니다
comparator를 이용하여 테이블에 정렬을 추가할 수 있죠 comparator를 저장할 상태를 만들겠습니다 여기서 상태는 배열인데 표의 모든 comparator를 대표하기 때문이죠 또한 이름 comparator에 초깃값을 설정하면 처음 표가 나타날 때부터 정렬돼 있습니다
다음은 상태를 표에 바인딩하여 모든 걸 구성할게요
각 컬럼은 비교 가능 영역에 주요 경로로 값을 특정하므로 정렬이 가능한 것이 기본값입니다 이제 이름, 편안함, 소음에 따라 표를 정렬할 수 있죠 표가 알아서 정렬하지 않습니다 저에게 달렸죠 onChange 제어자로 바뀐 기준에 따라 정렬할 수 있죠
그러면 테스트해 봅시다
표가 아주 멋지네요 장소의 데이터도 보여 주고 큰 화면을 잘 활용하고 있습니다 Mac과 달리 iPad는 가로로 스크롤 되지 않으므로 컬럼의 수를 제한하는 것이 중요하죠 그래야 모든 컬럼이 한 화면에 나타납니다 각 컬럼의 이름은 제목 영역에 나타나죠 이름을 탭 하면 해당 컬럼을 정렬합니다 소음도에 따라 정렬할 수도 있죠
Slide Over 상태에서는 표가 단일 컬럼으로 줄어들어 모든 정보를 축소한 형태로 나타냅니다 이제 목록을 표로 만들었으니 선택으로 넘어가죠 이 섹션에서는 SwiftUI의 선택 모형을 검토하고 선택 영역에 메뉴를 넣는 법을 다루겠습니다 그 과정에서 장소 표에 다양한 기능을 추가할 텐데요 먼저 SwiftUI에서 선택의 원리에 관해 얘기하죠 SwiftUI는 목록을 관리하고 표를 선택하는 API를 제공합니다 여기 몇 개 행이 있는 목록이 있는데 각 행에는 태그가 있죠 각 행 고유의 태그로 목록에서 선택을 관리합니다 이 도표에서 태그는 초록색 원으로 표현했죠
태그와 함께 선택 값을 가지고 있는 상태도 있습니다 태그값을 가지고 있는 유형이죠 예를 들어 여러 개를 선택했을 때 선택된 행의 태그를 가지고 있는 세트입니다 목록은 각 행의 태그와 선택 상태의 관계를 조율하는 일을 하죠 이는 선택 바인딩을 통해 처리합니다 이 예시에서처럼 2번 행을 선택했을 때 목록이 선택 바인딩을 통해 세트에 추가하죠 그와 유사하게 앱의 다른 부분이 프로그램적으로 세트를 바꿔 3행을 추가한다고 하면 선택 바인딩이 바뀌므로 목록이 이를 선택합니다 일반적인 모델로 iOS와 macOS가 동일하죠 선택은 두 부분으로 나뉩니다 태그와 상태죠 다음은 태그의 기원에 관해 이야기하겠습니다 태그는 선택할 수 있는 컨테이너 안의 뷰 값으로 해당 뷰가 선택됐는지 추적하는 역할을 합니다 대개는 SwiftUI가 자동으로 이런 태그를 통합하죠 태그는 식별자와 비슷하지만 같은 개념은 아닙니다 ForEach를 사용하면 SwiftUI는 자동으로 명시적인 식별자에서 태그를 생성하죠 표는 행 값의 식별자를 선택 태그로 사용합니다 장소 앱에서는 장소 구조체의 식별자 유형을 사용하죠 명시적인 식별자에 관한 추가 정보가 궁금하시면 'SwiftUI 파헤치기'를 시청하십시오
수동으로 뷰를 태그하려면 태그 제어자를 사용하세요 이것이 ForEach의 역할이죠 태그 제어자는 Hashable 값을 사용합니다 하지만 태그 제어자를 사용할 때 주의하십시오 선택할 수 있는 컨테이너 안의 모든 뷰가 같은 종류의 태그를 사용해야 합니다 그렇지 않으면 SwiftUI가 뷰를 선택하는 법을 모를 수 있습니다 ID 제어자를 사용할 때는 태그를 설정하지 않습니다 지금까지 태그를 알아봤고 앞에서 본 도표로 돌아가죠
이 도표의 태그 부분을 설명했으니 선택 원리의 다른 부분을 이야기하겠습니다 선택 상태죠 이전에는 세트를 사용했지만 다른 방법도 있습니다
이 데이터 구조를 이용하여 선택을 나타낼 수도 있죠 SwiftUI는 macOS Ventura에서 처음으로 단일 선택을 지원하며 macOS 사이드바의 필수 선택과 다중 선택을 지원합니다
iPadOS 16은 손쉬운 다중 선택을 도입하죠 이제 여러 행을 선택할 때 키보드가 연결돼 있으면 편집 모드로 들어갈 필요가 없어 모달리티를 피할 수 있죠 키보드를 사용할 때는 기본 단축 키인 시프트나 커맨드로 선택 영역의 확장과 수정할 수 있습니다 포인터로도 활용할 수 있죠 선택 기능을 적용한 장소 표의 모습입니다 이 예시에서는 키보드와 트랙 패드를 연결하여 행이 들어가 있지 않지만 선택 상태입니다 하지만 터치를 사용하면 편집 모드로 들어가야 합니다 두 손가락으로 팬 하여 빠르게 할 수 있죠 SwiftUI는 이 제스처를 자동으로 지원합니다 편집 모드 얘기를 한 김에 단일 선택과 편집 모드의 변경 사항도 알려 드리죠 iOS 16 출시로 iPhone과 iPad에서 목록 선택 시 단일 행을 선택할 때는 편집 모드를 요구하지 않습니다 업데이트된 내비게이션 API와 함께 사용하면 정말 유용하죠
모든 업데이트는 이 표로 나타낼 수 있는데 이전 테이블에 편집 모드 컬럼을 추가했죠 키보드가 없으면 다중 선택 때만 편집 모드를 요구합니다 이제 장소 표가 선택을 지원하도록 업데이트할게요 장소 표에 선택을 추가하는 건 쉬운 작업인데 선택을 저장할 상태만 추가하면 됩니다 상태를 만든 후에는 표의 이니셜라이저에 바인딩을 통과시킬 거예요 테이블은 선택 종류와 행 식별자를 맞추도록 하는데 저는 장소 ID 종류를 선택 종류로 사용했습니다 저는 다중 선택을 원해서 선택 상태에 세트를 사용했죠 표가 행을 자동으로 태그하므로 직접 태그할 필요는 없습니다
이제 표의 행을 선택할 수 있습니다 하지만 선택 후 행동을 지정하지 않았죠 제 생각에는 선택한 장소를 가이드에 추가하여 독서 모임의 다른 사람들과 공유하는 버튼이 있으면 좋겠네요 이건 도구모음 버튼을 추가하는 코드입니다 선택 영역이 비어 있지 않으면 버튼이 나타나죠 편집 버튼도 추가하여 손쉬운 선택 지원 기능을 보완하되 키보드가 없을 때도 편집 모드를 드나드는 방법을 제공합니다 좋은 iPad 앱은 키보드와 상관없이 유용하죠 따라서 편집 모드를 전환하는 컨트롤을 제공해야 합니다
거의 끝났어요 이제 행을 선택했을 때 나타나는 버튼과 편집 모드를 전환하는 버튼이 생겼습니다 이 시리즈의 두 번째 세션을 꼭 시청하셔서 도구모음에 관한 정보를 알아보십시오 도구모음 버튼도 만족스럽지만 더 추가할 수 있죠 선택과 관련한 액션은 최대한 쉽게 접근하도록 배치하는 것이 좋습니다 그래서 iOS 16, iPadOS 16 macOS Ventura에서 SwiftUI가 다중 선택 컨텍스트 메뉴를 지원하죠 다중 선택 컨텍스트 메뉴는 선택된 식별자 세트에서 컨텍스트 메뉴가 작동하도록 합니다 이 표의 구조를 살펴보면서 더 깊이 알아보죠
항목 기반의 컨텍스트 메뉴에는 세 종류가 있습니다 먼저 여러 항목의 메뉴를 선택 영역 위에 보여 주거나
단일 항목 위에 컨텍스트 메뉴를 보여 주거나
콘텐츠가 없는 빈 곳에 컨텍스트 메뉴를 보여 줄 수도 있죠
장소 표에 이런 기능을 추가합시다
이전 코드 예시 중 일부를 제외하여 컨텍스트 메뉴에 집중할게요 선택 종류를 지정하는 contextMenu 제어자를 추가했는데 목록이나 표의 선택 유형과 맞아야 합니다 현재 표를 사용 중이니 PlaceID를 사용할게요
이 클로저는 항목 세트에 전달되는데 이것이 비어 있다면 메뉴가 빈 곳에 나타나죠 새 장소를 추가하는 버튼은 빈 곳에 나타나는 게 좋겠네요 그러면 책을 읽을 수 있는 새로운 장소를 발견했을 때 빠르게 추가할 수 있죠 항목이 비어 있는 세트의 뷰 빌더가 뷰로 resolve 되지 않으면 SwiftUI가 빈 곳에 메뉴를 표시하지 않습니다 이제 단일 선택을 추가하죠 만약 세트의 항목이 1개면 한 장소에 대한 메뉴가 나타나겠죠 단일 선택과 다중 선택 상황 모두 장소를 가이드에 추가하는 기능이 필요하므로 메뉴에 새로운 뷰를 추가하겠습니다 지금까지 작업한 걸 확인해 보죠 먼저 새로운 컨텍스트 메뉴가 추가됐습니다 빈 곳을 클릭하면 새 장소를 추가하는 메뉴가 나오죠 단일 행을 선택하면 그 행의 컨텍스트 메뉴가 나옵니다 키보드로 선택 영역을 확장하면 파란색 표시가 나타나는데 여러 행에 대한 컨텍스트 메뉴를 활성화하여 가이드에 장소를 추가할 수 있습니다
이제 표가 아주 멋지군요 표에 구조를 추가할 시간입니다 그러려면 Split View를 추가해야 하죠 내비게이션은 iPad의 기본적인 경험입니다 Split View는 iPad의 큰 화면을 활용하여 추가 조작 없이 많은 정보를 한 번에 표시할 수 있죠 이 섹션에서는 SwiftUI의 내비게이션과 Split View에 관한 업데이트 내용을 다루겠습니다 이전 섹션에서는 장소 표에 선택과 편집 모드 같은 새로운 기능을 추가했죠 하지만 장소 앱에 구조가 부족합니다 그래서 이번 섹션에서는 Split View 기능을 활용하여 앱 구조의 기초를 만들어 보죠 iPadOS 16과 macOS Ventura에 새로 적용된 SwiftUI의 Split View 지원이 개선됐습니다 NavigationSplitView 유형이 생겼죠 SwiftUI는 2-3개 컬럼의 Split View를 지원하며 컬럼을 표시하는 복합적인 방법과 스타일을 사용할 수 있습니다 이 세션에서는 내비게이션 콘텐츠 표현 방법을 자세히 다루지 않으니까 'SwiftUI 내비게이션 쿡북'을 확인하시기 바랍니다 커트가 쓴 레시피에 맛깔나는 내비게이션 경험을 위한 방법이 적혀 있죠 저는 Split View에 집중할게요 iPad의 2열 Split View에 관한 도표입니다 SwiftUI에서는 왼쪽 컬럼을 사이드바 컬럼이라고 하고 옆에 있는 컬럼을 상세 컬럼이라고 하죠 두 컬럼이 서로 균형을 이루고 있습니다 가로 화면에서는 SwiftUI가 이걸 기본값으로 하지만 세로 화면에서는 사이드바가 가려지고 상세 컬럼만 남죠 사이드바 버튼을 누르면 사이드바가 나타나는데 어둡게 변하는 상세 컬럼 위에 나타납니다
일반적으로 2열 Split View는 공간이 부족할 때 상세 컬럼만 보여 주는데 상세 컬럼이 사이드바 컬럼에 비해 더 중요한 정보를 담고 있기 때문이죠 이 동작을 커스터마이징하고 싶다면 NavigationSplitView의 prominentDetail로 상세 컬럼을 항상 위에 둘 수 있습니다 아니면 NavigationSplitView의 balanced로 비중을 조정할 수 있죠 NavigationSplitView는 3열 Split View도 지원합니다 3열에는 사이드바와 상세 컬럼 사이에 콘텐츠 컬럼이 추가되는데요 UIKit를 사용하신다면 보조 컬럼으로 알고 계실 겁니다 가로 화면에서는 콘텐츠와 상세 컬럼이 보이고 사이드바는 토글 할 수 있죠 도구모음 버튼을 탭 하면 상세 컬럼이 밖으로 나가면서 사이드바와 콘텐츠 쪽에 공간을 마련합니다 세로 화면에서는 상세 컬럼만 보이는데요 도구모음 버튼을 누르면 콘텐츠가 나타납니다 거기서 한 번 더 탭 하면 사이드바가 나오죠 사이드바와 콘텐츠 컬럼이 모두 상세 컬럼을 덮습니다
3열 Split View의 경우 기본값을 유지하는 걸 추천하고 싶은데 주어진 공간을 최대한 활용하고 큰 화면에 최적화되어 있기 때문이죠 2열 Split View처럼 3열 Split View도 작은 화면에서는 겹쳐서 나타납니다 이제 Split View의 기초를 다뤘으니 장소 앱에도 추가할게요 이것은 ContentView입니다 열이 2개인 NavigationSplitView를 만들었죠 첫 번째 컬럼은 사이드바 컬럼이고 두 번째 컬럼은 상세 컬럼입니다 상세 컬럼은 사이드바 컬럼의 링크로 가득한데 아무것도 보여 줄 게 없다면 '장소를 선택하세요'라는 기본 문구가 표시되죠
기본 문구를 보여 주는 스크린샷인데, 꽤 괜찮죠 자동 스타일을 사용하여 가로 화면의 사이드바가 보이지만 세로 화면에서는 사라집니다 사이드바의 행을 탭 하면 행의 내용이 상세 컬럼에 나오죠 Slide Over를 사용하면 컬럼이 자동으로 사라집니다 이건 빙산의 일각이죠 내비게이션 기능이 많이 추가됐습니다 상태 회복을 위한 지원 개선과 딥 링크, 프로그램적 제어가 더욱 개선됐죠 더 많은 정보는 내비게이션 쿡북 세션에서 확인하십시오
앱에 iPad 기능을 추가했으니 책을 읽을 만한 조용한 곳을 찾겠습니다 독서 모임의 진도를 따라잡으면 좋겠네요 이 세션에서는 데이터를 잘 표현하기 위해 표를 사용하는 방법과 선택 인터랙션을 관리하는 방법 Split View로 모달리티를 피하는 방법을 알아봤죠
관련 세션도 시청하시고 iPad의 성능을 활용한 SwiftUI 앱을 만들어 보십시오
감사합니다 ♪ ♪
-
-
3:10 - Places List
struct PlacesList: View { @Binding var modelData: ModelData var body: some View { List(modelData.places) { place in PlaceCell(place) } } }
-
3:18 - Places Table
struct PlacesTable: View { @Binding var modelData: ModelData @State private var sortOrder = [KeyPathComparator(\Place.name)] var body: some View { Table(modelData.places, sortOrder: $sortOrder) { TableColumn("Name", value: \.name) { place in PlaceCell(place) } TableColumn("Comfort Level", value: \.comfortDescription).width(200) TableColumn("Noise", value: \.noiseLevel) { place in NoiseLevelView(level: place.noiseLevel) } } .onChange(of: sortOrder) { modelData.sort(using: $0) } } }
-
10:25 - Places Table with selection
struct PlacesTable: View { @EnvironmentObject var modelData: ModelData @State private var sortOrder = [KeyPathComparator(\Place.name)] @State private var selection: Set<Place.ID> = [] var body: some View { Table(modelData.places, selection: $selection, sortOrder: $sortOrder) { // columns } } }
-
10:26 - Places Table toolbar additions
Table(modelData.places, selection: $selection, sortOrder: $sortOrder) { ... } .toolbar { ToolbarItemGroup(placement: .navigationBarTrailing) { if !selection.isEmpty { AddToGuideButton(selection) } } ToolbarItemGroup(placement: .navigationBarLeading) { EditButton() } }
-
12:34 - Item context menus
// Item context menus Table(modelData.places, selection: $selection, sortOrder: $sortOrder) { ... } .contextMenu(forSelectionType: Place.ID.self) { items in if items.isEmpty { // Empty area AddPlaceButton() } else { if items.count == 1 { // Single item FavoriteButton(isSet: $modelData.places[items.first!].isFavorite) } // Single and multiple items AddToGuideButton(items) } }
-
16:55 - Navigation Split View example
// Navigation Split View example struct ContentView: View { var body: some View { NavigationSplitView { SidebarView() } detail: { Text("Select a place") } } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.