實戰教程·元宇宙來了,準備好你的電子名片了嗎?(二)

語言: CN / TW / HK

theme: smartblue

前提回顧

在上一章中,我們創建了CardView構件,使用了構件編程方式搭建視圖。使用構件的好處是當我們要整體修改構件內容時,就可以只修改構件的結構體,而無需每個卡片視圖都修改。

示例,在上一章中我們設置了平台圖標圖片,但由於平台圖標樣式的不統一性,看起來有些不協調。這時候,我們可以給CardView構件中的Image控件設置為圓形,讓其更加美觀些,如下圖所示:

對於搭建好的構件,只需要修改CardView構件本身,就可以在引用此構件的所有地方統一修改其樣式。

是不是很方便?接下來讓我們繼續完成下面的內容。

結構化編程:搭建數據模型

使用構件創建身份卡片視圖後,我們發現如果每次要新增一個卡片,就需要在卡片視圖中需要複製CardView構件並賦值。當我們需要創建很多身份卡片時,就會需要很多一樣的代碼,這不夠優雅。

有沒有辦法像搭建CardView構件一樣,只搭建一個結構然後填充數據,讓結構自動“遍歷”出內容?

SwiftUI聲明式語言中,可以使用List列表組件循環遍歷內容,在此之前我們需要定義好數據模型。我們創建一個新的Swift文件,命名為Model,如下圖所示:

Model文件作為我們搭建數據模型的文件,定義數據需要聲明的變量。首先我們引用SwiftUI,如下代碼所示:

import SwiftUI

接下來搭建數據模型,如下代碼所示:

struct Model: Identifiable { var id = UUID() var platformIcon: String var title: String var platformName: String var indexURL: String }

數據模型可以使用Class類或者Struct結構體聲明,這裏我們使用Struct結構體聲明瞭一個數據模型Model,遵循Identifiable協議。“Identifiable可識別”協議可以通過id區分不同的數據,當我們創建的數據中有兩條一模一樣的數據時,由於“Identifiable可識別”協議,系統將賦予不同的id,實現呈現兩條數據內容的效果。

使用Identifiable協議除了聲明需要的參數變量外,還需要在數據模型中聲明一個id,並複製為UUID。該數據模型中的需要使用的變量參數,我們可以直接使用之前在CardView身份卡片構件聲明的參數。

聲明好參數後,我們為展示數據,可以創建一個數據數組作為示例數據,如下代碼所示:

``` // MARK: 示例數據

var models = [ Model(platformIcon: "icon_juejin", title: "移動端簽約作者", platformName: "稀土掘金技術社區", indexURL: "https://juejin.cn/user/3897092103223517"), Model(platformIcon: "icon_csdn", title: "iOS創作者", platformName: "CSDN博客", indexURL: ""), Model(platformIcon: "icon_aliyun", title: "專家博主", platformName: "阿里雲社區", indexURL: ""), Model(platformIcon: "icon_huaweiyun", title: "專家博主", platformName: "華為雲社區", indexURL: ""), ] ```

如此便完成了數據模型的搭建。

卡片視圖:List列表和ForEach循環

我們回到ContentView視圖中,刪除原有的身份卡片視圖的代碼。卡片視圖我們可以看作一個列表,每張身份卡片就是一行行呈現的數據,列表的數據源則來源於上述創建好的數組models,如下圖所示:

List { ForEach(models) { item in CardView(platformIcon: item.platformIcon, title: item.title, platformName: item.platformName, indexURL: item.indexURL) } .listRowBackground(Color.clear) .listRowSeparator(.hidden) }

上述代碼中,我們使用List列表和ForEach循環結構,遍歷models數組中定義好的數據內容,並在調用CardView身份卡片構件的時候賦值為循環後item的一條條數據。

由於演示的iPhonex模擬器,因此為了呈現更好的列表樣式效果,使用listRowBackground列表項背景修飾符,將列表背景顏色修飾為透明。並使用listRowSeparator列表分割線修飾符,隱藏List自帶的分割線。完成這些操作後後,就可以完成展示身份卡片構件的樣式內容。

在模擬器中預覽的效果中,身份卡片左右邊距太寬了,我們可以直接修改CardView構件的代碼,去掉最後的padding邊距,如下圖所示:

導航菜單:標題和新建卡片入口

卡片視圖完善之後,我們來搭建頂部導航菜單。SwiftUI提供了NavigationView導航視圖控件方便我們快速搭建應用的頂部導航菜單。從視圖層級來看,頂部導航菜單是“包裹”整個頁面視圖的,因此NavigationView導航視圖控件需要在ContentView的body視圖最外層,如下代碼所示:

NavigationStack { //視圖內容 .navigationBarTitle("文如秋雨",displayMode: .inline) }

在iOS16版本,使用NavigationStack導航視圖控件搭建頂部導航菜單,iOS16版本以下的機型使用NavigationView導航。

搭建好頂部導航欄後,我們創建一個新的按鈕,作為創建身份卡片的入口。可以使用navigationBarItems導航欄按鈕修飾符創建導航菜單上的按鈕入口,不過在此之前,我們需要搭建好按鈕的視圖,再將按鈕賦予navigationBarItems導航欄按鈕修飾符。如下代碼所示:

``` func addBtn() -> some View { Button(action: {

    }) {
        Image(systemName: "plus.circle.fill")
            .font(.system(size: 17))
            .foregroundColor(.blue)
    }
}

```

上述代碼中,我們完成了一個按鈕視圖addBtn。一般在編程過程中,若視圖中除樣式代碼外還有交互操作,通常會將其抽離出來作為一個單獨的視圖進行構建,然後再在修飾符或者其他視圖中使用,這是SwiftUI開發中經常會遇到的事情。

完成按鈕視圖的構建後,我們將按鈕視圖加到頂部導航菜單中,如下代碼所示:

.navigationBarItems(trailing: addBtn())

模態彈窗:打開一個新頁面

個人主頁部分基本已經完成了,下面我們來完成點擊頂部導航菜單的“添加”按鈕,打開“新建身份卡”頁面。

首先我們先創建一個新的SwiftUI文件,命名為NewView,如下圖所示:

回到ContentView視圖中,使用Sheet模態彈窗控件,創建打開彈窗的樣式,如下代碼所示:

.sheet(isPresented:Is Presented) { Content }

上述代碼中,我們使用sheet修飾符控件搭建打開模態彈窗效果,但需要使用sheet修飾符控件,需要提前聲明好觸發打開動作的變量,如下代碼所示:

@State var showNewView:Bool = false

並將聲明好的觸發變量綁定到sheet修飾符控件中,以及設置Content跳轉的頁面目標為NewView。如下代碼所示:

.sheet(isPresented: $showNewView) { NewView() }

最後在點擊addBtn按鈕視圖時,切換showNewView打開視圖的變量狀態,便可實現點擊“添加按鈕”打開模態彈窗,如下代碼所示:

self.showNewView.toggle()

關閉彈窗的方法也很類似,我們來到NewView視圖,給NewView視圖的內容添加頂部導航菜單,並也創建一個關閉彈窗的按鈕,如下代碼所示:

``` struct NewView: View { var body: some View { NavigationStack { Text("Hello, World!") .navigationBarTitle("添加身份卡", displayMode: .inline) .navigationBarItems(trailing: closeBtn()) } }

func closeBtn() -> some View {
    Button(action: {
    }) {
        Image(systemName: "xmark.circle.fill")
            .font(.system(size: 17))
            .foregroundColor(.gray)
    }
}

} ```

上述代碼中,我們使用的方法和個人主頁的內容基本一致,使用NavigationStack導航菜單控件創建導航菜單,並使用navigationBarTitle頂部導航標題控件創建標題。單獨創建關閉按鈕視圖closeBtn,並將它賦予導航菜單中的navigationBarItems導航菜單欄目修飾符,以創建導航菜單上的按鈕。

關閉彈窗:雙向綁定或全局變量

實現打開模態彈窗後,我們來實現關閉模態彈窗,下面我們來介紹兩種關閉模態彈窗的方法:雙向綁定、全局變量。

由於我們在ContentView個人主頁聲明瞭一個變量showNewView來打開模態彈窗,當點擊addBtn按鈕時更新其狀態。因此我們也可以在NewView頁面中也聲明一個雙向綁定的變量showNewView,並在點擊closeBtn時更新其狀態,實現關閉彈窗的交互。

首先先聲明一個雙向綁定的變量showNewView如下圖所示:

@Binding var showNewView:Bool

然後在點擊closeBtn關閉按鈕時更新其狀態,如下代碼所示:

self.showNewView.toggle()

由於使用@Binding聲明變量,因此在NewView視圖中聲明的變量缺少賦值,將可能導致無法預覽,因此我們在預覽時需要給NewView視圖賦予默認值,如下代碼所示:

NewView(showNewView: .constant(false))

建立雙向綁定的視圖,在所有頁面也都需要進行@Binding聲明變量的綁定,因此我們回到ContentView視圖中,綁定showNewView變量,如下代碼所示:

NewView(showNewView: $showNewView)

完成後,可以在模擬器中操作打開模態彈窗和關閉模態彈窗的交互。

另一種關閉模態彈窗的方法是聲明一個全局變量,實現關閉彈窗的效果,如下代碼所示:

``` //聲明環境變量 @Environment(.presentationMode) var presentationMode

//調用 self.presentationMode.wrappedValue.dismiss() ```

上述代碼中,我們聲明瞭一個全局變量presentationMode,在點擊closeBtn關閉按鈕的時候調用presentationMode全局變量的wrappedValue.dismiss方法實現關閉彈窗效果。

兩種方式在實際開發過程中均有使用,除模態彈窗外,在頁面之間使用入棧出棧的方式進行切換頁面也經常會用到。

在模態彈窗交互邏輯中更經常使用全局變量關閉彈窗的方式,無需進行雙向綁定已經給視圖添加默認值,也就無需示例在其他頁面缺少綁定參數的麻煩。

本章小結

在本章中,我們主要實現了使用數據模型和List列表來構建身份卡片視圖的內容,在減少代碼量的同時,其實也是為了之後對身份卡片進行新增、編輯、刪除做前期準備。

在SwiftUI中,複雜的具有相似結構的界面設計,通常會使用List列表和ForEach來實現,常見的類似Todo待辦事項App中的卡片列表、信息流App的信息列表、短視頻平台的視頻封面卡片視圖等等。

另外我們還學習了使用NavigationStack導航菜單控件(iOS16版本及以上機型使用,低於此版本機型使用NavigationView,Apple經常會在新控件完善後棄用舊控件,這點有點煩)及其標題、導航按鈕修飾符的使用,並進一步瞭解了使用sheet模態彈窗修飾符打開彈窗。關閉彈窗部分,我們介紹了兩種方式實現關閉彈窗,並簡單介紹了其使用特點。

在下面的章節中,我們將繼續完成“添加身份卡”頁面的相關操作,請保持期待吧~

版權聲明

本文為稀土掘金技術社區首發簽約文章,14天內禁止轉載,14天后未獲授權禁止轉載,侵權必究!

「其他文章」