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

語言: 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天后未獲授權禁止轉載,侵權必究!

「其他文章」