SwiftUI ForEach 搭配 array 時,如何取得成員的位置(index)

使用 SwiftUI 的 ForEach 可以快速顯示 array 的內容,例如以下顯示歌單的例子。

struct Song: Identifiable {
let name: String
let singer: String
var id: String { name }
}

struct ContentView: View {
let songs = [
Song(name: "小酒窩", singer: "林俊傑"),
Song(name: "你從不知道", singer: "劉增瞳"),
Song(name: "對的時間點", singer: "林俊傑")
]
var body: some View {
VStack {
ForEach(songs) { song in
Text("\(song.name) by \(song.singer)")

}
}
}
}

不過有時我們除了取得 array 的成員,還想取得它在 array 裡的位置(index),此時有以下幾種方法。

  • 方法 1: 利用 array 的 firstIndex 找出資料的位置
  • 方法 2: 利用 enumerated() 取得 array 成員的 index & 內容
  • 方法 3: 利用 array 的 index 當 id (比較不建議,比方動畫可能有問題)

方法 1: 利用 array 的 firstIndex 找出資料的位置

struct ContentView: View {
let songs = [
Song(name: "小酒窩", singer: "林俊傑"),
Song(name: "你從不知道", singer: "劉增瞳"),
Song(name: "對的時間點", singer: "林俊傑")
]

var body: some View {
VStack {
ForEach(songs) { song in
Text("\(song.name) by \(song.singer)")
.onTapGesture {
let index = songs.firstIndex {
$0.id == song.id
}
if let index = index {
print(index)
}
}

}
}
}
}

方法 2: 利用 enumerated() 取得 array 成員的 index & 內容

struct Song: Identifiable {
let name: String
let singer: String
var id: String { name }
}

struct ContentView: View {
let songs = [
Song(name: "小酒窩", singer: "林俊傑"),
Song(name: "你從不知道", singer: "劉增瞳"),
Song(name: "對的時間點", singer: "林俊傑")
]

var body: some View {
VStack {
ForEach(Array(songs.enumerated()), id: \.element.id) { index, song in
Text("\(song.name) by \(song.singer)")
.onTapGesture {
print(index)
}

}
}
}
}

方法 3: 利用 array 的 index 當 id

此方法比較不建議,因為可能會產生特別的問題,比方加上動畫效果時會出問題,相關說明可參考以下連結。

相反的,若是採用方法 1 & 2,動畫都可以正常地以上下移動的方式呈現。

  • 方法 1
struct Song: Identifiable {
let name: String
let singer: String
var id: String { name }
}

struct ContentView: View {
@State private var songs = [
Song(name: "小酒窩", singer: "林俊傑"),
Song(name: "你從不知道", singer: "劉增瞳"),
Song(name: "對的時間點", singer: "林俊傑")
]

var body: some View {
VStack {

ForEach(songs) { song in
Text("\(song.name) by \(song.singer)")
.onTapGesture {
withAnimation {
let index = songs.firstIndex {
$0.id == song.id
}
if let index = index {
songs.swapAt(index, 0)
}
}
}
}
}
}
}
  • 方法 2
struct Song: Identifiable {
let name: String
let singer: String
var id: String { name }
}

struct ContentView: View {
@State private var songs = [
Song(name: "小酒窩", singer: "林俊傑"),
Song(name: "你從不知道", singer: "劉增瞳"),
Song(name: "對的時間點", singer: "林俊傑")
]

var body: some View {
VStack {

ForEach(Array(songs.enumerated()), id: \.element.id) { index, song in
Text("\(song.name) by \(song.singer)")
.onTapGesture {
withAnimation {
songs.swapAt(index, 0)
}
}
}
}
}
}

補充: 如何將 array 的成員變 Binding

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com