實戰教程·什麼年代了還在敲傳統木魚?(一)

語言: CN / TW / HK

theme: smartblue

專案背景

最近由於疫情閒暇在家,在瀏覽網路資訊的時候發現一款“電子木魚App”悄悄爆紅網路。無需購買實體的木魚,只需要下載App,通過不斷點選App按鈕,即可完成“自我救贖”,甚至還有不同的敲擊聲音可以選擇。在饒有興趣地查閱相關文章後,不僅覺得“有點意思”,難道這是佛教在做數字化轉型?

一笑而過後,當靜下來卻心裡有些感慨。

想起兒時也曾想遁入空門不理世事,做一個和尚追求心中的信仰,過簡單樸素的生活。而如今在社會謀生,應付複雜人性,每天拖著疲憊的身體時常半夜回家,這真的是我想要的嗎?

感慨之後,想想“電子木魚”這個案例也挺有意思,不如也嘗試做下教程,也算是一種另類的解壓。

專案分析

首先我們來先行分析“電子木魚”App的功能點,如下圖所示:

電子木魚主要功能點為點選木魚本體發出敲擊聲音,每當敲擊一下,在介面上將出現“功德+1”的字樣,且頂部計算敲擊的次數。再深入些,我們還可以支援自定義提示文字,比如“開心+1”、“財富+1”等等提示文字。

整體專案難道不大,下面我們來一步一步實現它。

專案準備

首先建立一個新的SwiftUI專案,命名為DigitalWoodfish。如下圖所示:

在ContentView檔案中,選擇模擬器預覽示例程式碼效果,如下圖所示:

然後,我們需要匯入一張“木魚”的圖片作為專案的素材,在Assets檔案中,我們拖入一張網路上下載的木魚圖片。

為了更好的呈現效果,建議下載的圖片背景顏色為透明,因為筆者使用的是iphone,在新版的iOS中可以提取圖片中的元素出來,很容易就獲得了一張沒有背景顏色的圖片,可以使用此方法獲得專案素材。

素材整理好後,拖入到Assets檔案中,如下圖所示:

回到ContentView檔案中,我們開始搭建UI樣式部分。

敲擊主體:電子木魚

第一步,我們先搭建木魚的主體展示部分,可以使用Image圖片元件,先刪除示例程式碼中VStack中的程式碼,然後在頂部工具欄右上角選擇“+”按鈕,在Library中,點選Media欄目,可以看到在Assets匯入的所有素材。

點選woodfish素材,拖動到VStack程式碼中,如下圖所示:

拖入完成後,我們就得到了帶有woodfish素材的Image元件程式碼,如下圖所示:

由於匯入的圖片尺寸等原因,素材超出了螢幕邊界範圍,這時候需要對Image圖片進行修飾,依舊是點選頂部工具欄右上角“+”按鈕,在Library選擇Modifiers修飾符欄目,找到Image的修飾符,選擇Image Resizable圖片可調整大小修飾符,如下圖所示:

將Image Resizable圖片可調整大小修飾符加到Image圖片上,如此,修飾符便可作用到控制元件上,如下圖所示:

同理為了保證圖片尺寸不變形,還需要拖入Aspect Ratio調整長寬比修飾符,設定為fit自適應。最後使用Frame尺寸框架修飾符調整其大小。如此,便得到了一個木魚主體檢視。如下程式碼所示:

Image("woodfish") .resizable() .aspectRatio(contentMode: .fit) .frame(maxWidth: 180, maxHeight: 180)

大家可能注意到了一點,和之前直接使用程式碼程式設計的方式不同,本章使用的方式更類似於UI設計方式。是的,在推出SwiftUI之後,蘋果這一宣告式語言最大的特點就是通過“描述性語言”來“建立一個事物”。

上述的程式設計方式中,我們類似於日常描述一樣,建立了一個圖片和描述這個圖片:

  1. 新增一個來自素材庫的圖片;
  2. 這個圖片太大超出邊界了,希望它的大小是可以調整的;
  3. 調整後尺寸變形了,希望它能保持原來的寬高比;
  4. 還是有點大/小,希望調整它最大是多大;

完成“木魚”主體後,為了突出木魚本體,可以將整個頁面背景設定為黑色,和上述方式一致,可以在Library拖入Views欄目中的Depth Stack堆疊檢視,並將上述的程式碼剪下到裡面,程式碼如下:

``` ZStack { VStack { Image("woodfish") .resizable() .aspectRatio(contentMode: .fit) .frame(maxWidth: 180, maxHeight: 180)

}
.padding()

} ```

背景顏色部分,可以在Library的Views欄目拖入Color顏色,拖入到ZStack堆疊檢視中,並設定顏色值為.black黑色。如下圖所示:

由於檢視安全區域的原因,我們可以看到App頁面上下都留有一片白色的區域。這時為Color顏色設定修飾符,讓其忽略安全區域。

在Library的Modifiers修飾符欄目中選擇Edges Ignoring Safe Area忽略安全區域,將該修飾符加到Color上,如下程式碼所示:

``` ZStack { Color(.black) .edgesIgnoringSafeArea(.all) VStack { Image("woodfish") .resizable() .aspectRatio(contentMode: .fit) .frame(maxWidth: 180, maxHeight: 180)

}
.padding()

} ```

標題文字:功德值 10086

完成木魚主體內容後,我們給這個檢視加個標題。在Library的Modifiers修飾符欄目中選擇Navigation Title導航標題,將其加到VStack縱向檢視中,如下圖所示:

我們看到並沒有出現想要的導航欄標題,這是因為我們並沒有建立導航欄檢視,導致在主體檢視上增加的修飾符無效。

要是用Navigation相關的修飾符,需要做在整體檢視外層包裹一層NavigationView導航檢視,在Library的Views欄目中拖入Navigation Stack導航堆疊檢視,並把ZStack堆疊檢視的內容剪下在裡面,如下圖所示:

特別說明:Navigation Stack導航堆疊檢視是SwiftUI在2022年推出在iOS16版本新增的特性,如果是低版本的裝置可能會出現不適用的情況,可以使用NavigationView代替。

完成後我們發現還是看不到導航標題的內容,這是因為整體背景由於Color顏色和ZStack堆疊檢視將介面顏色填充為黑色,而Navigation Title導航標題文字顏色為預設為黑色,導致顏色不可見。

這時,可以使用修飾符欄目中的Preferred Color Scheme首選配色方案,將首選配色設定為夜間模式,如此SwiftUI將會將標題由原來的黑色自動轉為白色,如下程式碼所示:

``` NavigationStack{ ZStack { Color(.black) .edgesIgnoringSafeArea(.all)

    VStack {
        Image("woodfish")
            .resizable()
            .aspectRatio(contentMode: .fit)
            .frame(maxWidth: 180, maxHeight: 180)
    }
    .padding()
    .navigationTitle("Title")
    .preferredColorScheme(.dark)
}

} ```

標題的文字部分,需要展示功德總數,並在每次敲擊的時候增加總數。除了基礎的“功德數”之外,還可以設定“靜心值”、“開心值”等,因此標題部分需要動態設定文字值。

宣告2個引數作為標題的內容,如下程式碼所示:

@State var gameType:String = "功德值" @State var totalNumber:Int = 10086

上述程式碼中,gameType為虔誠求取的內容,型別為String字串型別,示例:功德值。totalNumber為總數,統計敲擊的次數,由於需要每次 敲擊的時候進行累加,因此設定為Int數值型別,示例:10086。

宣告引數後,設定Navigation Title導航標題文字內容為宣告的變數的組合,如下程式碼所示:

.navigationTitle(gameType+":"+String(totalNumber))

由於totalNumber總數為Int型別,而Navigation Title導航標題文字內容需要為String字串型別的文字,因此在使用字串拼接的時候,需要將Int型別轉換為String型別,SwiftUI可以直接使用型別包裹進行型別轉換。

提示文字:功德值+1

下面我們來完成敲擊動作,每當我們敲擊/點選木魚的時候,木魚會顯示提示文字,示例:功德值+1。

我們可以先建立文字部分,在Library的Views欄目中拖入Text文字控制元件,設定文字內容為“功德值+1”,如下程式碼所示:

Text("功德值+1")

提示文字部分,根據文字結構可以拆分為求拜的內容(與gameType保持一致),和敲擊時增加的數量。求拜的內容可以直接使用gameType引數,我們還需要宣告每次增加的值的引數,如下程式碼所示:

@State var number:Int = 1

宣告好引數,需調整Text元件展示的文字的內容,如下程式碼所示:

Text(gameType+"+"+String(number))

互動動畫:敲擊木魚

下面來完成敲擊木魚的互動動作,當我們點選木魚檢視時,Navigation Title導航標題文字的“功德值”總數會增加,我們可以給木魚主體檢視新增一個點選動作。

在Library的Modifiers修飾符欄目中選擇On Tap Gesture點選手勢修飾符,將它加到Image控制元件上。當每次點選時,讓totalNumber總功德值加上每次點選的number的值,如下程式碼所示:

.onTapGesture { totalNumber += number }

其中,totalNumber += number相當於totalNumber = totalNumber+number。

如此,在每次點選木魚的時候,功德值總數都會增加。

接下來,我們給木魚點選增加互動動作。電子木魚點選時,每次點選木魚都會有一個收縮的動畫帶來點選效果,且提示文字為每次點選時才出現,而後消失。這都給使用者創造一種點選的反饋,而動畫正是SwiftUI優秀之處。

對於上述的互動動作,我們可以使用Bool運算邏輯,實現敲擊木魚的時候木魚縮小,縮小後再恢復原狀。亦敲擊木魚的時候提示文字呈現,而後消失。

首先宣告一個Bool型別的變數,如下程式碼所示:

@State var isTap:Bool = false

然後給Text文字加一個if判斷,當isTap為true,即木魚被點選時才展示Text提示文字,如下程式碼所示:

if isTap { Text(gameType+"+"+String(number)) }

然後給木魚Image圖片增加縮放的修飾符,在Library的Modifiers修飾符欄目中選擇Scale Effect縮放修飾符,新增到Image控制元件的Frame修飾符之後,如下程式碼所示:

.scaleEffect(isTap ? 0.99 : 1.0)

Scale Effect縮放修飾符根據isTap的狀態,展示縮放比例,由於isTap預設狀態為false,因此當isTap為ture時Scale Effect縮放比例為0.99,為false時則預設為1.0。判斷條件邏輯說明如下:

引數 ? 為ture時的狀態 : 為false時的狀態

最後,我們需要在點選木魚的時候修改isTap的狀態,但修改後還需要自動恢復原來的狀態。因此我們可以在切換isTap狀態的同時,在主執行緒執行一個相反的邏輯,以達到恢復的效果,如下程式碼所示:

if !isTap { self.isTap.toggle() DispatchQueue.main.asyncAfter(deadline: .now()+0.1) { self.isTap.toggle() } }

上述程式碼中,我們給木魚Image圖片控制元件點選時間中添加了一個邏輯:當isTap沒有被點選的時候,切換isTap的狀態,表明,木魚被點選,木魚被點選切換isTap狀態後,在主執行緒等待0.1秒後,自動切換isTap狀態,恢復到木魚未敲擊時的狀態。

專案效果預覽

在Xcode右側的預覽視窗嘗試點選木魚看看效果,如下圖所示:

iShot_2022-11-09_18.21.18.gif

專案小結

這是一款很有趣的App,玩法簡單但不失樂趣,彷彿間感受到自身被淨化了。其實想了想,這款App之所以能夠火起來,可能是現代打工人對自己的自嘲吧,通過創造一些沒什麼用的,來讓這個世界更有趣一些。

以往的專案可能用的最多的是直接程式設計的模式,記住基本的控制元件的使用,記住這些控制元件能使用的修飾符,記住控制元件能使用的互動動畫。似乎,作為一個程式設計師不老老實實寫程式碼就不算程式設計師。

SwiftUI提供了一種新型的開發模式,這種宣告式語法就好像我們在“告訴”計算機,我們要什麼東西,以及它應該長成什麼樣。因此,這次的案例嘗試從SwiftUI本身出發,以“拖拽元件”的方式呈現一種程式設計方式,希望大家也能有所收穫。

另外,電子木魚App還沒有完善,下一章節,我們將實現敲擊時的聲音以及更改敲擊木魚祈禱的內容,請保持期待吧。

版權宣告

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

「其他文章」