SwiftUI 框架

SwiftUI Navigation 框架的新功能 讓我們更有效率地導航視圖

一直以來,NavigationView 一直都是 SwiftUI Navigation 框架的致命弱點,它不同的問題總是讓我們逼不得已改用 UINavigationController。在 iOS 16 中,Apple 推出了新的 Navigation API,讓我們可以更有效率地導航視圖。
SwiftUI Navigation 框架的新功能 讓我們更有效率地導航視圖
SwiftUI Navigation 框架的新功能 讓我們更有效率地導航視圖
In: SwiftUI 框架
本篇原文(標題:A First Look at SwiftUI NavigationStack, NavigationPath, and “navigationDestination”)刊登於作者 Medium,由 Anupam Chugh 所著,並授權翻譯及轉載。

自推出以來,NavigationView 一直都是 SwiftUI Navigation 框架的致命弱點。它之前不支援 NavigationLink 中延遲載入目標視圖(雖然後來解決了這個問題)、以及無法以編程方式導航 Deep Link 等問題,總是讓我們逼不得已改用 UINavigationController

幸好,在 iOS 16 中,Apple 推出了一個以數據驅動的新導航結構,與之前以視圖驅動的結構不同。

新的 Navigation API 有幾個重要的改善,包括一個 NavigationStack,讓開發者可以從堆疊 (stack) 中推送和彈出視圖,一個 NavigationPath,用於管理 routing 堆疊,以及一個修飾符 navigationDestination,用來以編程方式有效率地導航視圖。而在這次更新中,Apple 也棄用了 NavigationView

簡單介紹 NavigationStack

要在視圖層次結構 (hierarchy) 堆疊中匯入 NavigationStack,步驟非常簡單:

NavigationStack {
       NavigationLink {
            Text("Destination Screen")
       } label: {
            Text("Goto Next Screen")
       }
}

在一般情況下,我們可以利用新的 NavigationStack 容器 (container) 直接重構舊的 NavigationView。不過,讓我們看看新 NavigationLink 的 init 語法。

現在的 API 有一個 value-label 語法,label 承載連結的內容視圖,而 value 則包含目標視圖的 builder。

另一個值得注意的地方,是 NavigationLink 的舊 init 方法如 NavigationLink(isActive:destination:label:) 已被棄用。

同樣地,在 iOS 16 中,NavigationLink(destination:tag:selection:) 也被棄用了。也就是說,我們需要完全不同的程式碼,來重構建基於 NavigationLink 程序化導航。

利用 navigationDestination 修飾符來程序化導航

在前文中,我們看到了如何在 init 程式碼中設置 NavigationLink 的目標視圖。但是,大家還記得在 iOS 16 之前,要構建複雜的導航結構時,isActive boolean flag 為我們帶來多少痛苦嗎?

幸好,從 iOS 16 開始,我們可以在 .navigationDestination 修飾符中設置目標視圖。有了 navigationDestination,我們就可以根據類型 (type),以編程方式 route 到不同的畫面。我們還可以為不同視圖類型,添加多個 navigationDestination 修飾符。

在以下的範例中,我們會構建一個 SwiftUI 導航 App,這個 App 會在目標畫面上構建同樣的列表視圖:

NavigationStack {
    RowListsView()
    .navigationDestination(for: Int.self) { i in
        RowListsView()
    }
}

之前,我們會利用 tags 來 route NavigationLink。

在 iOS 16 之後,我們有新的 NavigationPath,來保存與在 NavigationStack 中顯示的視圖有關的類型擦除 (type erased) 數據。

NavigationPath 的強大之處,在於它能夠輕鬆地從堆疊中推送或彈出屬於不同數據類型的視圖。

讓我們看看以下的範例 App,我們在 NavigationStack 中插入了 NavigationPath

final class Router: ObservableObject {
    @Published var path = NavigationPath()
}

struct ContentView: View {
    
    @EnvironmentObject var router: Router
    
    var body: some View {
        NavigationStack(path: $router.path) {
            RowListsView()
            .navigationDestination(for: Int.self) { i in
                RowListsView()
            }
        }
        .environmentObject(router)
    }
}

我們把 NavigationPath 設置在 ObservableObject 類別 (class) 中,並把它設置在 EnvironmentObject 中,以便將其傳遞給子視圖。不過,我們也可以使用 @Binding 來達到同樣的效果。

你可以留意一下修改後的 NavigationStack(path:) init。

以下是 RowListsView 的程式碼:

struct RowListsView : View{
    
    @EnvironmentObject var router: Router
    
    var body: some View{
        
        Form{
            List(1..<5) { i in
                NavigationLink(value: i) {
                    Text("\(i)")
                }
            }
            Section{
                
                if router.path.count > 0
                {
                    Button("Screen count \(router.path.count)"){
                        print("")
                    }
                    
                    Button("Pop to root", role: .destructive){
                        router.path = .init()
                    }
                    
                    Button("Jump back two screens", role: .none){
                        
                        if router.path.count >= 2{
                            router.path.removeLast(2)
                        }
                        else if router.path.count >= 1{
                            router.path.removeLast(1)
                        }
                    }
                }
            }
        }
    }
}

我們稍微修改了上面的 SwiftUI 視圖,加入了客製化的 back 按鈕,這些按鈕會以編程方式改變 NavigationPath,最後改變了視圖 NavigationStack

我們可以看到,只要調用以下程式碼,就可以簡單地彈回 root:

router.path = .init()

我們可以長按 back 按鈕,來顯示不同畫面標題的下拉選單,讓我們可能選擇彈回任何畫面。 現在,標題會預設顯示為 back,但我們可以設置 .navigationTitle(string:) 修飾符來客製化標題。

總結

以上就是新的 SwiftUI NavigationStack 的簡介,我們可以利用新模式做很多事情,像是處理 deep link 等。

此外,你也可以參考這個開源程式庫,來把新的 Navigation API backport 到之前的 iOS 版本。

這篇文章到此為止。你可以在 GitHub 上參閱完整的 SwiftUI 程式碼。

本篇原文(標題:A First Look at SwiftUI NavigationStack, NavigationPath, and “navigationDestination”)刊登於作者 Medium,由 Anupam Chugh 所著,並授權翻譯及轉載。
作者簡介:Anupam Chugh,Better Programming 的 Debugger,一位獨立的 iOS 開發者。小學老師給我起的別名是:Mr. Chatterbox。linktr.ee/anupamchugh
譯者簡介:Kelly Chan-AppCoda 編輯小姐。
作者
AppCoda 編輯團隊
此文章為客座或轉載文章,由作者授權刊登,AppCoda編輯團隊編輯。有關文章詳情,請參考文首或文末的簡介。
評論
更多來自 AppCoda 中文版
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。