1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SwiftUI - 用 ForEach 顯示資料的同時取得 index

Posted at

環境

  • Xcode 12.0 beta 6 (12A8189n)

雖然在範例用了 Xcode 12 beta ,不過由於沒有用新的 API ,因此在 Xcode 11 也可以順利執行!

前言

在 Swift UI 裡面,要做出像是 UITableView 一樣,列出一系列的資料的時候會用像是這樣的用法:

ForEach(locations, id: \.id) { location in
    Text(location.name)
}

寫出一個簡單的畫面的話就如圖:

圖片.png

程式碼如下:

import SwiftUI

struct ContentView: View {
    
    let locations: [Location] = [
        Location(name: "彰化"),
        Location(name: "台中"),
        Location(name: "南投"),
    ]
    
    var body: some View {
        VStack {
            HStack {
                Text("中部")
                    .font(.title)
                    .fontWeight(.heavy)
                Spacer(minLength: 0)
            }
            .padding(.horizontal)
            ScrollView {
                VStack(spacing: 8) {
                    ForEach(locations, id: \.id) { location in
                        HStack {
                            Image(systemName: "mappin")
                                .font(.system(size: 19))
                                .foregroundColor(.red)
                                .padding()
                            VStack(alignment: .leading, spacing: 8) {
                                Text(location.name)
                                Text(location.id)
                                    .font(.caption)
                                    .foregroundColor(Color.gray)
                            }
                            Spacer(minLength: 0)
                        }
                    }
                }
                .padding()
            }
        }
    }
}

Location 的定義

struct Location: Identifiable {
    let id: String
    let name: String
    
    init(uuid: () -> String = { UUID().uuidString }, name: String) {
        self.id = uuid()
        self.name = name
    }
}

回到正題

但是 ForEach 沒有像 UITableViewDelegate 的方法 tableView(_:didSelectRowAt:) 可以透過傳入的 indexPath 取得點擊到的那一行的索引,而在實務上又經常需要這個索引值,那該怎麼辦呢?

這時候就比較麻煩了,我們就必須透過 enumerated() 這個方法來產生 (index, value) 的陣列。

但是由於 enumerated() 本身的回傳型別是 EnumeratedSequence<Array<Element>> ,而這個型別無法直接餵給 ForEach , 編輯器就會開始抱怨:

Generic struct 'ForEach' requires that 'EnumeratedSequence<[Location]>' conform to 'RandomAccessCollection'

所以在傳入之前就還要再將她生成一個 Array ,傳入 id 的時候也因為結構不同而需要調整,就會變得這樣有點落落長的形式:

ForEach(Array(locations.enumerated()), id: \.element.id) { index, location in
    // 省略
}

最終效果和程式碼

這裡就拿 index 來動態顯示不同的 SF Symbols :

圖片.png

程式碼

import SwiftUI

struct ContentView: View {
    
    let locations: [Location] = [
        Location(name: "彰化"),
        Location(name: "台中"),
        Location(name: "南投"),
    ]
    
    var body: some View {
        VStack {
            HStack {
                Text("中部")
                    .font(.title)
                    .fontWeight(.heavy)
                Spacer(minLength: 0)
            }
            .padding(.horizontal)
            ScrollView {
                VStack(spacing: 8) {
                    ForEach(Array(locations.enumerated()), id: \.element.id) { index, location in
                        HStack {
                            Image(systemName: "mappin")
                                .font(.system(size: 19))
                                .foregroundColor(.red)
                                .padding()
                            VStack(alignment: .leading, spacing: 8) {
                                HStack {
                                    // 為了顯示在這邊用 index
                                    Image(systemName: "\(index).circle")
                                    Text(location.name)
                                }
                                Text(location.id)
                                    .font(.caption)
                                    .foregroundColor(Color.gray)
                            }
                            Spacer(minLength: 0)
                        }
                    }
                }
                .padding()
            }
        }
    }
}

struct Location: Identifiable {
    let id: String
    let name: String
    
    init(uuid: () -> String = { UUID().uuidString }, name: String) {
        self.id = uuid()
        self.name = name
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

以上。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?