-
[SwiftUI] Navigation (feat. WWDC2022)iOS 2024. 11. 6. 13:45
이 포스트는 WWDC2022: SwiftUI Cookbook for Navigation 세션의 내용을 공부하며 정리한 글입니다.
잘못 이해한 부분이 있다면 댓글 남겨주세요 :)NewAPIs
- SwiftUI의 Navigation API는 화면전환을 데이터에 기반하여 프로그래밍 방식으로 제어하는 것을 지향한다.
- 기존의 NavigationView에서 NavigationStack으로 변경되면서 좀 더 Programatic한 화면전환이 가능해졌다고 한다.
NavigationStack / NavigationLink
NavigationStack(path: $path) { NavigationLink("Details", value: value) }
2-Column NavigationSplitView
NavigationSplitView { RecipeCategories() } detail: { RecipeGrid }
3-Column NavigationSplitView
NavigationSplitView { RecipeCategories() } content: { RecipeList() } detail: { RecipeDetail() }
Programatic Navigation
Pushable Stack - Destination
- 기존의 NavigationLink를 value와 value에 해당하는 View로 구분
- NavigationDestination의 modifire에 목적지 View를 선언한다.
Pushable Stack - Value
- NavigationLink는 목적지 View로 전달한 value만 표현
NavigationStack 작동방식
- NavigationStack은 스택이 표시하는 모든 데이터들의 path를 추적한다. Stack에 Root만 있는 경우 path는 비어있다.
- NavigationStack은 스택 내부 / 스택에 푸시된 View들의 NavigationDestination들을 추적한다.
- 일반적으로는 목적지들의 Set을 가지고 있다.
- NavigationLink를 클릭하면 Link의 Value가 path에 추가되고 path와 목적지 View를 매핑하여 스택에서 push할 View를 결정한다.
여러 타입의 value를 다루는 방법
- NavigationLink가 표현하는 value를 path 스택에서 관리하며, 만약 여러 타입의 데이터를 관리해야한다면 NavigationPath컬렉션을 사용할 수 있다.
특정 View로 곧바로 Push/Pop하는 방법
- path 배열을 조작하여 스택에서의 순서와 관계없이 특정 View로 이동할 수있다.
2-Column NavigationSplitView
- 컬럼이 2개인 NavigationSplitView 구성 시 NavigationStack 안에 Detail의 View를 구성하면 Detail의 View로 value를 전달할 수 있다.
- @State가 적용된 path 배열을 선언하고 NavigationStack에 바인딩하면 Detail 화면에서 선택한 NavigationLink의 path를 바탕으로 원하는 View로 Push하는 화면전환 로직을 Programatic하게 구현할 수 있다.
DetailView 예시
- DetailView를 NavigationStack으로 감쌌기 때문에 NavigationDestination 모디파이어를 사용해 선택한 path와 일치하는 View로 Push할 수 있다.
NavigationSplitView에서 특정 Detail 화면으로 Push
- 특정한 Detail 화면으로 점프하는 로직 역시 path 배열을 조작하여 구현가능하다
SceneStorage
ScenStorage 사용 방법
- Navigation 상태를 model 타입으로 변환
- NavigationModel을 Codable로 변환
- SceneStorage를 사용해 Model 저장 / 복원
NavigationSplitView에 NavigationModel 적용
- View가 각각의 Navigation 상태(path, category 등)가 아닌 NavigationModel을 의존하는 구조로 변경
NavigationModel with Codable
- Codable에 구현된 encode가 아닌 커스텀 encode를 구현하는 이유
- NavigationModel의 전체 값을 저장하기 않기 위해
- Recipe 데이터베이스는 이미 Recipe의 모든 세부 정보를 갖고 있음. 그런데 이를 또 @SceneStorage에 반복 저장하는 것은 저장소를 적절하게 사용하는 것이 아니다.
- Local 네비게이션 상태를 Recipe 데이터 베이스가 독립적으로 변경할 수 있는 경우에 로컬 네비게이션 상태에 오래된 데이터가 잔존하는 것을 막기 위해
- NavigationModel의 전체 값을 저장하기 않기 위해
CodingKey 추가
- Codingkey를 통해 사용자 지정 코드화
- Recipe가 갖고 있는 ID를 path에 저장할 계획
커스텀 encode 메서드
- 코딩키를 사용해 keyed 컨테이너를 생성
- encodeIfPresent로 category가 nil이 아닌 경우에만 category값 사용
- map으로 recipe의 Id만 container의 key로 사용
- NavigationModel을 JSON으로 인코딩할 수 있음
NavigationModel 생성자
- container가 가진 인코딩된 Recipe.ID들을 디코딩한 후 Recipe 데이터베이스에서 일치하는 모델들을 조회하여 path에 할당
- compactMap을 사용해 찾을 수 없는 레시피들을 recipePathIds에서 폐기
JSON을 read/write 할 연산프로퍼티 추가
@SceneStorage 사용
- 앱에 기존 모델 데이터가 있으면 NavigationModel 업데이트
- NavigationModel이 변경될 때마다 반복되는 비동기 for 루프에서 NavigationModel의 변경사항을 다시 @SceneStorage에 저장
Summary
- NavigationSplitView, NavigationStack, List를 함께 사용할 수 있다.
- navigationDestination은 스택이나 스택의 하위 뷰 내 모든 위치에 있을 수 있지만 일반적으로는 해당하는 NavigationLink부근이 권장된다.
- 단, navigationDestination은 Lazy 컨테이너 안에 사용될 수 없다.
- NavigationSplitView는 디바이스 크기에 맞춰서 레이아웃이 변경되므로 여러 디바이스에서 공통으로 적용할 수 있다.
'iOS' 카테고리의 다른 글
[SwiftUI] UI Update (feat. WWDC2021) (0) 2024.11.03 [SwiftUI] View의 Lifetime (feat. WWDC 2021) (0) 2024.11.01 [SwiftUI] View Identity (feat. WWDC 2021) (0) 2024.10.31 [RxSwift] UILabel이 subscribe를 사용할 수 없는 이유 (0) 2024.07.31 Alamofire를 구조화된 코드로 사용하기 (feat. URLRequsetConvertible) (2) 2024.06.19