欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SwiftUI自定義導(dǎo)航的方法實例

 更新時間:2022年06月06日 11:00:39   作者:Swift君  
導(dǎo)航是我們平時經(jīng)常會遇到的一個需求,下面這篇文章主要給大家介紹了關(guān)于SwiftUI自定義導(dǎo)航的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

默認(rèn)情況下,SwiftUI提供的各種導(dǎo)航API在很大程度上是以用戶直接輸入為中心的——也就是說,導(dǎo)航是在系統(tǒng)響應(yīng)例如按鈕的點擊和標(biāo)簽切換等事件時由系統(tǒng)本身處理的。

然而,有時我們可能想更直接地控制應(yīng)用程序的導(dǎo)航執(zhí)行方式,盡管SwiftUI在這方面仍然不如UIKit或AppKit靈活,但它確實提供了相當(dāng)多的方法,讓我們在構(gòu)建的視圖中執(zhí)行完全自定義的導(dǎo)航。

切換標(biāo)簽(tabs)

讓我們先來看看我們?nèi)绾文芸刂飘?dāng)前在??TabView???中顯示的標(biāo)簽。通常情況下,當(dāng)用戶手動點擊每個標(biāo)簽欄中的一個項目時,標(biāo)簽就會被切換,但是通過在一個給定的??TabView???中注入一個選擇(??selection???)綁定,我們可以觀察并控制當(dāng)前顯示的標(biāo)簽。在這里,我們要做的就是在兩個標(biāo)簽之間切換,這兩個標(biāo)簽是用整數(shù)??0???和??1??標(biāo)記的:復(fù)制

struct RootView: View {
? ? @State private var activeTabIndex = 0
? ? var body: some View {
? ? ? ? TabView(selection: $activeTabIndex) {
? ? ? ? ? ? Button("Switch to tab B") {
? ? ? ? ? ? ? ? activeTabIndex = 1
? ? ? ? ? ? }
? ? ? ? ? ? .tag(0)
? ? ? ? ? ? .tabItem { Label("Tab A", systemImage: "a.circle") }

? ? ? ? ? ? Button("Switch to tab A") {
? ? ? ? ? ? ? ? activeTabIndex = 0
? ? ? ? ? ? }
? ? ? ? ? ? .tag(1)
? ? ? ? ? ? .tabItem { Label("Tab B", systemImage: "b.circle") }
? ? ? ? }
? ? }
}

但真正好的地方是,在識別和切換標(biāo)簽時,我們并不僅僅局限于使用整數(shù)。相反,我們可以自由地使用任何??Hashable???值來表示每個標(biāo)簽——例如通過使用一個枚舉,其中包含我們想要顯示的每個標(biāo)簽的情況。然后我們可以將這部分狀態(tài)封裝在一個??ObservableObject??中,這樣我們就可以很容易地注入到我們的視圖層次環(huán)境中:

enum Tab {
? ? case home
? ? case search
? ? case settings
}
class TabController: ObservableObject {
? ? @Published var activeTab = Tab.home
? ? func open(_ tab: Tab) {
? ? ? ? activeTab = tab
? ? }
}

有了上述內(nèi)容,我們現(xiàn)在可以用新的??Tab???類型來標(biāo)記??TabView???中的每個視圖,如果我們再把??TabController??注入到視圖層次結(jié)構(gòu)的環(huán)境中,那么其中的任何視圖都可以隨時切換顯示的Tab。

struct RootView: View {
? ? @StateObject private var tabController = TabController()
? ? var body: some View {
? ? ? ? TabView(selection: $tabController.activeTab) {
? ? ? ? ? ? HomeView()
? ? ? ? ? ? ? ? .tag(Tab.home)
? ? ? ? ? ? ? ? .tabItem { Label("Home", systemImage: "house") }
? ? ? ? ? ? SearchView()
? ? ? ? ? ? ? ? .tag(Tab.search)
? ? ? ? ? ? ? ? .tabItem { Label("Search", systemImage: "magnifyingglass") }
? ? ? ? ? ? SettingsView()
? ? ? ? ? ? ? ? .tag(Tab.settings)
? ? ? ? ? ? ? ? .tabItem { Label("Settings", systemImage: "gearshape") }
? ? ? ? }
? ? ? ? .environmentObject(tabController)
? ? }
}

例如,現(xiàn)在我們的??HomeView???可以使用一個完全自定義的按鈕切換到設(shè)置標(biāo)簽——它只需要從環(huán)境中獲取我們的??TabController???,然后它可以調(diào)用??open??方法來執(zhí)行標(biāo)簽切換,像這樣:

struct HomeView: View {
? ? @EnvironmentObject private var tabController: TabController
? ? var body: some View {
? ? ? ? ScrollView {
? ? ? ? ? ? ...
? ? ? ? ? ? Button("Open settings") {
? ? ? ? ? ? ? ? tabController.open(.settings)
? ? ? ? ? ? }
? ? ? ? }
? ? }
}

很好! 另外,由于??TabController???是一個完全由我們控制的對象,我們也可以用它來切換主視圖層次結(jié)構(gòu)以外的標(biāo)簽。例如,我們可能想根據(jù)推送通知或其他類型的服務(wù)器事件來切換標(biāo)簽,現(xiàn)在可以通過調(diào)用上述視圖代碼中的相同的??open??方法來完成。

要了解更多關(guān)于環(huán)境對象以及SwiftUI狀態(tài)管理系統(tǒng)的其余部分,請查看本指南。

控制導(dǎo)航堆棧

就像標(biāo)簽視圖一樣,SwiftUI的??NavigationView???也可以被編程自定義控制。例如,假設(shè)我們正在開發(fā)一個應(yīng)用程序,在其主導(dǎo)航堆棧中顯示一個日歷視圖作為根視圖,然后用戶可以通過點擊位于該應(yīng)用程序?qū)Ш綑谥械木庉嫲粹o來打開一個日歷編輯視圖。為了連接這兩個視圖,我們使用了一個??NavigationLink??,每當(dāng)點擊一個給定的視圖時,它就會自動將其壓入到導(dǎo)航棧中:

struct RootView: View {
? ? @ObservedObject var calendarController: CalendarController
? ? var body: some View {
? ? ? ? NavigationView {
? ? ? ? ? ? CalendarView(
? ? ? ? ? ? ? ? calendar: calendarController.calendar
? ? ? ? ? ? )
? ? ? ? ? ? .toolbar {
? ? ? ? ? ? ? ? ToolbarItem(placement: .navigationBarTrailing) {
? ? ? ? ? ? ? ? ? ? NavigationLink("Edit") {
? ? ? ? ? ? ? CalendarEditView(
? ? ? ? ? ? ? ? ? calendar: $calendarController.calendar
? ? ? ? ? ? ? )
? ? ? ? ? ? ? .navigationTitle("Edit your calendar")
? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? .navigationTitle("Your calendar")
? ? ? ? }
? ? ? ? .navigationViewStyle(.stack)
? ? }
}

在這種情況下,我們在所有設(shè)備上使用堆棧式導(dǎo)航風(fēng)格,甚至是iPad,而不是讓系統(tǒng)選擇使用哪種導(dǎo)航風(fēng)格。

現(xiàn)在我們假設(shè),我們想讓我們的??CalendarView???以自定義方式顯示其編輯視圖,而不需要構(gòu)建一個單獨的實例。要做到這一點,我們可以在編輯按鈕的??NavigationLink???中注入一個??isActive???綁定,然后將其傳遞給我們的??CalendarView??:

struct RootView: View {
? ? @ObservedObject var calendarController: CalendarController
? ? @State private var isEditViewShown = false
? ? var body: some View {
? ? ? ? NavigationView {
? ? ? ? ? ? CalendarView(
? ? ? ? ? ? ? ? calendar: calendarController.calendar,
? ? ? ? ? ? ? ? isEditViewShown: $isEditViewShown
? ? ? ? ? ? )
? ? ? ? ? ? .toolbar {
? ? ? ? ? ? ? ? ToolbarItem(placement: .navigationBarTrailing) {
? ? ? ? ? ? ? ? ? ? NavigationLink("Edit", isActive: $isEditViewShown) {
? ? ? ? ? ? ? ? ? ? ? ? CalendarEditView(
? ? ? ? ? ? ? ? ? ? ? ? ? ? calendar: $calendarController.calendar
? ? ? ? ? ? ? ? ? ? ? ? )
? ? ? ? ? ? ? ? ? ? ? ? .navigationTitle("Edit your calendar")
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? .navigationTitle("Your calendar")
? ? ? ? }
? ? ? ? .navigationViewStyle(.stack)
? ? }
}

如果我們現(xiàn)在也更新??CalendarView???,使其使用??@Binding???綁定屬性接受上述值,那么現(xiàn)在只要我們想顯示我們的編輯視圖,就可以簡單地將該屬性設(shè)置為??true???,我們的根視圖的??NavigationLink??將自動被觸發(fā):

struct CalendarView: View {
    var calendar: Calendar
    @Binding var isEditViewShown: Bool
    var body: some View {
        ScrollView {
            ...
            Button("Edit calendar settings") {
                isEditViewShown = true
            }
        }
    }
}

當(dāng)然,我們也可以選擇將??isEditViewShown???屬性封裝在某種形式的??ObservableObject???中,例如??NavigationController???,就像我們之前處理??TabView??時那樣。

這就是我們?nèi)绾我宰远x編程方式觸發(fā)顯示在我們的用戶界面中的??NavigationLink??——但如果我們想在不給用戶任何直接控制的情況下執(zhí)行這種導(dǎo)航呢?

例如,我們現(xiàn)在假設(shè)我們正在開發(fā)一個包括導(dǎo)出功能的視頻編輯應(yīng)用程序。當(dāng)用戶進(jìn)入導(dǎo)出流程時,一個??VideoExportView???被顯示為模態(tài),一旦導(dǎo)出操作完成,我們想把??VideoExportFinishedView??推送到該模態(tài)的導(dǎo)航棧中。

最初,這可能看起來非常棘手,因為(由于SwiftUI是一個聲明式的UI框架)沒有??push???方法,當(dāng)我們想在導(dǎo)航棧中添加一個新視圖時,我們可以調(diào)用該方法。事實上,在??NavigationView???中顯示一個新視圖的唯一內(nèi)置方法是使用??NavigationLink??,它需要成為我們視圖層次結(jié)構(gòu)本身的一部分。

也就是說,這些??NavigationLink??實際上不一定是可見的——所以在這種情況下,實現(xiàn)我們目標(biāo)的一個方法是在我們的視圖中添加一個隱藏的導(dǎo)航鏈接,然后我們可以在視頻導(dǎo)出操作完成后以編程方式觸發(fā)該鏈接。如果我們也在我們的目標(biāo)視圖中隱藏系統(tǒng)提供的返回按鈕,那么我們就可以完全鎖定用戶能夠在這兩個視圖之間手動導(dǎo)航:

struct VideoExportView: View {
? ? @ObservedObject var exporter: VideoExporter
? ? @State private var didFinish = false
? ? @Environment(\.presentationMode) private var presentationMode
? ? var body: some View {
? ? ? ? NavigationView {
? ? ? ? ? ? VStack {
? ? ? ? ? ? ? ? ...
? ? ? ? ? ? ? ? Button("Export") {
? ? ? ? ? ? ? ? ? ? exporter.export {
? ? didFinish = true
}
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? .disabled(exporter.isExporting)

? ? ? ? ? ? ? ? NavigationLink("Hidden finish link", isActive: $didFinish) {
? ? ? ? ? ? ? ? ? ? VideoExportFinishedView(doneAction: {
? ? ? ? ? ? ? ? ? ? ? ? presentationMode.wrappedValue.dismiss()
? ? ? ? ? ? ? ? ? ? })
? ? ? ? ? ? ? ? ? ? .navigationTitle("Export completed")
? ? ? ? ? ? ? ? ? ? .navigationBarBackButtonHidden(true)
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? .hidden()
? ? ? ? ? ? }
? ? ? ? ? ? .navigationTitle("Export this video")
? ? ? ? }
? ? ? ? .navigationViewStyle(.stack)
? ? }
}
struct VideoExportFinishedView: View {
? ? var doneAction: () -> Void

? ? var body: some View {
? ? ? ? VStack {
? ? ? ? ? ? Label("Your video was exported", systemImage: "checkmark.circle")
? ? ? ? ? ? ...
? ? ? ? ? ? Button("Done", action: doneAction)
? ? ? ? }
? ? }
}

我們在??VideoExportFinishedView???中注入一個??doedAction???閉包,而不是讓它檢索當(dāng)前的??presentationMode??本身,是因為我們希望解耦整個模態(tài)流程,而不僅僅是那個特定的視圖。要了解更多信息,請查看 "解耦SwiftUI模態(tài)或詳細(xì)視圖"。

使用這樣一個隱藏的??NavigationLink??絕對可以被認(rèn)為是一個有點 "黑 "的解決方案,但它的效果非常好,如果我們把一個導(dǎo)航鏈接看成是導(dǎo)航堆棧中兩個視圖之間的連接(而不僅僅是一個按鈕),那么上述設(shè)置可以說是有意義的。

小結(jié)

盡管SwiftUI的導(dǎo)航系統(tǒng)仍然不如UIKit和AppKit提供的系統(tǒng)靈活,但它已經(jīng)足夠強大,可以滿足很多不同的使用情——-特別是當(dāng)與SwiftUI非常全面的狀態(tài)管理系統(tǒng)相結(jié)合時。

當(dāng)然,我們也可以選擇將我們的SwiftUI視圖層次包裹在托管控制器中,只使用UIKit/AppKit來實現(xiàn)我們的導(dǎo)航代碼。哪種解決方案是最合適的,可能取決于我們在每個項目中實際想要執(zhí)行多少自定義和程序化的導(dǎo)航。

到此這篇關(guān)于SwiftUI自定義導(dǎo)航的文章就介紹到這了,更多相關(guān)SwiftUI自定義導(dǎo)航內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳談swift內(nèi)存管理中的引用計數(shù)

    詳談swift內(nèi)存管理中的引用計數(shù)

    下面小編就為大家?guī)硪黄斦剆wift內(nèi)存管理中的引用計數(shù)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • Swift map和filter函數(shù)原型基礎(chǔ)示例

    Swift map和filter函數(shù)原型基礎(chǔ)示例

    這篇文章主要為大家介紹了Swift map和filter函數(shù)原型基礎(chǔ)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • Swift中重寫和重載的使用與對比總結(jié)

    Swift中重寫和重載的使用與對比總結(jié)

    這篇文章主要給大家介紹了關(guān)于Swift中重寫和重載的使用方法,以及重寫和重載的對比分析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-10-10
  • Swift中內(nèi)置的集合類型學(xué)習(xí)筆記

    Swift中內(nèi)置的集合類型學(xué)習(xí)筆記

    Swift中自帶數(shù)組、set、字典三大集合類型,這里將學(xué)習(xí)過程中的基礎(chǔ)的Swift中內(nèi)置的集合類型學(xué)習(xí)筆記進(jìn)行整理,需要的朋友可以參考下
    2016-06-06
  • R.swift的使用與安裝教程

    R.swift的使用與安裝教程

    這篇文章主要給大家介紹了關(guān)于R.swift使用與安裝的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • Swift中常量和變量的區(qū)別與聲明詳解

    Swift中常量和變量的區(qū)別與聲明詳解

    Swift語言同樣和Java和OC等語言一樣是同樣是需要聲明常量和變量的,下面就讓我們來學(xué)習(xí)一下Swift的常量和變量。這篇文章主要給大家介紹了關(guān)于Swift中常量和變量的區(qū)別與聲明的相關(guān)資料,需要的朋友可以參考下。
    2017-11-11
  • 使用Swift實現(xiàn)iOScollectionView廣告無限滾動效果(DEMO)

    使用Swift實現(xiàn)iOScollectionView廣告無限滾動效果(DEMO)

    本文給大家分享使用Swift實現(xiàn)iOScollectionView廣告無限滾動效果(DEMO),非常不錯,具有一定的參考借鑒價值,感興趣的朋友一起看看吧
    2016-11-11
  • Swift算法之棧和隊列的實現(xiàn)方法示例

    Swift算法之棧和隊列的實現(xiàn)方法示例

    Swift語言中沒有內(nèi)設(shè)的棧和隊列,很多擴展庫中使用Generic Type來實現(xiàn)?;蚴顷犃小O旅孢@篇文章就來給大家詳細(xì)介紹了Swift算法之棧和隊列的實現(xiàn)方法,需要的朋友可以參考學(xué)習(xí),下面來一起看看吧。
    2017-03-03
  • IOS 實現(xiàn)簡單的彈幕功能

    IOS 實現(xiàn)簡單的彈幕功能

    本文主要介紹IOS 實現(xiàn)彈幕功能,這里給大家一個實例來展現(xiàn)彈幕功能,有需要的小伙伴可以參考下
    2016-07-07
  • Swift在控件中添加點擊手勢的方法

    Swift在控件中添加點擊手勢的方法

    這篇文章主要介紹了Swift在控件中添加點擊手勢的方法,本文講解如何在tableview的headerview中添加點擊手勢的方法,需要的朋友可以參考下
    2015-01-01

最新評論