如何在SwiftUI的列表中启用选择

Rum*_*les 11 list selection swiftui

我试图用SwiftUI创建一个简单的多选列表。我无法使其工作。

列表有第二个参数,它是SelectionManager,所以我尝试创建一个具体的实现。但是,它永远不会被调用,行也不会突出显示。

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
Run Code Online (Sandbox Code Playgroud)

代码运行良好,但行未突出显示,并且从未调用SelectionManager代码。

And*_*era 19

编辑模式

正如之前的答案中提到的,您可以在编辑模式下添加它。这意味着用户将不得不在某个时候按下编辑按钮来选择行。如果您希望列表具有查看状态和编辑状态,这将非常有用。

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)"))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

恒定编辑模式

您也可以简单地始终保持编辑模式。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)"))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这似乎绝对是解决这个问题的最佳方法。但是,有关如何避免视图显示和“切换”到编辑模式之间的滞后的任何提示? (2认同)

pie*_*bie 8

根据您的需要,有两种方法可以执行此操作:

如果要在“编辑模式”下执行此操作:

必须先在列表上启用“编辑模式”,然后再进行选择。从界面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)
Run Code Online (Sandbox Code Playgroud)

您可以通过EditButton在某处的视图中添加一个来实现。在那之后,您只需要为实现的对象绑定一个var SelectionManager(您无需在这里自己滚动: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)"))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这种方法看起来像这样: 在此处输入图片说明

如果您不想使用“编辑模式”:

在这一点上,我们将不得不自己动手。注意:此实现存在一个错误,这意味着只有Text会导致选择发生。可以这样做,Button但是由于Beta 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)
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


Rob*_*Rob 5

Rather than using edit mode, I’d just update the row on the basis of the model, and toggle a boolean in the model when the row is tapped as suggested by /sf/answers/3991662251/. Perhaps something like:

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")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Where

protocol SelectableRow {
    var text: String { get }
    var isSelected: Bool { get set }
}
Run Code Online (Sandbox Code Playgroud)

Then you can do things like:

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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Yielding:

在此处输入图片说明