SwiftUIを使用して単純な複数選択リストを作成しようとしています。私はそれを動作させることができません。
リストは、SelectionManagerである2番目の引数を取るため、具体的な実装を作成してみました。ただし、呼び出されることはなく、行が強調表示されることもありません。
import SwiftUI
var demoData = ["Phil Swanson", "Karen Gibbons", "Grant Kilman", "Wanda Green"]
struct SelectKeeper : SelectionManager{
var selections = Set<UUID>()
mutating func select(_ value: UUID) {
selections.insert(value)
}
mutating func deselect(_ value: UUID) {
selections.remove(value)
}
func isSelected(_ value: UUID) -> Bool {
return selections.contains(value)
}
typealias SelectionValue = UUID
}
struct SelectionDemo : View {
@State var selectKeeper = SelectKeeper()
var body: some View {
NavigationView {
List(demoData.identified(by: \.self)){ name in
Text(name)
}
.navigationBarTitle(Text("Selection Demo"))
}
}
}
#if DEBUG
struct SelectionDemo_Previews : PreviewProvider {
static var previews: some View {
SelectionDemo()
}
}
#endif
コードは正常に実行されますが、行が強調表示されず、SelectionManagerコードが呼び出されることはありません。
目的に応じて、これを行うには2つの方法があります。
選択が重要になる前に、リストで「編集モード」を有効にする必要があります。 List
のインターフェースから:
_ /// Creates an instance.
///
/// - Parameter selection: A selection manager that identifies the selected row(s).
///
/// - See Also: `View.selectionValue` which gives an identifier to the rows.
///
/// - Note: On iOS and tvOS, you must explicitly put the `List` into Edit
/// Mode for the selection to apply.
@available(watchOS, unavailable)
public init(selection: Binding<Selection>?, content: () -> Content)
_
これを行うには、ビューにEditButton
を追加します。その後、SelectionManager
を実装するものにvarをバインドするだけです(ここで自分でロールする必要はありません:D)
_var demoData = ["Phil Swanson", "Karen Gibbons", "Grant Kilman", "Wanda Green"]
struct SelectionDemo : View {
@State var selectKeeper = Set<String>()
var body: some View {
NavigationView {
List(demoData.identified(by: \.self), selection: $selectKeeper){ name in
Text(name)
}
.navigationBarItems(trailing: EditButton())
.navigationBarTitle(Text("Selection Demo \(selectKeeper.count)"))
}
}
}
_
この時点で、自分自身をロールバックする必要があります。注:この実装にはバグがあります。つまり、Text
のみが選択を発生させます。これをButton
で行うことは可能ですが、ベータ2での変更によりborderlessButtonStyle()
が削除されたため、間抜けに見え、回避策はまだわかりません。
_struct Person: Identifiable, Hashable {
let id = UUID()
let name: String
}
var demoData = [Person(name: "Phil Swanson"), Person(name: "Karen Gibbons"), Person(name: "Grant Kilman"), Person(name: "Wanda Green")]
struct SelectKeeper : SelectionManager{
var selections = Set<UUID>()
mutating func select(_ value: UUID) {
selections.insert(value)
}
mutating func deselect(_ value: UUID) {
selections.remove(value)
}
func isSelected(_ value: UUID) -> Bool {
return selections.contains(value)
}
typealias SelectionValue = UUID
}
struct SelectionDemo : View {
@State var selectKeeper = Set<UUID>()
var body: some View {
NavigationView {
List(demoData) { person in
SelectableRow(person: person, selectedItems: self.$selectKeeper)
}
.navigationBarTitle(Text("Selection Demo \(selectKeeper.count)"))
}
}
}
struct SelectableRow: View {
var person: Person
@Binding var selectedItems: Set<UUID>
var isSelected: Bool {
selectedItems.contains(person.id)
}
var body: some View {
GeometryReader { geo in
HStack {
Text(self.person.name).frame(width: geo.size.width, height: geo.size.height, alignment: .leading)
}.background(self.isSelected ? Color.gray : Color.clear)
.tapAction {
if self.isSelected {
self.selectedItems.remove(self.person.id)
} else {
self.selectedItems.insert(self.person.id)
}
}
}
}
}
_
編集モードを使用するのではなく、モデルに基づいて行を更新し、行がタップされたときにモデルのブール値をトグルするように提案します https://stackoverflow.com/a/57023746/1271826 。おそらく次のようなものです:
struct MultipleSelectionRow<RowContent: SelectableRow>: View {
var content: Binding<RowContent>
var body: some View {
Button(action: {
self.content.value.isSelected.toggle()
}) {
HStack {
Text(content.value.text)
Spacer()
Image(systemName: content.value.isSelected ? "checkmark.circle.fill" : "circle")
}
}
}
}
どこ
protocol SelectableRow {
var text: String { get }
var isSelected: Bool { get set }
}
その後、次のようなことができます:
struct Person: Hashable, Identifiable, SelectableRow {
let id = UUID().uuidString
let text: String
var isSelected: Bool = false
}
struct ContentView : View {
@State var people: [Person] = [
Person(text: "Mo"),
Person(text: "Larry"),
Person(text: "Curly")
]
var body: some View {
List {
ForEach($people.identified(by: \.id)) { person in
MultipleSelectionRow(content: person)
}
}
}
}
収量:
編集モード
前の回答で述べたように、これを編集モードで追加できます。つまり、ユーザーが行を選択するには、ある時点で編集ボタンを押す必要があります。これは、リストのビューステートと編集ステートが必要な場合に役立ちます。
var demoData = ["Phil Swanson", "Karen Gibbons", "Grant Kilman", "Wanda Green"]
struct SelectionDemo : View {
@State var selectKeeper = Set<String>()
var body: some View {
NavigationView {
List(demoData, id: \.self, selection: $selectKeeper){ name in
Text(name)
}
.navigationBarItems(trailing: EditButton())
.navigationBarTitle(Text("Selection Demo \(selectKeeper.count)"))
}
}
}
定数編集モード
編集モードを常にオンにしておくこともできます。 SwiftUIには、環境変数を手動で制御できる環境修飾子があります。この場合、editMode
変数を制御します。
var demoData = ["Phil Swanson", "Karen Gibbons", "Grant Kilman", "Wanda Green"]
struct SelectionDemo : View {
@State var selectKeeper = Set<String>()
var body: some View {
NavigationView {
List(demoData, id: \.self, selection: $selectKeeper){ name in
Text(name)
}
// the next line is the modifier
.environment(\.editMode, .constant(EditMode.active))
.navigationBarTitle(Text("Selection Demo \(selectKeeper.count)"))
}
}
}