在SwitUI列表EditMode中禁用对单独的行进行编辑

时间:2020-07-20 19:16:24

标签: swiftui swiftui-list

我试图找到一种方法来限制列表中可以选择的项目数量(使用EditMode),可以说我有一个包含6个项目的列表,并且希望用户能够选择其中3个项目,当选择3我想禁用未选择的人。但是,我希望所选的项目继续可取消选择,以便可以选择其他项目。 selectedItems集稍后会转换为数据模型并保存到数据库。

struct SelectItemListView: View {

var items = ["one", "two", "three", "four", "five", "six"]
var numberOfselectedItems = 3 // <- controlled by user input in real project
@State var selectedItems = Set<String>()

var body: some View {
    List(items, id: \.self, selection: $selectedItems) { item in // <- id is \.id from datamodel in real project
        Text(item)
    }
    .disabled(selectedItems.count >= numberOfselectedItems)

    .environment(\.editMode, .constant(EditMode.active))
}
}

代码可以编译,但是会禁用整个列表,而不是单个行。在SwiftUI中甚至有可能吗?

1 个答案:

答案 0 :(得分:0)

不幸的是,据我所知,目前还没有任何方法可以完成您想要的事情。

但是,可以通过创建自定义ListItemRow来实现此目的,该自定义将接受任何内容(基本上是通用内容),但是它将选择和取消选择该行。

struct CustomListRow<Content> : View where Content : View {
    @Binding var isSelectable: Bool
    @State private var isSelected: Bool = false
    
    var onSelectionChanged :(_ isSelected:Bool) -> () = {_ in}
    
    var content: () -> Content
    
    var body: some View {
        HStack {
            Circle()
                .overlay(
                    Circle()
                        .stroke(self.isSelected ? Color.blue : Color(red: 0.741, green: 0.741, blue: 0.749), lineWidth: 1)
                        .overlay(Image(systemName: "checkmark").resizable().scaledToFit().frame(width: 12, height: 12).foregroundColor(Color.white))
            )
                .frame(width: 20, height: 20, alignment: .center)
                .foregroundColor(self.isSelected ? Color.blue : Color.clear)
            
            self.content()
        }
        .onTapGesture {
            if(self.isSelectable) {
                self.isSelected.toggle()
                self.onSelectionChanged(self.isSelected)
            }
        }
    }
    
    func onSelectionChanged(callback: @escaping (_ isSelected: Bool) -> ()) -> some View {
        CustomListRow(isSelectable: self.$isSelectable, onSelectionChanged: callback, content: self.content)
    }
}

这是使用方法

struct ContentView: View {
    
    var items = ["one", "two", "three", "four", "five", "six"]
    var numberOfselectedItems = 3 // <- controlled by user input in real project
    @State var selectedItems = Set<String>()
    
    var body: some View {
        List(items, id: \.self) { item in // <- id is \.id from datamodel in real project
            CustomListRow(isSelectable: Binding(get: {
                return (self.itemExist(item: item) || self.selectedItems.count < self.numberOfselectedItems)
            }, set: { _ in})) {
                Text("item: \(item) - Exist: \(self.itemExist(item: item) ? "true" : "false") - count: \(self.selectedItems.count)")
            }
            .onSelectionChanged { isSelected in
                if(isSelected == true) {
                    self.selectedItems.insert(item)
                } else {
                    self.selectedItems.remove(item)
                }
            }
        }
    }
    
    func itemExist(item: String) -> Bool{
        return self.selectedItems.contains(item)
    }
}

您可以更进一步,创建一个自定义List来使用此CustomListItemRow,并在初始化器中传递几个额外的变量,例如Data和{{1 }},并注意在自定义视图中控制选择,以使其在不同项目中可重用MaxSelectionSize。但这超出了这个问题的范围。