#5 @State 和 @Binding 的使用用法

這篇文章打算透過一個簡單的程式範例來記錄一下學習到的觀念. 每當使用者按下按鈕後, 會在畫面中新增一個數字. 這邊簡單的用一個陣列來存放這些數字. 因為會與畫面產生連動, 所以宣告方式必須要在前面加上@State這個關鍵字.

@State var numberArray = [Int]()

接著, 利用Button來讓使用者點選, 當點後後利用append來讓陣列加入一個1~6 隨機的數字.

Button("新增數字"){
self.numberArray.append(Int.random(in: 1...6))
}

接著, 利用ForEach和VStack來把陣列中的數字給顯示出來.

VStack{

ForEach(0 ..< numberArray.count){ item in
Text("\(self.numberArray[item])")
}

Button("新增數字"){
self.numberArray.append(Int.random(in: 1...6))
}
}

看起來應該是完成了, 但是實際執行時會發生錯誤, 不管怎麼用力按都不會加入新產生的數字, 顯示錯誤如下:

ForEach<Range<Int>, Int, Text> count (1) != its initial count (0). `ForEach(_:content:)` should only be used for *constant* data. Instead conform data to `Identifiable` or use `ForEach(_:id:content:)` and provide an explicit `id`!

原來是ForEach裡面為Range時, 只能使用數量固定的資料(constant), 如果今天數量會增減的話, 必須搭配參數id來實現. 修改方式如下:

VStack{

ForEach(0 ..< numberArray.count, id: \.self){ item in
Text("\(self.numberArray[item])")
}

Button("新增數字"){
self.numberArray.append(Int.random(in: 1...6))
}
}

目前看起來按鈕的功能只需要短短幾行就可以實現, 但當今天程式碼變的複雜時, 如果每次要加入按鈕都得重新寫一個Button, 會是一個沒有效率的做法. 我們另外創一個Struct來試著解決這個問題:

struct addButton: View {

@State var numberArray = [Int]()

var body: some View {
Button("新增數字"){
self.numberArray.append(Int.random(in: 1...6))
}
}
}

所以原本在原本的程式碼中, 我們把Button給取代成addButton(), 但實際執行起來發現, 不管怎麼按按鈕, 畫面都沒有任何新的數字出現?

VStack{


ForEach(0 ..< numberArray.count, id: \.self){ item in
Text("\(self.numberArray[item])")
}

addButton()

}

原因是因為在addButton和ContentView裡面的numberArray其實是不同的陣列, 那要怎麼樣做才能讓這兩個陣列指的是同一個陣列呢? 這時候就會用到@Binding這個關鍵字, 來讓這兩個陣列給綁定在一起. 為了避免混淆, 這邊把兩個Struct裡面的陣列取了不同名稱. 此外, 要特別注意, @Binding後面的變數不能給定初始值, 不然會發生錯誤. 修改後的程式碼如下:

struct addButton: View {

@Binding var arraytemp: [Int]

var body: some View {
Button("新增數字"){
self.arraytemp.append(Int.random(in: 1...6))
}
}
}

因為arraytemp要帶入的型別為Binding<[Int]>, 所以在numberArray的前面要加上一個$.

struct ContentView: View {

@State var numberArray = [Int]()

var body: some View {

VStack{


ForEach(0 ..< numberArray.count, id: \.self){ item in
Text("\(self.numberArray[item])")
}

addButton(arraytemp: $numberArray)

}

}
}

--

--

KuoJed
彼得潘的 Swift iOS / Flutter App 開發教室

「沒有一件你努力過的事是白費的。」 當你這麼相信,並且實踐,就會成真。