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

語言: CN / TW / HK

theme: smartblue

前提回顧

在上一章節中,我們完成了“電子木魚”項目的基礎部分,包含基礎的UI樣式、交互邏輯和動畫效果。這一章節,我們來實現“電子木魚”App的聲音播放、自定義設置頁面及其交互邏輯。

音頻準備:木魚敲擊聲

當每次點擊木魚的時候,電子木魚App都需要發出“咚”的敲擊聲。我們在網上可以找到並下載木魚敲擊聲,下載好的文件拖入到項目中,如下圖所示:

在此請記住下載的音頻的時長(通常為1秒),以及文件名稱、文件後綴名(通常為mp3、m4a),在之後的代碼中需準確調用。

緊接着,我們來實現音頻播放相關的代碼。音頻播放需要使用到一個新的框架:AVFoundation。

AVFoundation是蘋果在iOS和OS X系統中,用於處理基於時間的媒體數據的Objective-C框架,供使用者來開發媒體類型的應用程序。

AVFoundation框架可以用來實現播放聲音的效果,首先需要在項目中引入AVFoundation框架,由於是Apple自帶的框架,可以直接在項目中import導入,如下代碼所示:

import AVFoundation

為了項目方便,我們可以創建一個新的Swift文件來放置播放音頻的相關代碼。創建一個新的Swift文件,命名為AudioPlayer。在AudioPlayer文件中,引入AVFoundation框架,預設一個播放器,然後創建一個方法來使用播放器,如下代碼所示:

``` import AVFoundation import Foundation import SwiftUI

var soundPlayer: AVAudioPlayer?

func playAudio(forResource: String, ofType: String) { let path = Bundle.main.path(forResource: forResource, ofType: ofType)! let url = URL(fileURLWithPath: path)

do {
    soundPlayer = try AVAudioPlayer(contentsOf: url)
    soundPlayer?.play()
} catch {
    print("音頻文件出現問題")
}

} ```

上述代碼中,我們預先創建了一個播放器soundPlayer,然後創建了一個方法playAudio播放聲音,傳入兩個參數,forResource用於確定所需播放的音頻文件的文件名稱,ofType為文件的後綴名。

確定後參數後,將兩個參數值給到路徑path,再把路徑給到地址url,便於後面播放器使用。在代碼中使用聲音播放器AVAudioPlayer播放聲音,如果嘗試執行失敗則打印輸出錯誤信息。

完成後,回到Content文件,在點擊木魚時調用playAudio方法,如下代碼所示:

playAudio(forResource: "dong", ofType: "mp3")

在預覽窗口敲擊了一下,效果不錯(不禁笑出了聲)。

頁面跳轉:打開設置頁面

接下來,我們再升級一下,嘗試編輯“功德值”參數等相關內容。我們創建一個新的SwiftUI文件,命名為DetailView,如下圖所示:

回到ContentView文件,我們先來實現頁面跳轉交互邏輯。從ContentView跳轉到DetailView頁面的交互,是通過導航視圖右邊的按鈕進行跳轉,按鈕部分可以使用navigationBarItems導航欄元素修飾符創建,如下代碼所示:

.navigationBarItems(trailing: Image(systemName: "slider.horizontal.3"))

如此我們創建了按鈕的樣式部分,但設置按鈕不能進行交互,若是我們在創建按鈕,並且實現按鈕的交互動作,那麼在navigationBarItems導航欄元素修飾符中的代碼就太過於複雜,且不夠清晰。

我們可以創建按鈕元素視圖,將再這個視圖賦予navigationBarItems導航欄元素修飾符,以減少在修飾符中存在太多的不同性質的代碼,如下代碼所示:

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

}) {
    Image(systemName: "slider.horizontal.3")
        .foregroundColor(.white)
}

} ```

頁面跳轉部分,需要提前聲明一個Bool類型的參數來控制跳轉動作,如下代碼所示:

@State var showDetailView:Bool = false

頁面跳轉可以使用Sheet模態彈窗打開方式,在Library選擇Modifiers修飾符欄目,找到sheet的修飾符,拖到VStack縱向佈局容器中,並綁定聲明好的觸發條件及確定好需要打開的頁面,如下代碼所示:

.sheet(isPresented: $showDetailView) { DetailView() }

創建好頁面跳轉方法後,將觸發條件給予到點擊按鈕處,並在點擊設置按鈕時,切換showDetailView的狀態,如下代碼所示:

self.showDetailView.toggle()

在預覽窗口點擊頂部導航菜單的“設置”按鈕,即可打開DetailView頁面。

設置頁面:自定義內容

來到DetailView頁面,先設想下我們需要設置的內容,整個電子木魚App可以設置哪些內容?

聯想在ContentView頁面使用@State聲明的變量,兩個頁面參數需要進行聯動,則在DetailView頁面需要使用@Bingding聲明相同的變量,用於兩個頁面的數據綁定,如下代碼所示:

@Binding var gameType: String @Binding var totalNumber: Int @Binding var number: Int

使用@Binding做數據雙向綁定需要注意2點,一是在DetailView頁面聲明的用於綁定的變量缺少默認值,因此在視圖預覽的時候需要給予默認值方可正常預覽,如下代碼所示:

DetailView(gameType: .constant(""), totalNumber: .constant(0), number: .constant(0))

二是在DetailView頁面聲明的變量,若其他頁面需要跳轉到該頁面,則需要綁定DetailView頁面聲明的值。

我們在ContentView頁面需要跳轉到DetailView頁面,因此回到ContentView頁面,在跳轉的地方綁定對應的參數值,如下代碼所示:

DetailView(gameType: $gameType, totalNumber: $totalNumber, number: $number)

完成後,項目不再提示報錯信息後,我們回到DetailView頁面來完善頁面相關內容。

樣式部分,可以使用Form表單作為主體框架,在Library選擇Views欄目,找到Form表單控件,拖入到主體視圖中,如下圖所示:

參數設置部分,虔誠內容可以使用輸入框作為設置,在Library選擇Views欄目,找到TextField輸入框控件,拖入到Form表單中,輸入框內容綁定使用@Bingding綁定的gameType參數,如下代碼所示:

TextField("請輸入內容", text: $gameType)

初始總量部分,我們可以使用步進器作為設置數值的控件,在Library選擇Views欄目,找到Stepper步進器控件,拖入到Form表單中,Stepper步進器的值綁定初始總量totalNumber。

文字部分為了更好地展示該項設置的內容,可以使用字段拼接內容,同理,每次敲擊木魚增加的數值也可以使用Stepper步進器,如下代碼所示:

Form { TextField("請輸入內容", text: $gameType) Stepper(value: $totalNumber, in: 0...9999) { Text(gameType + ":" + "(totalNumber)") } Stepper(value: $number, in: 1...9999) { Text(gameType + " + " + "(number)") } }

由於在DetailView頁面並沒有傳入相應的值,因此在預覽窗口只能看到缺少值的效果,我們在ContentView頁面中點開設置查看綁定參數值後的效果,如下圖所示:

很好,我們嘗試調試了下項目,運轉不錯。

最後,我們還需要增加關閉彈窗的交互動作,和上面提及過的方法一致,我們可以創建按鈕元素視圖,將再這個視圖賦予navigationBarItems導航欄元素修飾符,作為收起彈窗的按鈕,如下代碼所示:

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

    }) {
        Image(systemName: "xmark.circle.fill")
        .foregroundColor(.white)
    }

} ```

然後對整個表單外層增加一個NavigationStack導航欄,並給Form表單增加navigationBarItems修飾符,創建關閉按鈕的樣式,順便再把標題加上如下代碼所示:

.navigationBarTitle("編輯內容", displayMode: .inline) .navigationBarItems(trailing: closeBtn())

完成之後,創建一個環境變量用於實現關閉彈窗交互,並在點擊關閉按鈕時調用它,如下代碼所示:

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

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

如此就完成了關閉彈窗的交互效果,回到ContentView視圖試試效果,如下圖所示:

項目預覽:整體項目效果展示

完成之後,我們回到ContentView頁面,預覽下整體效果,如下圖所示:

iShot_2022-11-09_23.29.49.gif

項目總結

在本次項目中,我們通過“電子木魚”項目學習瞭如何使用SwiftUI這一聲明式創建頁面元素,也接觸了完全使用Library通過拖拽組件和修飾符的方式來構建頁面。

動畫和交互方面,首次使用了AVFoundation框架,結合SwiftUI實現了敲擊木魚的“咚咚咚”音頻播放,在運行項目聽到“咚”的那一刻,也都忍不住想笑出聲,總算能體會到為何“電子木魚”App能夠火起來的原因。

如果一款軟件為人們帶來快樂,即使它沒有特別的功能,也不失為一款優秀的作品。

版權聲明

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

「其他文章」