Sponsor Link
環境&対象
- macOS Big Sur 11.1
- Xcode 12.3
[SwiftUI] Listでselectionを有効にする方法
概要
SwiftUI の List は、イニシャライザで selection を指定することで、選択可能なリストになります。
iOS では、editMode を設定する必要がありますが、macOS では、editMode を設定することなく選択可能になります。
しかし、selection を指定しても選択できないリストになることがあり困り果てていたのですが、解決策が見つかったので、説明します。
# 以下のサンプル等は、macOS 上で確認していますが、iOS 上でも同じ制限があると思われます(未確認)
よく 例題として提示されているコード

//
// StringListView.swift
//
// Created by : Tomoaki Yagishita on 2021/02/01
// © 2021 SmallDeskSoftware
//
import SwiftUI
struct StringListView: View {
// (1)
@State private var selection:String? = nil
// (2)
var demoData = ["text1", "text2", "text3"]
var body: some View {
VStack {
// (3)
List(demoData, id:\.self, selection: $selection) { name in
Text(name)
}
// (4)
if let selection = selection {
Text("selected text is \(selection)")
}
}
.padding()
}
}
- リストで選択された値を保持する変数です
- デモ用のデータ
- initializer に selection も指定しているので、選択できるようになります
- 選択されているのであれば、選択されたテキストを表示
普通に動きます。
すこし応用編の選択できるリストビュー
アプリの開発を進めていくと、単純な型のリストを使うことは減ってきて、struct や class を使ったリストに変わっていきます。
そこで、以下のように struct をリスト表示しようとして問題が発生しました。
//
// StructListView.swift
//
// Created by : Tomoaki Yagishita on 2021/02/01
// © 2021 SmallDeskSoftware
//
import SwiftUI
// (1)
struct MyStruct: Identifiable, Hashable {
var id: String
}
struct StructListView: View {
// (2)
@State private var selectedStruct: MyStruct? = nil
// (3)
var demoStructs = [MyStruct(id: "0"), MyStruct(id: "1"), MyStruct(id: "2")]
var body: some View {
VStack {
// (4)
List(demoStructs, id:\.id, selection:$selectedStruct) { element in
Text(element.id)
}
}
.padding()
}
}
- 例題用にシンプルな struct を作成しています
- 選択された要素を保持する変数
- デモ用のデータ
- struct に id プロパティを持たせているので id には、id を指定しています
上記のコードでリストは期待通りに表示されます。しかし、要素の選択ができません。
難しい仕組みを使っているつもりはなかったので、ここでハマりました。
選択できない理由は、id と selection の型が異なるためでした
ドキュメントに明確な記載は無いと思うのですが、List の引数である id と selection は、同じ型を対象としていないと選択動作ができなくなるようです。

//
// StructListView.swift
//
// Created by : Tomoaki Yagishita on 2021/02/01
// © 2021 SmallDeskSoftware
//
import SwiftUI
struct MyStruct: Identifiable, Hashable {
var id: String
}
struct StructListView: View {
// (1)
@State private var selectedString:String? = nil
// (2)
@State private var selectedStruct: MyStruct? = nil
var demoStructs = [MyStruct(id: "0"), MyStruct(id: "1"), MyStruct(id: "2")]
var body: some View {
VStack {
// (3)
List(demoStructs, id:\.id, selection:$selectedString) { element in
Text(element.id)
}
Text("selected String : \(selectedString ?? "not selected")")
// (4)
List(demoStructs, id:\.self, selection:$selectedStruct) { element in
Text(element.id)
}
Text("selected struct : \(selectedStruct?.id ?? "not selected")")
}
.padding()
}
}
- id として String を使う List での選択を保持する変数として String 型で定義
- id として MyStruct を使う List では保持する変数として、MyStruct 型の定義
- id として String 型、selection として、String? 型を指定
- id として MyStruct 型、selection として、MyStruct? 型を指定
上記の組み合わせでは期待する選択動作ができます。
うまく動かないケースでは、id として、String 型が指定され、selection として MyStruct? 型が指定されています。
この動作から、id として指定されている方法で要素を認識し、選択された場合その値を selection に設定するという動作だと推測します。
うまく動かないケースでは、id と selection で型が一致するように指定されていなかったために、選択操作をしても結果として選択用の変数に保存できないというのが背景にあった問題だと思います。
わかってみれば 簡単ですが、わかるまでに、半日以上使いました・・・
まとめ:SwiftUI の List での選択動作が動かないときに確認すること
- id と selection の型が一致するか再確認
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
Sponsor Link