Open1
SwiftUI:Listの単一選択にチェックボックスをつける

SwiftUIでList
を選択可にした際、selection
の引数に渡す変数の型で、単一選択なのか複数選択なのかが変わります。
単一選択の場合は以下のような感じになります。
struct ContentView1: View {
@State private var selectedItem: Int? = nil
let items = ["大谷", "中谷", "小谷"]
var body: some View {
VStack {
Text("選択されている担当者: \(selectedItem == nil ? "なし" : items[selectedItem!])")
List(selection: $selectedItem) {
ForEach(items.indices, id: \.self) { index in
Text(items[index]).tag(index)
}
}
.environment(\.editMode, .constant(.active))
}
}
}
表示は以下のようになりますね。
行を選択するとその行が反転するだけ、という感じです。
複数選択の場合の表示は以下のようになります。
行頭に円が追加表示されて、選択するとその行が反転した上で円にチェックマークが入ります。
で、要は単一選択でも複数選択のようにチェックマークを付けたいじゃないかという話です。
struct ContentView2: View {
@State private var selectedItems: Set<Int> = []
let items = ["大谷", "中谷", "小谷"]
var body: some View {
VStack {
let selectedItem = selectedItems.first
Text("選択されている担当者: \(selectedItem == nil ? "なし" : items[selectedItem!])")
List(selection: $selectedItems) {
ForEach(items.indices, id: \.self) { index in
Text(items[index]).tag(index)
}
}
.environment(\.editMode, .constant(.active))
}
.onChange(of: selectedItems) { oldSelection, newSelection in
guard let toBeUnchecked = oldSelection.first else {
return
}
if newSelection.count <= 1 {
return
}
selectedItems.remove(toBeUnchecked)
}
}
}
.onChange(of:peform:)
を使って2つ目が選択されたら先に選択されていた分を選択解除し、常に選択アイテムが1つ以下になるようにします。
.onChange(of:perform:)
のクロージャはiOS17で変わったのでiOS16以前の場合は .onChange(of: selectedItems) { oldSelection, newSelection in
の部分を.onChange(of: selectedItems) { [oldSelection = self.selectedItems] newSelection in
のように書き換えれば良いでしょう。