Jetpack Compose(Kotlin)で、SwiftのKeyPath的なやつ使ってリスト表示したいなぁ〜
geDem
やりたいこと
SwiftにはKeyPathという機能があります。
「構造体のプロパティにアクセスできるパス」みたいなイメージです。
Viewの引数にKeyPathを与えると、型を明確に決めなくても値を取得できるので、例えば以下みたいなリストを表示するときとかに便利です。
ListView.swiftstruct ListView<ItemType: Identifiable>: View {
// リストデータをジェネリクスで受け取る
let listData: [ItemType]
// KeyPathを受け取る
let title: KeyPath<ItemType, String>
let description: KeyPath<ItemType, String>
var body: some View {
VStack {
ForEach(listData) { item in
VStack(alignment: .leading, spacing: .zero) {
HStack {
// KeyPathを使って値にアクセス
Text(item[keyPath: title])
Text(item[keyPath: description])
}
Divider()
}
}
}
.frame(width: 200, alignment: .leading)
}
}
struct SomeType1: Identifiable {
var id: UUID = UUID()
let title: String
let description: String
}
struct SomeType2: Identifiable {
var id: UUID = UUID()
let name: String
let bio: String
}
#Preview {
VStack(spacing: 30) {
ListView(
listData: [
SomeType1(title: "朝ごはん", description: "食べる"),
SomeType1(title: "自転車", description: "こぐ"),
SomeType1(title: "パソコン", description: "叩く")
],
// KeyPath指定
title: \SomeType1.title,
description: \SomeType1.description
)
ListView(
listData: [
SomeType2(name: "たろう", bio: "釣りが好き"),
SomeType2(name: "じろう", bio: "ゲームが好き"),
SomeType2(name: "さぶろう", bio: "なんでも好き")
],
title: \SomeType2.name,
description: \SomeType2.bio
)
}
}
こんな感じのがJetpack Composeで実装できたら便利なのになぁと思って試行錯誤しました。
Jetpack Composeでの実装方法
要は、インスタンスから値にアクセスできれば良いので、クロージャで実装できます。
ListView.kt@Composable
fun <ListItem> ListView(
listData: List<ListItem>,
titleSelector: (ListItem) -> String,
descriptionSelector: (ListItem) -> String
) {
Column(Modifier.width(200.dp)) {
listData.map { item ->
Column {
Row {
// クロージャにインスタンスを渡す
Text(text = titleSelector(item))
Text(text = descriptionSelector(item))
}
Divider()
}
}
}
}
data class SomeType1(
val title: String,
val description: String
)
data class SomeType2(
val name: String,
val bio: String
)
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
PlaygroundTheme {
Column(
Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(
space = 30.dp,
alignment = Alignment.CenterVertically
),
horizontalAlignment = Alignment.CenterHorizontally
) {
ListView(
listData = listOf(
SomeType1(title = "朝ごはん", description = "食べる"),
SomeType1(title = "自転車", description = "こぐ"),
SomeType1(title = "パソコン", description = "叩く")
),
// インスタンスの値にアクセス
titleSelector = { it.title },
descriptionSelector = { it.description }
)
ListView(
listData = listOf(
SomeType2(name = "たろう", bio = "釣りが好き"),
SomeType2(name = "じろう", bio = "ゲームが好き"),
SomeType2(name = "さぶろう", bio = "なんでも好き")
),
titleSelector = { it.name },
descriptionSelector = { it.bio }
)
}
}
}
🙂↕️最後まで読んでいただきありがとうございます🙂↕️