ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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 데이터 베이스가 독립적으로 변경할 수 있는 경우에 로컬 네비게이션 상태에 오래된 데이터가 잔존하는 것을 막기 위해

     

    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는 디바이스 크기에 맞춰서 레이아웃이 변경되므로 여러 디바이스에서 공통으로 적용할 수 있다.

    댓글

All Posts are written by Tunastorm