删除具有绑定布尔值的列表项 - SwiftUI

Posted

技术标签:

【中文标题】删除具有绑定布尔值的列表项 - SwiftUI【英文标题】:Deleting List Item With A Binding Boolean Value - SwiftUI 【发布时间】:2020-10-06 14:46:47 【问题描述】:

在我的应用程序中,我有一个屏幕,用户可以使用 Swift Toggle 打开和关闭(地图的)过滤器,也可以删除它们(使用 Edit Button)。我使用Binding 布尔变量来连接切换开关,以便我可以对每个更改做出反应,代码如下:

//The filter data model
class DSFilter
    
    var filterId : Int
    var filterTitle : String
    var isActive : Bool
    
    
    init(filterId : Int, filterTitle : String, isActive : Bool)
        self.filterId = filterId
        self.filterTitle = filterTitle
        self.isActive = isActive
    


//The data model representable
struct filterItem : View 
    @Binding var filter : DSFilter
    @Binding var isOn : Bool
    @State var image = Image("defPerson")
    
    var body : some View 
        HStack 
            image.resizable().frame(width: 45, height: 45).clipShape(Circle())
            VStack(alignment: .leading, spacing: 8) 
                Text(filter.filterTitle).font(Font.custom("Quicksand-Bold",size: 15))
                Text(filter.filterTitle).font(Font.custom("Quicksand-Medium",size: 13)).foregroundColor(.gray)
            .padding(.leading)
            Spacer()
            HStack
                Toggle("",isOn: $isOn).padding(.trailing, 5).onReceive([self.isOn].publisher.first())  (value) in
                    self.filter.isActive = self.isOn
                .frame(width: 30)
            
        .frame(height: 60)
            .onAppear()
                self.isOn = self.filter.isActive             
        
    


//The view where the user has the control
struct FilterView: View 
    @State var otherFilters : [addedFilter] = []
    @Binding var isActive : Bool
    
    var body: some View 
        VStack
            Capsule().fill(Color.black).frame(width: 50, height: 5).padding()
            ZStack
                HStack
                    Spacer()

                    Text("Filters").font(Font.custom("Quicksand-Bold", size: 20))

                    Spacer()
                .padding()
                HStack
                    Spacer()
                    
                    EditButton()
                    
                .padding()
            

                List
                    
                    ForEach(0..<self.otherFilters.count, id:\.self) i in
                        
                        filterItem(filter: self.$otherFilters[i].filter, isOn: self.$otherFilters[i].isOn)
                    .onDelete(perform:removeRows)
                   
   
                
            
            Spacer()
        
        
    
    
    func removeRows(at offsets: IndexSet) 
        print("deleting")
                    print("deleted filter!")
            self.otherFilters.remove(at: Array(offsets)[0]) //I use it like so because you can only delete 1 at a time anyway
       
        
    


//The class I use to bind the isOn value so I can react to change
struct addedFilter : Identifiable
    var id : Int
    var filter : DSFilter
    var isOn : Bool
    
    init(filter : DSFilter)
        self.id = Int.random(in: 0...100000)
        self.filter = filter
        self.isOn = filter.isActive
    


当我现在使用删除时,我收到以下错误 (Xcode 12.0):

Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444
2020-10-06 17:42:12.567235+0300 Hynt[5207:528572] Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444

我需要更改哪些内容才能删除这些项目?我应该对ToggleBinding 问题采取不同的方法吗?

【问题讨论】:

由于您尝试在索引 0 处删除,因此数组为空。如,长度 = 0。 我没有尝试在索引 0 中删除,我在 offsets 数组的第一个索引处删除(可以是 0 但不一定是),并且数组不为空 【参考方案1】:

嗨,而不是保留三个不同的数组来管理 addedFilter、DSFilter、isOn。你为什么不试试下面的一个单一的模型

class FFilter: Identifiable 
var id: Int
 var addedFilter: addedFilter
var dsFilter: DSFilter
var isOn: Bool

init(addedFilter: addedFilter, dsFilter: DSFilter, isOn: Bool) 
    self.id = addedFilter.id
    self.addedFilter = addedFilter
    self.dsFilter = dsFilter
    self.isOn = isOn

ViewModel 更改代码如下:

class ViewModel: ObservableObject 

@Published var superFilters: [FFilter] = [
    FFilter(
        addedFilter: addedFilter(
            filter: DSFilter(filterId: 1, filterTitle: "Title1", isActive: true)
        ),
        dsFilter: DSFilter(filterId: 1, filterTitle: "Title1", isActive: true),
        isOn: true),
    FFilter(
        addedFilter: addedFilter(
            filter: DSFilter(filterId: 2, filterTitle: "Title2", isActive: false)
        ),
        dsFilter: DSFilter(filterId: 2, filterTitle: "Title2", isActive: false),
        isOn: false),
    FFilter(
        addedFilter: addedFilter(filter: DSFilter(filterId: 3, filterTitle: "Title3", isActive: true)),
        dsFilter: DSFilter(filterId: 3, filterTitle: "Title3", isActive: true),
        isOn: true),
    FFilter(
        addedFilter: addedFilter(
            filter: DSFilter(filterId: 4, filterTitle: "Title4", isActive: true)
        ),
        dsFilter: DSFilter(filterId: 4, filterTitle: "Title4", isActive: true),
        isOn: true),
    FFilter(
        addedFilter: addedFilter(
            filter: DSFilter(filterId: 5, filterTitle: "Title5", isActive: false)
        ),
        dsFilter: DSFilter(filterId: 5, filterTitle: "Title5", isActive: false),
        isOn: false)
]

然后更改您的列表代码

  List 
 ForEach(viewModel.superFilters)  filter in
      filterItem(filter: .constant(filter.dsFilter), isOn: .constant(filter.isOn))
      .onDelete(perform: removeRows(at:)) 

和 removeRows 方法:

func removeRows(at offsets: IndexSet) 
    print("deleting at \(offsets)")
    self.viewModel.superFilters.remove(atOffsets: offsets)

我希望它会起作用。如果您仍然遇到任何问题,请告诉我

【讨论】:

DSFilter 模型仍有重构空间,您可以使用一次。 这行得通!但是只有一件事:我希望我的用户能够打开和关闭过滤器,并且按照您所说的不可能,我该如何更改它? @TheMachineX 如果我看看你的过滤器模型。添加了Filter和DSFilter。两者都做同样的工作。我说的是不是在两个不同的模型中保留相同的东西。只需创建一个简单的模型,它可以将过滤器设置为 ON 或 OFF。因此,从您的数组中创建一个自定义 getter 属性,只需迭代打开或关闭的那个。您可以随意拨打电话。如果您遇到任何问题,请告诉我。【参考方案2】:

我建议您将removeRows()函数中的行从

self.otherFilters.remove(at: Array(offsets)[0]

self.otherFilters.remove(atOffsets: offsets) 

【讨论】:

【参考方案3】:

所以,经过多次尝试和失败后,我得出了答案。 问题似乎出在绑定部分 - 如果我从中绑定一个元素,编译器将无法处理数组大小的变化,所以我做了以下操作: 首先,我将filterItem struct 更改为过滤器属性为@State 而不是@Binding

struct filterItem : View 
    @State var filter : DSFilter
    @Binding var isOn : Bool

其次,我已将addedFilter struct 更改为以下内容:

struct addedFilter : Identifiable
    var id : Int
    var isOn : Bool
    
    init(filter : DSFilter)
        self.id = filter.filterId
        self.isOn = filter.isActive
    

ForEach 循环如下:

ForEach(0..<self.normOthFilters.count, id:\.self) i in
                        filterItem(filter: self.normOthFilters[i], isOn: self.$otherFilters[i].isOn)
                    .onDelete(perform:removeRows)

到我添加的主视图:

    @State var normDefFilters : [DSFilter] = []
    @State var normOthFilters : [DSFilter] = []

最后是 onDelete 函数如下:

func removeRows(at offsets: IndexSet) 
        print("deleting")
            print("deleted filter!")
            self.normOthFilters.remove(atOffsets: offsets)
        
    

最后,我没有更改绑定值的值,因此我没有收到任何错误!

【讨论】:

以上是关于删除具有绑定布尔值的列表项 - SwiftUI的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 绑定布尔 if 语句(无法将类型 'Binding<Bool>' 的值转换为预期的条件类型 'Bool')

绑定到布尔值的可见性的 TargetNullValue

绑定布尔值的 bind_param 问题

一种返回布尔值的方法,该布尔值标识两个数组的值是不是相同

更新 UIViewRepresentable 中的绑定

根据布尔值更改 Xamarin 表单标签的文本