SwiftUI ForEach 搭配 array 時,如何取得成員的位置(index)
Published in
6 min readApr 6, 2022
使用 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)
}
}
}
}
}
}