web-dev-qa-db-ja.com

SwiftUIリストで選択を有効にする方法

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コードが呼び出されることはありません。

16
Rumbles

目的に応じて、これを行うには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)"))
        }
    }
}
_

このアプローチは次のようになります: enter image description here

「編集モード」を使用したくない場合:

この時点で、自分自身をロールバックする必要があります。注:この実装にはバグがあります。つまり、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)
                }
            }
        }
    }
}
_

enter image description here

11
piebie

編集モードを使用するのではなく、モデルに基づいて行を更新し、行がタップされたときにモデルのブール値をトグルするように提案します 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)
            }
        }
    }
}

収量:

enter image description here

11
Rob

編集モード

前の回答で述べたように、これを編集モードで追加できます。つまり、ユーザーが行を選択するには、ある時点で編集ボタンを押す必要があります。これは、リストのビューステートと編集ステートが必要な場合に役立ちます。

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)"))
        }
    }
}
8
Andre Carrera