Как включить выбор в списке SwiftUI? - PullRequest
5 голосов
/ 21 июня 2019

Я пытаюсь создать простой список множественного выбора с SwiftUI. Я не могу заставить это работать.

Список принимает второй аргумент, который представляет собой SelectionManager, поэтому я попытался создать конкретную реализацию одного из них. Но он никогда не вызывается, и строки никогда не выделяются.

импорт 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 никогда не вызывается.

Ответы [ 3 ]

4 голосов
/ 21 июня 2019

В зависимости от того, что вы хотите, есть два способа сделать это:

Если вы хотите сделать это в «режиме редактирования»:

Вы должны включить «Режим редактирования» в списке, прежде чем выбор будет иметь значение. Из интерфейса для 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 (вам не нужно бросать свой собственный здесь: 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, но из-за изменений в 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)
                }
            }
        }
    }
}

enter image description here

2 голосов
/ 29 июня 2019

Режим редактирования

Как уже упоминалось в предыдущем ответе, вы можете добавить это в режиме редактирования.Это означает, что пользователь должен будет нажать кнопку редактирования в какой-то момент, чтобы выбрать строки.Это полезно, если вы хотите иметь состояние просмотра и состояние редактирования для вашего списка.

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)"))
        }
    }
}
1 голос
/ 14 июля 2019

Вместо того, чтобы использовать режим редактирования, я бы просто обновил строку на основе модели и переключил логическое значение в модели, когда строка была нажата, как предложено 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...